Examples - a-schild/jave2 GitHub Wiki

Examples

Recipes for the conversions people ask for most. See Usage for how to add the library, and Supported formats for what the bundled ffmpeg can read and write.

Every example calls attrs.setOutputFormat(...). Older examples elsewhere on the internet call attrs.setFormat(...), which has not existed for a long time and will not compile.

Audio

WAV to MP3

File source = new File("source.wav");
File target = new File("target.mp3");

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setBitRate(128000);
audio.setChannels(2);
audio.setSamplingRate(44100);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp3");
attrs.setAudioAttributes(audio);

new Encoder().encode(new MultimediaObject(source), target, attrs);

Extracting the audio from a video, as WAV

File source = new File("source.avi");
File target = new File("target.wav");

AudioAttributes audio = new AudioAttributes();
audio.setCodec("pcm_s16le");

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("wav");
attrs.setAudioAttributes(audio);

new Encoder().encode(new MultimediaObject(source), target, attrs);

Taking the soundtrack off a video, as MP3

Leaving VideoAttributes unset is what drops the picture.

File source = new File("source.mp4");
File target = new File("target.mp3");

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setBitRate(192000);
audio.setChannels(2);
audio.setSamplingRate(44100);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp3");
attrs.setAudioAttributes(audio);

new Encoder().encode(new MultimediaObject(source), target, attrs);

If the source audio is already MP3, audio.setCodec(AudioAttributes.DIRECT_STREAM_COPY) lifts the stream out unchanged, which is both faster and lossless.

FLAC to MP4, audio only

An MP4 carrying nothing but sound. Same shape as above, a different container, and setFaststart so it can be streamed.

AudioAttributes audio = new AudioAttributes();
audio.setCodec("aac");
audio.setBitRate(256000);

VideoAttributes video = new VideoAttributes();
video.setFaststart(true);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

Players that insist on a picture want a video stream, so for those, loop a still image instead. See Encoding Attributes for setLoop.

FLAC to MP3, keeping the album art

Cover art is carried as a video stream, so an audio only encoding drops it. Ask for the video stream and ask for it unchanged.

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setBitRate(320000);

VideoAttributes video = new VideoAttributes();
video.setCodec("copy");          // this is what keeps the cover

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp3");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

new Encoder().encode(new MultimediaObject(source), target, attrs);

You can check it survived, since the cover shows up as a video stream:

boolean hasCover = new MultimediaObject(target).getInfo().getVideo() != null;

To AMR, for telephony

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libopencore_amrnb");
audio.setSamplingRate(8000);     // amr-nb only does 8 kHz
audio.setChannels(1);            // and mono
audio.setBitRate(12200);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("amr");
attrs.setAudioAttributes(audio);

Changing the volume

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setVolume(512);            // 256 is unchanged, so this is twice as loud

Video

AVI to FLV

File source = new File("source.avi");
File target = new File("target.flv");

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setBitRate(64000);
audio.setChannels(1);
audio.setSamplingRate(22050);

VideoAttributes video = new VideoAttributes();
video.setCodec("flv");
video.setBitRate(160000);
video.setFrameRate(15);
video.setSize(new VideoSize(400, 300));

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("flv");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

new Encoder().encode(new MultimediaObject(source), target, attrs);

To MP4, H.264

AudioAttributes audio = new AudioAttributes();
audio.setCodec("aac");
audio.setBitRate(128000);

VideoAttributes video = new VideoAttributes();
video.setCodec("libx264");
video.setCrf(23);                // 0 lossless, 51 worst, 23 is the usual default
video.setPreset(PresetEnum.MEDIUM.getPresetName());   // or just "medium"

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

To MP4 at a fixed size, two pass

When the size of the result matters more than the time it takes, let the encoder look at the material first. setTwoPass(true) runs ffmpeg twice: once to measure, once to encode using what it measured, so the bitrate lands where it does the most good.

AudioAttributes audio = new AudioAttributes();
audio.setCodec("aac");
audio.setBitRate(128000);

VideoAttributes video = new VideoAttributes();
video.setCodec("libx264");
video.setBitRate(1200000);       // the budget the two passes exist to spend well

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);
attrs.setTwoPass(true);

A video bitrate is required, since that is the budget being planned, and this is the wrong tool alongside setCrf, which asks for a quality rather than a size. See Encoding Attributes for the details.

To WebM, VP9

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libopus");
audio.setBitRate(96000);

VideoAttributes video = new VideoAttributes();
video.setCodec("libvpx-vp9");
video.setBitRate(250000);
video.setFrameRate(25);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("webm");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

Taking a portion out of the middle

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setOffset(30.0f);          // start thirty seconds in
attrs.setDuration(10.0f);        // and take ten seconds

Videos filmed sideways

Phones record the orientation as metadata rather than rotating the picture, so a video that looks upright in a phone gallery can arrive on its side.

