Skip to content

Commit 6603682

Browse files
kokorinkokorin
authored andcommitted
Readme & examples, fix build
1 parent 9e33fc2 commit 6603682

18 files changed

+380
-472
lines changed

README.md

Lines changed: 146 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Jaffree
22

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)
44

55
It integrates with ffmpeg via `java.lang.Process`.
66

@@ -40,7 +40,7 @@ Inspired by [ffmpeg-cli-wrapper](https://github.com/bramp/ffmpeg-cli-wrapper)
4040

4141
## Checking media streams with ffprobe
4242

43-
See whole example [here](/src/test/java/examples/ffprobe/ShowStreams.java).
43+
See whole example [here](/src/test/java/examples/ShowStreamsExample.java).
4444

4545
```java
4646
FFprobeResult result = FFprobe.atPath()
@@ -59,7 +59,7 @@ for (Stream stream : result.getStreams()) {
5959

6060
Sometimes ffprobe can't show exact duration, use ffmpeg trancoding to NULL output to get it.
6161

62-
See whole example [here](/src/test/java/examples/ffmpeg/ExactDuration.java).
62+
See whole example [here](/src/test/java/examples/ExactDurationExample.java).
6363

6464
```java
6565
final AtomicLong durationMillis = new AtomicLong();
@@ -82,7 +82,7 @@ System.out.println("Exact duration: " + durationMillis.get() + " milliseconds");
8282

8383
## Re-encode and track progress
8484

85-
See whole example [here](/src/test/java/examples/ffmpeg/ReEncode.java).
85+
See whole example [here](/src/test/java/examples/ReEncodeExample.java).
8686

8787
```java
8888
final AtomicLong duration = new AtomicLong();
@@ -116,7 +116,7 @@ FFmpeg.atPath()
116116

117117
Pay attention that arguments related to Input must be set at Input, not at FFmpeg.
118118

119-
See whole example [here](/src/test/java/examples/ffmpeg/CutAndScale.java).
119+
See whole example [here](/src/test/java/examples/CutAndScaleExample.java).
120120

121121
```java
122122
FFmpeg.atPath()
@@ -136,7 +136,7 @@ FFmpeg.atPath()
136136

137137
## Custom parsing of ffmpeg output
138138

139-
See whole example [here](/src/test/java/examples/ffmpeg/ParsingOutput.java).
139+
See whole example [here](/src/test/java/examples/ParsingOutputExample.java).
140140

141141
```java
142142
// StringBuffer - because it's thread safe
@@ -162,7 +162,7 @@ System.out.println("Loudnorm report:\n" + loudnormReport);
162162
Ability to interact with SeekableByteChannel is one of the features, which distinct Jaffree from
163163
similar libraries. Under the hood Jaffree uses tiny FTP server to interact with SeekableByteChannel.
164164

165-
See whole example [here](/src/test/java/examples/ffmpeg/UsingChannels.java).
165+
See whole example [here](/src/test/java/examples/UsingChannelsExample.java).
166166
```java
167167
try (SeekableByteChannel inputChannel =
168168
Files.newByteChannel(pathToSrc, StandardOpenOption.READ);
@@ -185,7 +185,7 @@ requires seekable output for many formats.
185185

186186
Under the hood pipes are not OS pipes, but TCP Sockets. This allows much higher bandwidth.
187187

188-
See whole example [here](/src/test/java/examples/ffmpeg/UsingStreams.java).
188+
See whole example [here](/src/test/java/examples/UsingStreamsExample.java).
189189

190190
```java
191191
try (InputStream inputStream =
@@ -204,13 +204,140 @@ try (InputStream inputStream =
204204
}
205205
```
206206

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+
207334
## FFmpeg stop
208335

209-
See whole examples [here](/src/test/java/examples/ffmpeg/Stop.java).
336+
See whole examples [here](/src/test/java/examples/StopExample.java).
210337

211338
### Grace stop
212339

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).
214341
This will pass `q` symbol to ffmpeg's stdin.
215342

216343
**Note** output media finalization may take some time - up to several seconds.
@@ -222,38 +349,37 @@ Thread.sleep(5_000);
222349
future.graceStop();
223350
```
224351

225-
226352
### Force stop
227353

228354
There are 3 ways to stop ffmpeg forcefully.
229355

230356
**Note**: ffmpeg may not (depending on output format) correctly finalize output.
231357
It's very likely that produced media will be corrupted with force stop.
232358

233-
* Throw an exception in ProgressListener (ffmpeg only)
359+
* Throw an exception in `ProgressListener` (ffmpeg only)
234360
```java
235361
final AtomicBoolean stopped = new AtomicBoolean();
236362
ffmpeg.setProgressListener(
237363
new ProgressListener() {
238364
@Override
239365
public void onProgress(FFmpegProgress progress) {
240366
if (stopped.get()) {
241-
throw new RuntimeException("Stooped with exception!");
367+
throw new RuntimeException("Stopped with exception!");
242368
}
243369
}
244370
}
245371
);
246372
```
247373

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)
249375
```java
250376
FFmpegResultFuture future = ffmpeg.executeAsync();
251377

252378
Thread.sleep(5_000);
253379
future.forceStop();
254380
```
255381

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
257383
```java
258384
Thread thread = new Thread() {
259385
@Override
@@ -267,9 +393,11 @@ Thread.sleep(5_000);
267393
thread.interrupt();
268394
```
269395

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)
271400

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)
273401
```java
274402
FFmpegResult result = FFmpeg.atPath(BIN)
275403
.addInput(UrlInput.fromPath(VIDEO1_MP4).setDuration(10, TimeUnit.SECONDS))
@@ -352,119 +480,7 @@ FFmpegResult result = FFmpeg.atPath(BIN)
352480
.execute();
353481
```
354482

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-
467483
### Programmatic mosaic video creation
468484

469485
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

Comments
 (0)