All Projects → kokorin → Jaffree

kokorin / Jaffree

Licence: apache-2.0
Java ffmpeg and ffprobe command-line wrapper

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Jaffree

Ffmpy
Pythonic interface for FFmpeg/FFprobe command line
Stars: ✭ 360 (+95.65%)
Mutual labels:  wrapper, ffmpeg
acinerella
FFmpeg wrapper library for audio/video decoding
Stars: ✭ 18 (-90.22%)
Mutual labels:  wrapper, ffmpeg
ffmpeg-progressbar-cli
A colored progress bar for FFmpeg.
Stars: ✭ 140 (-23.91%)
Mutual labels:  wrapper, ffmpeg
Goffmpeg
FFMPEG wrapper written in GO
Stars: ✭ 469 (+154.89%)
Mutual labels:  wrapper, ffmpeg
Video Downloader Deploy
Video Downloaders (you-get, youtube-dl, annie) One-Click Deployment Batch. || 视频下载器 (you-get, youtube-dl, annie) 一键配置脚本。
Stars: ✭ 178 (-3.26%)
Mutual labels:  ffmpeg
Keras Multi Head
A wrapper layer for stacking layers horizontally
Stars: ✭ 172 (-6.52%)
Mutual labels:  wrapper
Config4k
A Kotlin wrapper for Typesafe Config
Stars: ✭ 168 (-8.7%)
Mutual labels:  wrapper
Puppeteer Recorder
Record animations using puppeteer. Based on electron-recorder.
Stars: ✭ 169 (-8.15%)
Mutual labels:  ffmpeg
Qt5.cr
Qt5 bindings for Crystal, based on Bindgen
Stars: ✭ 182 (-1.09%)
Mutual labels:  wrapper
Gear360pano
Simple script to create equirectangular panorama by stitching images from Samsung Gear 360
Stars: ✭ 178 (-3.26%)
Mutual labels:  ffmpeg
Tdl
Node.js bindings to TDLib.
Stars: ✭ 177 (-3.8%)
Mutual labels:  wrapper
Rocksdb Sharp
.net bindings for the rocksdb by facebook
Stars: ✭ 173 (-5.98%)
Mutual labels:  wrapper
Autosubsync
Automatically synchronize subtitles with audio using machine learning
Stars: ✭ 179 (-2.72%)
Mutual labels:  ffmpeg
Libuvsharp
.NET bindings for libuv
Stars: ✭ 170 (-7.61%)
Mutual labels:  wrapper
Wiki
Wikipedia Interface for Node.js
Stars: ✭ 180 (-2.17%)
Mutual labels:  wrapper
React Openlayers
OpenLayer React Components
Stars: ✭ 169 (-8.15%)
Mutual labels:  wrapper
Homebridge Dafang
Homebridge Plugin for Xiaomi Dafang / Wyze Cam IP Camera => Hey Siri, Start Video Recording
Stars: ✭ 176 (-4.35%)
Mutual labels:  ffmpeg
Nopaginate
Android pagination library (updated 01.05.2018)
Stars: ✭ 180 (-2.17%)
Mutual labels:  wrapper
Online Video Editor
API based Online Video Editing using FFMPEG & NodeJs for Backend Editing
Stars: ✭ 176 (-4.35%)
Mutual labels:  ffmpeg
Ksprefs
🚀⚡ Kotlin SharedPreferences wrapper & cryptographic preferences android library.
Stars: ✭ 176 (-4.35%)
Mutual labels:  wrapper

Jaffree

Jaffree stands for JAva FFmpeg and FFprobe FREE command line wrapper. Jaffree supports programmatic video production and consumption (with transparency)

It integrates with ffmpeg via java.lang.Process.

Inspired by ffmpeg-cli-wrapper

Tested with the help of GitHub Actions

Tests

OS: Ubuntu, MacOS, Windows

JDK: 7, 8, 11, 14

Usage

Maven Central

<dependency>
    <groupId>com.github.kokorin.jaffree</groupId>
    <artifactId>jaffree</artifactId>
    <version>0.10.1</version>
</dependency>

<!--
    You should also include slf4j into dependencies.
    This is done intentionally to allow changing of slf4j version.
  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>

Examples

