1
1
# Jaffree
2
2
3
- Jaffree stands for [ Ja ] va [ ff ] mpeg and [ ff ] probe [ free ] command line wrapper. Jaffree supports programmatic video production and consumption (with transparency)
3
+ Jaffree stands for JAva FFmpeg and FFprobe FREE command line wrapper. Jaffree supports programmatic video production and consumption (with transparency)
4
4
5
5
It integrates with ffmpeg via ` java.lang.Process ` .
6
6
@@ -40,7 +40,7 @@ Inspired by [ffmpeg-cli-wrapper](https://github.com/bramp/ffmpeg-cli-wrapper)
40
40
41
41
## Checking media streams with ffprobe
42
42
43
- See whole example [ here] ( /src/test/java/examples/ffprobe/ShowStreams .java ) .
43
+ See whole example [ here] ( /src/test/java/examples/ShowStreamsExample .java ) .
44
44
45
45
``` java
46
46
FFprobeResult result = FFprobe . atPath()
@@ -59,7 +59,7 @@ for (Stream stream : result.getStreams()) {
59
59
60
60
Sometimes ffprobe can't show exact duration, use ffmpeg trancoding to NULL output to get it.
61
61
62
- See whole example [ here] ( /src/test/java/examples/ffmpeg/ExactDuration .java ) .
62
+ See whole example [ here] ( /src/test/java/examples/ExactDurationExample .java ) .
63
63
64
64
``` java
65
65
final AtomicLong durationMillis = new AtomicLong ();
@@ -82,7 +82,7 @@ System.out.println("Exact duration: " + durationMillis.get() + " milliseconds");
82
82
83
83
## Re-encode and track progress
84
84
85
- See whole example [ here] ( /src/test/java/examples/ffmpeg/ReEncode .java ) .
85
+ See whole example [ here] ( /src/test/java/examples/ReEncodeExample .java ) .
86
86
87
87
``` java
88
88
final AtomicLong duration = new AtomicLong ();
@@ -116,7 +116,7 @@ FFmpeg.atPath()
116
116
117
117
Pay attention that arguments related to Input must be set at Input, not at FFmpeg.
118
118
119
- See whole example [ here] ( /src/test/java/examples/ffmpeg/CutAndScale .java ) .
119
+ See whole example [ here] ( /src/test/java/examples/CutAndScaleExample .java ) .
120
120
121
121
``` java
122
122
FFmpeg . atPath()
@@ -136,7 +136,7 @@ FFmpeg.atPath()
136
136
137
137
## Custom parsing of ffmpeg output
138
138
139
- See whole example [ here] ( /src/test/java/examples/ffmpeg/ParsingOutput .java ) .
139
+ See whole example [ here] ( /src/test/java/examples/ParsingOutputExample .java ) .
140
140
141
141
``` java
142
142
// StringBuffer - because it's thread safe
@@ -162,7 +162,7 @@ System.out.println("Loudnorm report:\n" + loudnormReport);
162
162
Ability to interact with SeekableByteChannel is one of the features, which distinct Jaffree from
163
163
similar libraries. Under the hood Jaffree uses tiny FTP server to interact with SeekableByteChannel.
164
164
165
- See whole example [ here] ( /src/test/java/examples/ffmpeg/UsingChannels .java ) .
165
+ See whole example [ here] ( /src/test/java/examples/UsingChannelsExample .java ) .
166
166
``` java
167
167
try (SeekableByteChannel inputChannel =
168
168
Files . newByteChannel(pathToSrc, StandardOpenOption . READ );
@@ -185,7 +185,7 @@ requires seekable output for many formats.
185
185
186
186
Under the hood pipes are not OS pipes, but TCP Sockets. This allows much higher bandwidth.
187
187
188
- See whole example [ here] ( /src/test/java/examples/ffmpeg/UsingStreams .java ) .
188
+ See whole example [ here] ( /src/test/java/examples/UsingStreamsExample .java ) .
189
189
190
190
``` java
191
191
try (InputStream inputStream =
@@ -204,13 +204,140 @@ try (InputStream inputStream =
204
204
}
205
205
```
206
206
207
+ ## Screen Capture
208
+
209
+ See whole example [ here] ( /src/test/java/examples/ScreenCaptureExample.java ) .
210
+
211
+ ``` java
212
+ FFmpeg . atPath()
213
+ .addInput(CaptureInput
214
+ .captureDesktop()
215
+ .setCaptureFrameRate(30 )
216
+ .setCaptureCursor(true )
217
+ )
218
+ .addOutput(UrlOutput
219
+ .toPath(pathToVideo)
220
+ // Record with ultrafast to lower CPU usage
221
+ .addArguments(" -preset" , " ultrafast" )
222
+ .setDuration(30 , TimeUnit . SECONDS )
223
+ )
224
+ .setOverwriteOutput(true )
225
+ .execute();
226
+
227
+ // Re-encode when record is completed to optimize file size
228
+ Path pathToOptimized = pathToVideo. resolveSibling(" optimized-" + pathToVideo. getFileName());
229
+ FFmpeg . atPath()
230
+ .addInput(UrlInput . fromPath(pathToVideo))
231
+ .addOutput(UrlOutput . toPath(pathToOptimized))
232
+ .execute();
233
+
234
+ Files . move(pathToOptimized, pathToVideo, StandardCopyOption . REPLACE_EXISTING );
235
+ ```
236
+
237
+ ## Produce Video in Pure Java Code
238
+
239
+ See whole example [ here] ( /src/test/java/examples/ProduceVideoExample.java ) .
240
+ Check also more [ advanced example] ( /src/test/java/examples/BouncingBallExample.java ) which produce
241
+ both audio and video
242
+
243
+ ``` java
244
+ FrameProducer producer = new FrameProducer () {
245
+ private long frameCounter = 0 ;
246
+
247
+ @Override
248
+ public List<Stream > produceStreams () {
249
+ return Collections . singletonList(new Stream ()
250
+ .setType(Stream . Type . VIDEO )
251
+ .setTimebase(1000L )
252
+ .setWidth(320 )
253
+ .setHeight(240 )
254
+ );
255
+ }
256
+
257
+ @Override
258
+ public Frame produce () {
259
+ if (frameCounter > 30 ) {
260
+ return null ; // return null when End of Stream is reached
261
+ }
262
+
263
+ BufferedImage image = new BufferedImage (320 , 240 , BufferedImage . TYPE_3BYTE_BGR );
264
+ Graphics2D graphics = image. createGraphics();
265
+ graphics. setPaint(new Color (frameCounter * 1.0f / 30 , 0 , 0 ));
266
+ graphics. fillRect(0 , 0 , 320 , 240 );
267
+ long pts = frameCounter * 1000 / 10 ; // Frame PTS in Stream Timebase
268
+ Frame videoFrame = new Frame (0 , pts, image);
269
+ frameCounter++ ;
270
+
271
+ return videoFrame;
272
+ }
273
+ };
274
+
275
+ FFmpeg . atPath()
276
+ .addInput(FrameInput . withProducer(producer))
277
+ .addOutput(UrlOutput . toUrl(pathToVideo))
278
+ .execute();
279
+ ```
280
+
281
+ Here is an output of the above example:
282
+
283
+ ![ example output] ( /src/test/resources/examples/programmatic.gif )
284
+
285
+ ### Consume Video in Pure Java Code
286
+
287
+ See whole example [ here] ( /src/test/java/examples/ExtractFramesExample.java ) .
288
+
289
+ ``` java
290
+ FFmpeg . atPath()
291
+ .addInput(UrlInput
292
+ .fromPath(pathToSrc)
293
+ )
294
+ .addOutput(FrameOutput
295
+ .withConsumer(
296
+ new FrameConsumer () {
297
+ private long num = 1 ;
298
+
299
+ @Override
300
+ public void consumeStreams (List<Stream > streams ) {
301
+ // All stream type except video are disabled. just ignore
302
+ }
303
+
304
+ @Override
305
+ public void consume (Frame frame ) {
306
+ // End of Stream
307
+ if (frame == null ) {
308
+ return ;
309
+ }
310
+
311
+ try {
312
+ String filename = " frame_" + num++ + " .png" ;
313
+ Path output = pathToDstDir. resolve(filename);
314
+ ImageIO . write(frame. getImage(), " png" , output. toFile());
315
+ } catch (Exception e) {
316
+ e. printStackTrace();
317
+ }
318
+ }
319
+ }
320
+ )
321
+ // No more then 100 frames
322
+ .setFrameCount(StreamType . VIDEO , 100L )
323
+ // 1 frame every 10 seconds
324
+ .setFrameRate(0.1 )
325
+ // Disable all streams except video
326
+ .disableStream(StreamType . AUDIO )
327
+ .disableStream(StreamType . SUBTITLE )
328
+ .disableStream(StreamType . DATA )
329
+ )
330
+ .execute();
331
+ ```
332
+
333
+
207
334
## FFmpeg stop
208
335
209
- See whole examples [ here] ( /src/test/java/examples/ffmpeg/Stop .java ) .
336
+ See whole examples [ here] ( /src/test/java/examples/StopExample .java ) .
210
337
211
338
### Grace stop
212
339
213
- Start ffmpeg with FFmpeg#executeAsync and stop it with FFmpegResultFuture#graceStop (ffmpeg only).
340
+ Start ffmpeg with ` FFmpeg#executeAsync ` and stop it with ` FFmpegResultFuture#graceStop ` (ffmpeg only).
214
341
This will pass ` q ` symbol to ffmpeg's stdin.
215
342
216
343
** Note** output media finalization may take some time - up to several seconds.
@@ -222,38 +349,37 @@ Thread.sleep(5_000);
222
349
future. graceStop();
223
350
```
224
351
225
-
226
352
### Force stop
227
353
228
354
There are 3 ways to stop ffmpeg forcefully.
229
355
230
356
** Note** : ffmpeg may not (depending on output format) correctly finalize output.
231
357
It's very likely that produced media will be corrupted with force stop.
232
358
233
- * Throw an exception in ProgressListener (ffmpeg only)
359
+ * Throw an exception in ` ProgressListener ` (ffmpeg only)
234
360
``` java
235
361
final AtomicBoolean stopped = new AtomicBoolean ();
236
362
ffmpeg. setProgressListener(
237
363
new ProgressListener () {
238
364
@Override
239
365
public void onProgress (FFmpegProgress progress ) {
240
366
if (stopped. get()) {
241
- throw new RuntimeException (" Stooped with exception!" );
367
+ throw new RuntimeException (" Stopped with exception!" );
242
368
}
243
369
}
244
370
}
245
371
);
246
372
```
247
373
248
- * Start ffmpeg with FFmpeg#executeAsync and stop it with FFmpegResultFuture#forceStop (ffmpeg only)
374
+ * Start ffmpeg with ` FFmpeg#executeAsync ` and stop it with ` FFmpegResultFuture#forceStop ` (ffmpeg only)
249
375
``` java
250
376
FFmpegResultFuture future = ffmpeg. executeAsync();
251
377
252
378
Thread . sleep(5_000 );
253
379
future. forceStop();
254
380
```
255
381
256
- * Start ffmpeg with FFmpeg#execute (or ffprobe with FFprobe#execute) and interrupt thread
382
+ * Start ffmpeg with ` FFmpeg#execute ` (or ffprobe with ` FFprobe#execute ` ) and interrupt thread
257
383
``` java
258
384
Thread thread = new Thread () {
259
385
@Override
@@ -267,9 +393,11 @@ Thread.sleep(5_000);
267
393
thread. interrupt();
268
394
```
269
395
270
- ## Complex filtergraph (mosaic video)
396
+ ## Complex Filtergraph (mosaic video)
397
+
398
+ More details about this example can be found on ffmpeg wiki:
399
+ [ Create a mosaic out of several input videos] ( https://trac.ffmpeg.org/wiki/Create%20a%20mosaic%20out%20of%20several%20input%20videos )
271
400
272
- More details about this example can be found on ffmpeg wiki: [ Create a mosaic out of several input videos] ( https://trac.ffmpeg.org/wiki/Create%20a%20mosaic%20out%20of%20several%20input%20videos )
273
401
``` java
274
402
FFmpegResult result = FFmpeg . atPath(BIN )
275
403
.addInput(UrlInput . fromPath(VIDEO1_MP4 ). setDuration(10 , TimeUnit . SECONDS ))
@@ -352,119 +480,7 @@ FFmpegResult result = FFmpeg.atPath(BIN)
352
480
.execute();
353
481
```
354
482
355
- ## Programmatic video
356
-
357
-
358
- ### Producing video
359
-
360
- Jaffree allows creation of video in pure java code.
361
-
362
- See whole example [ here] ( /src/test/java/examples/programmatic/ProduceGif.java ) .
363
-
364
- ``` java
365
- Path output = Paths . get(" test.gif" );
366
-
367
- FrameProducer producer = new FrameProducer () {
368
- private long frameCounter = 0 ;
369
-
370
- @Override
371
- public List<Stream > produceStreams () {
372
- return Collections . singletonList(new Stream ()
373
- .setType(Stream . Type . VIDEO )
374
- .setTimebase(1000L )
375
- .setWidth(320 )
376
- .setHeight(240 )
377
- );
378
- }
379
-
380
- @Override
381
- public Frame produce () {
382
- if (frameCounter > 30 ) {
383
- return null ;
384
- }
385
- System . out. println(" Creating frame " + frameCounter);
386
-
387
- BufferedImage image = new BufferedImage (320 , 240 , BufferedImage . TYPE_3BYTE_BGR );
388
- Graphics2D graphics = image. createGraphics();
389
- graphics. setPaint(new Color (frameCounter * 1.0f / 30 , 0 , 0 ));
390
- graphics. fillRect(0 , 0 , 320 , 240 );
391
-
392
- Frame videoFrame = new Frame ()
393
- .setStreamId(0 )
394
- .setPts(frameCounter * 1000 / 10 )
395
- .setImage(image);
396
- frameCounter++ ;
397
-
398
- return videoFrame;
399
- }
400
- };
401
-
402
- FFmpegResult result = FFmpeg . atPath(BIN )
403
- .addInput(
404
- FrameInput . withProducer(producer)
405
- )
406
- .addOutput(
407
- UrlOutput . toPath(output)
408
- )
409
- .execute();
410
- ```
411
-
412
- Here is an output of the above example:
413
-
414
- ![ example output] ( programmatic.gif )
415
-
416
- Jaffree also allows producing of audio tracks, see BouncingBall [ example] ( examples/src/main/java/BouncingBall.java ) for more details.
417
-
418
-
419
- ### Consuming video
420
-
421
- Jaffree allows consumption of video in the similar manner.
422
-
423
- See whole example [ here] ( /src/test/java/examples/programmatic/ExtractFrames.java ) .
424
-
425
- ``` java
426
- final Path tempDir = Files . createTempDirectory(" jaffree" );
427
- System . out. println(" Will write to " + tempDir);
428
-
429
- final AtomicLong trackCounter = new AtomicLong ();
430
- final AtomicLong frameCounter = new AtomicLong ();
431
-
432
- FrameConsumer consumer = new FrameConsumer () {
433
- @Override
434
- public void consumeStreams (List<Stream > tracks ) {
435
- trackCounter. set(tracks. size());
436
- }
437
-
438
- @Override
439
- public void consume (Frame frame ) {
440
- if (frame == null ) {
441
- return ;
442
- }
443
-
444
- long n = frameCounter. incrementAndGet();
445
- String filename = String . format(" frame%05d.png" , n);
446
- try {
447
- ImageIO . write(frame. getImage(), " png" , tempDir. resolve(filename). toFile());
448
- } catch (IOException e) {
449
- throw new RuntimeException (e);
450
- }
451
- }
452
- };
453
-
454
- FFmpegResult result = FFmpeg . atPath(BIN )
455
- .addInput(
456
- UrlInput . fromPath(VIDEO_MP4 )
457
- .setDuration(1 , TimeUnit . SECONDS )
458
- )
459
- .addOutput(
460
- FrameOutput . withConsumer(consumer)
461
- .extractVideo(true )
462
- .extractAudio(false )
463
- )
464
- .execute();
465
- ```
466
-
467
483
### Programmatic mosaic video creation
468
484
469
485
Jaffree allows simultaneous reading from several sources (with one instance per every source and target).
470
- You can find details in Mosaic [ example] ( /src/test/java/examples/programmatic/Mosaic .java ) .
486
+ You can find details in Mosaic [ example] ( /src/test/java/examples/MosaicExample .java ) .
0 commit comments