MultimediaInfo info = new MultimediaObject(source).getInfo();
if (info.getRotate() == 90 || info.getRotate() == 270) {
    // width and height of info.getVideo().getSize() are the other way round
    // once it is played back
}

Several files at once

Joining videos

List<MultimediaObject> sources = new ArrayList<>();
sources.add(new MultimediaObject(new File("part1.mp4")));
sources.add(new MultimediaObject(new File("part2.mp4")));

VideoAttributes video = new VideoAttributes();
video.setCodec("mpeg4");

FilterGraph graph = new FilterGraph();
FilterChain chain = new FilterChain();
chain.addFilter(new MediaConcatFilter(sources.size(), true, true));  // video, audio
graph.addChain(chain);
video.setComplexFiltergraph(graph);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setVideoAttributes(video);

new Encoder().encode(sources, target, attrs);

When several sources are joined there is no single source to describe, so EncoderProgressListener.sourceInfo is not called, and progress is reported as PROGRESS_UNKNOWN rather than a percentage.

Joining audio files

The same filter, told to take the audio streams and not the video ones. Note that the filter graph still hangs off VideoAttributes even though the output has no video, since that is where setComplexFiltergraph lives.

List<MultimediaObject> sources = new ArrayList<>();
sources.add(new MultimediaObject(new File("1.mp3")));
sources.add(new MultimediaObject(new File("2.mp3")));

AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
audio.setChannels(2);
audio.setSamplingRate(44100);

FilterGraph graph = new FilterGraph();
FilterChain chain = new FilterChain();
chain.addFilter(new MediaConcatFilter(sources.size(), false, true));   // no video, audio
graph.addChain(chain);

VideoAttributes video = new VideoAttributes();
video.setComplexFiltergraph(graph);

EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp3");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);

new Encoder().encode(sources, target, attrs);

The two booleans are which streams to carry: MediaConcatFilter(n, video, audio). Passing (n, true, false) joins the pictures and drops the sound, and the single argument constructor takes both, which fails if any source lacks either.

All the sources must have compatible streams. Files with different sample rates or channel counts need converting first, one at a time, and joining afterwards.

Stills

One frame

ScreenExtractor extractor = new ScreenExtractor();
extractor.renderOneImage(new MultimediaObject(source), 320, 240, 2000L, target, 2);
//                                                     w    h   millis  file quality

A frame every so many seconds

extractor.render(new MultimediaObject(source), 320, 240, 5,
                 outputDirectory, "frame", "jpg", 2);
//                                          every 5 seconds, into a directory

Filters

VideoAttributes video = new VideoAttributes();

// text across the picture
video.addFilter(new DrawtextFilter("watermark", "10", "10", "Sans", 24.0,
                                   new Color("ffffff")));

// or scale it
video.addFilter(new ScaleFilter(new VideoSize(1280, 720)));

// or crop it
video.addFilter(new CropFilter(640, 480, 0, 0));   // width, height, x, y

Burning in subtitles takes the file itself, the filter escapes the path for you:

video.addFilter(new AssSubtitlesFilter(new File("subtitles.ass")));

Following along

new Encoder().encode(new MultimediaObject(source), target, attrs,
    new EncoderProgressListener() {
        public void sourceInfo(MultimediaInfo info) { }
        public void progress(int permil) {
            if (permil == EncoderProgressListener.PROGRESS_UNKNOWN) {
                System.out.println("working");
            } else {
                System.out.println(permil / 10.0 + "%");
            }
        }
        public void message(String message) { }
        @Override public void done() {
            System.out.println("finished");
        }
    });

PROGRESS_UNKNOWN, which is -1, means the source declared no duration, so there is nothing for the progress to be a proportion of. Live streams, webm files from browser recorders and concatenations all land there, and an indeterminate progress bar is the right response.

Stopping a running encoding

encode() blocks, so to be able to abort it, run it on a thread of your own and keep the Encoder where the other thread can reach it. abortEncoding() kills the ffmpeg process.

Encoder encoder = new Encoder();

Thread thread = new Thread(() -> {
    try {
        encoder.encode(new MultimediaObject(source), target, attrs, listener);
        // finished
    } catch (EncoderException ex) {
        // failed, or was aborted
    }
});
thread.start();

// later, from anywhere
encoder.abortEncoding();

quitEncoding() is the gentler alternative: it sends ffmpeg a q, so ffmpeg finishes writing what it has and closes the file properly, leaving a shorter but playable result. abortEncoding() kills the process outright and the partial file is usually unusable. Neither is any use once the run has ended, and quitEncoding() throws if nothing is running.

One Encoder runs one encoding at a time, since it keeps a single process, so give each concurrent conversion its own.

With two pass encoding there are two processes one after the other, and aborting ends whichever is running and abandons the encoding as a whole. Aborting during the measuring pass therefore throws away work without producing anything, which is worth knowing if you abort often.

Something not covered here

If the option you need is not in the typed API, see Custom ffmpeg arguments.

The test sources in jave-core-test exercise most of the library against real files, and are worth reading when you want to see a class used in anger.