Checking media streams with ffprobe

See whole example here.

FFprobeResult result = FFprobe.atPath()
    .setShowStreams(true)
    .setInput(pathToVideo)
    .execute();

for (Stream stream : result.getStreams()) {
    System.out.println("Stream #" + stream.getIndex()
        + " type: " + stream.getCodecType()
        + " duration: " + stream.getDuration(TimeUnit.SECONDS) + " seconds");
}

Detecting exact media file duration

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

See whole example here.

final AtomicLong durationMillis = new AtomicLong();

FFmpegResult ffmpegResult = FFmpeg.atPath()
    .addInput(
        UrlInput.fromUrl(pathToVideo)
    )
    .addOutput(new NullOutput())
    .setProgressListener(new ProgressListener() {
        @Override
        public void onProgress(FFmpegProgress progress) {
            durationMillis.set(progress.getTimeMillis());
        }
    })
    .execute();

System.out.println("Exact duration: " + durationMillis.get() + " milliseconds");

Re-encode and track progress

See whole example here.

final AtomicLong duration = new AtomicLong();
FFmpeg.atPath()
    .addInput(UrlInput.fromUrl(pathToSrc))
    .addOutput(new NullOutput())
    .setOverwriteOutput(true)
    .setProgressListener(new ProgressListener() {
        @Override
        public void onProgress(FFmpegProgress progress) {
            duration.set(progress.getTimeMillis());
        }
    })
    .execute();

FFmpeg.atPath()
    .addInput(UrlInput.fromUrl(pathToSrc))
    .addOutput(UrlOutput.toUrl(pathToDst))
    .setProgressListener(new ProgressListener() {
        @Override
        public void onProgress(FFmpegProgress progress) {
            double percents = 100. * progress.getTimeMillis() / duration.get();
            System.out.println("Progress: " + percents + "%");
        }
    })
    .setOverwriteOutput(true)
    .execute();

Cut and scale media file

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

See whole example here.

FFmpeg.atPath()
    .addInput(
            UrlInput.fromUrl(pathToSrc)
                    .setPosition(10, TimeUnit.SECONDS)
                    .setDuration(42, TimeUnit.SECONDS)
    )
    .addOutput(
            UrlOutput.toUrl(pathToDst)
                    .setPosition(10, TimeUnit.SECONDS)
    )
    .setFilter(StreamType.VIDEO, "scale=160:-2")
    .setOverwriteOutput(true)
    .execute();

Custom parsing of ffmpeg output

See whole example here.

// StringBuffer - because it's thread safe
final StringBuffer loudnormReport = new StringBuffer();

FFmpeg.atPath()
    .addInput(UrlInput.fromUrl(pathToVideo))
    .addArguments("-af", "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json")
    .addOutput(new NullOutput(false))
    .setOutputListener(new OutputListener() {
        @Override
        public void onOutput(String line) {
            loudnormReport.append(line);
        }
    })
    .execute();

System.out.println("Loudnorm report:\n" + loudnormReport);

Supplying and consuming data with SeekableByteChannel

Ability to interact with SeekableByteChannel is one of the features, which distinct Jaffree from similar libraries. Under the hood Jaffree uses tiny FTP server to interact with SeekableByteChannel.

See whole example here.

try (SeekableByteChannel inputChannel =
         Files.newByteChannel(pathToSrc, StandardOpenOption.READ);
     SeekableByteChannel outputChannel =
         Files.newByteChannel(pathToDst, StandardOpenOption.CREATE,
                 StandardOpenOption.WRITE, StandardOpenOption.READ,
                 StandardOpenOption.TRUNCATE_EXISTING)
) {
    FFmpeg.atPath()
        .addInput(ChannelInput.fromChannel(inputChannel))
        .addOutput(ChannelOutput.toChannel(filename, outputChannel))
        .execute();
}

Supplying and consuming data with InputStream and OutputStream

Notice It's recommended to use ChannelInput & ChannelOutput since ffmpeg leverage seeking in input and requires seekable output for many formats.

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

See whole example here.

try (InputStream inputStream =
         Files.newInputStream(pathToSrc);
     OutputStream outputStream =
         Files.newOutputStream(pathToDst, StandardOpenOption.CREATE,
                 StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)
) {
    FFmpeg.atPath()
        .addInput(PipeInput.pumpFrom(inputStream))
        .addOutput(
                PipeOutput.pumpTo(outputStream)
                        .setFormat("flv")
        )
        .execute();
}

Screen Capture

See whole example here.

FFmpeg.atPath()
    .addInput(CaptureInput
            .captureDesktop()
            .setCaptureFrameRate(30)
            .setCaptureCursor(true)
    )
    .addOutput(UrlOutput
            .toPath(pathToVideo)
            // Record with ultrafast to lower CPU usage
            .addArguments("-preset", "ultrafast")
            .setDuration(30, TimeUnit.SECONDS)
    )
    .setOverwriteOutput(true)
    .execute();

//Re-encode when record is completed to optimize file size 
Path pathToOptimized = pathToVideo.resolveSibling("optimized-" + pathToVideo.getFileName());
FFmpeg.atPath()
    .addInput(UrlInput.fromPath(pathToVideo))
    .addOutput(UrlOutput.toPath(pathToOptimized))
    .execute();

Files.move(pathToOptimized, pathToVideo, StandardCopyOption.REPLACE_EXISTING);

Produce Video in Pure Java Code

See whole example here. Check also more advanced example which produce both audio and video

FrameProducer producer = new FrameProducer() {
    private long frameCounter = 0;

    @Override
    public List<Stream> produceStreams() {
        return Collections.singletonList(new Stream()
                .setType(Stream.Type.VIDEO)
                .setTimebase(1000L)
                .setWidth(320)
                .setHeight(240)
        );
    }

    @Override
    public Frame produce() {
        if (frameCounter > 30) {
            return null; // return null when End of Stream is reached
        }

        BufferedImage image = new BufferedImage(320, 240, BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D graphics = image.createGraphics();
        graphics.setPaint(new Color(frameCounter * 1.0f / 30, 0, 0));
        graphics.fillRect(0, 0, 320, 240);
        long pts = frameCounter * 1000 / 10; // Frame PTS in Stream Timebase
        Frame videoFrame = new Frame(0, pts, image);
        frameCounter++;

        return videoFrame;
    }
};

FFmpeg.atPath()
    .addInput(FrameInput.withProducer(producer))
    .addOutput(UrlOutput.toUrl(pathToVideo))
    .execute();

Here is an output of the above example:

example output

Consume Video in Pure Java Code

See whole example here.

FFmpeg.atPath()
        .addInput(UrlInput
                .fromPath(pathToSrc)
        )
        .addOutput(FrameOutput
                .withConsumer(
                        new FrameConsumer() {
                            private long num = 1;

                            @Override
                            public void consumeStreams(List<Stream> streams) {
                                // All stream type except video are disabled. just ignore
                            }

                            @Override
                            public void consume(Frame frame) {
                                // End of Stream
                                if (frame == null) {
                                    return;
                                }

                                try {
                                    String filename = "frame_" + num++ + ".png";
                                    Path output = pathToDstDir.resolve(filename);
                                    ImageIO.write(frame.getImage(), "png", output.toFile());
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                )
                // No more then 100 frames
                .setFrameCount(StreamType.VIDEO, 100L)
                // 1 frame every 10 seconds
                .setFrameRate(0.1)
                // Disable all streams except video
                .disableStream(StreamType.AUDIO)
                .disableStream(StreamType.SUBTITLE)
                .disableStream(StreamType.DATA)
        )
        .execute();

FFmpeg stop

See whole examples here.

Grace stop

Start ffmpeg with FFmpeg#executeAsync and stop it with FFmpegResultFuture#graceStop (ffmpeg only). This will pass q symbol to ffmpeg's stdin.

Note output media finalization may take some time - up to several seconds.

FFmpegResultFuture future = ffmpeg.executeAsync();

Thread.sleep(5_000);
future.graceStop();

Force stop

There are 3 ways to stop ffmpeg forcefully.

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

  • Throw an exception in ProgressListener (ffmpeg only)
final AtomicBoolean stopped = new AtomicBoolean();
ffmpeg.setProgressListener(
        new ProgressListener() {
            @Override
            public void onProgress(FFmpegProgress progress) {
                if (stopped.get()) {
                    throw new RuntimeException("Stopped with exception!");
                }
            }
        }
);
  • Start ffmpeg with FFmpeg#executeAsync and stop it with FFmpegResultFuture#forceStop (ffmpeg only)
FFmpegResultFuture future = ffmpeg.executeAsync();

Thread.sleep(5_000);
future.forceStop();
  • Start ffmpeg with FFmpeg#execute (or ffprobe with FFprobe#execute) and interrupt thread
Thread thread = new Thread() {
    @Override
    public void run() {
        ffmpeg.execute();
    }
};
thread.start();

Thread.sleep(5_000);
thread.interrupt();

Complex Filtergraph (mosaic video)

More details about this example can be found on ffmpeg wiki: Create a mosaic out of several input videos

FFmpegResult result = FFmpeg.atPath(BIN)
        .addInput(UrlInput.fromPath(VIDEO1_MP4).setDuration(10, TimeUnit.SECONDS))
        .addInput(UrlInput.fromPath(VIDEO2_MP4).setDuration(10, TimeUnit.SECONDS))
        .addInput(UrlInput.fromPath(VIDEO3_MP4).setDuration(10, TimeUnit.SECONDS))
        .addInput(UrlInput.fromPath(VIDEO4_MP4).setDuration(10, TimeUnit.SECONDS))

        .setComplexFilter(FilterGraph.of(
                FilterChain.of(
                        Filter.withName("nullsrc")
                                .addArgument("size", "640x480")
                                .addOutputLink("base")
                ),
                FilterChain.of(
                        Filter.fromInputLink(StreamSpecifier.withInputIndexAndType(0, StreamType.ALL_VIDEO))
                                .setName("setpts")
                                .addArgument("PTS-STARTPTS"),
                        Filter.withName("scale")
                                .addArgument("320x240")
                                .addOutputLink("upperleft")
                ),
                FilterChain.of(
                        Filter.fromInputLink(StreamSpecifier.withInputIndexAndType(1, StreamType.ALL_VIDEO))
                                .setName("setpts")
                                .addArgument("PTS-STARTPTS"),
                        Filter.withName("scale")
                                .addArgument("320x240")
                                .addOutputLink("upperright")
                ),
                FilterChain.of(
                        Filter.fromInputLink(StreamSpecifier.withInputIndexAndType(2, StreamType.ALL_VIDEO))
                                .setName("setpts")
                                .addArgument("PTS-STARTPTS"),
                        Filter.withName("scale")
                                .addArgument("320x240")
                                .addOutputLink("lowerleft")
                ),
                FilterChain.of(
                        Filter.fromInputLink(StreamSpecifier.withInputIndexAndType(3, StreamType.ALL_VIDEO))
                                .setName("setpts")
                                .addArgument("PTS-STARTPTS"),
                        Filter.withName("scale")
                                .addArgument("320x240")
                                .addOutputLink("lowerright")
                ),
                FilterChain.of(
                        Filter.fromInputLink("base")
                                .addInputLink("upperleft")
                                .setName("overlay")
                                .addArgument("shortest", "1")
                                .addOutputLink("tmp1")
                ),
                FilterChain.of(
                        Filter.fromInputLink("tmp1")
                                .addInputLink("upperright")
                                .setName("overlay")
                                //.addArgument("shortest", "1")
                                .addArgument("x", "320")
                                .addOutputLink("tmp2")
                ),
                FilterChain.of(
                        Filter.fromInputLink("tmp2")
                                .addInputLink("lowerleft")
                                .setName("overlay")
                                //.addArgument("shortest", "1")
                                .addArgument("y", "240")
                                .addOutputLink("tmp3")
                ),
                FilterChain.of(
                        Filter.fromInputLink("tmp3")
                                .addInputLink("lowerright")
                                .setName("overlay")
                                //.addArgument("shortest", "1")
                                .addArgument("x", "320")
                                .addArgument("y", "240")
                )
        ))

        .addOutput(UrlOutput.toPath(outputPath))
        .execute();

Programmatic mosaic video creation

Jaffree allows simultaneous reading from several sources (with one instance per every source and target). You can find details in Mosaic example.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].