GithubHelp home page GithubHelp logo

kokorin / jaffree Goto Github PK

View Code? Open in Web Editor NEW
451.0 8.0 75.0 1.53 MB

______ Stop the War in Ukraine! _______ Java ffmpeg and ffprobe command-line wrapper

License: Apache License 2.0

Java 100.00%
java ffmpeg video wrapper ffprobe

jaffree's Introduction

SWUbanner

Jaffree Sparkline

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 Coverage

OS: Ubuntu, MacOS, Windows

JDK: 8, 11, 17

Usage

Maven Central

<dependency>
    <groupId>com.github.kokorin.jaffree</groupId>
    <artifactId>jaffree</artifactId>
    <version>${jaffree.version}</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() + " 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))
    .setOverwriteOutput(true)
    .addOutput(new NullOutput())
    .setProgressListener(new ProgressListener() {
        @Override
        public void onProgress(FFmpegProgress progress) {
            duration.set(progress.getTimeMillis());
        }
    })
    .execute();

FFmpeg.atPath()
    .addInput(UrlInput.fromUrl(pathToSrc))
    .setOverwriteOutput(true)
    .addArguments("-movflags", "faststart")
    .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 + "%");
        }
    })
    .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)
    )
    .setFilter(StreamType.VIDEO, "scale=160:-2")
    .setOverwriteOutput(true)
    .addArguments("-movflags", "faststart")
    .addOutput(
        UrlOutput.toUrl(pathToDst)
                 .setPosition(10, TimeUnit.SECONDS)
    )
    .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();
}

Live Stream Re-Streaming (HLS)

See whole example here.

FFmpeg.atPath()
    .addInput(
        UrlInput.fromUrl(liveStream)
    )
    .addOutput(
        UrlOutput.toPath(dir.resolve("index.m3u8"))
            .setFrameRate(30)
            // check all available options: ffmpeg -help muxer=hls
            .setFormat("hls")
            // enforce keyframe every 2s - see setFrameRate
            .addArguments("-x264-params", "keyint=60")
            .addArguments("-hls_list_size", "5")
            .addArguments("-hls_delete_threshold", "5")
            .addArguments("-hls_time", "2")
            .addArguments("-hls_flags", "delete_segments")
    )
    .setOverwriteOutput(true)
    .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 = Frame.createVideoFrame(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();

Managing errors

Jaffree will raise exceptions when a fatal error that causes a non-zero exit code occurs.

In some cases an error can occur but FFmpeg manages to catch it and exit correctly. This can be a convenient case, although sometimes one would prefer an exception to be raised to Jaffree.

To do so, the -xerror argument can be used to tell FFmpeg to exit the process with an error status when an error occurs.

FFmpeg.atPath()
      .addArgument("-xerror")
      // ...

Please see Issue 276 for more details on an actual usecase.

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();

Java 8 Completion API

See whole examples here.

ffmpeg.executeAsync().toCompletableFuture()
    .thenAccept(res -> {
        // get the result of the operation when it is done
    })
    .exceptionally(ex -> {
        // handle exceptions produced during operation
    });

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.

Build & configuration

IntelliJ IDEA

This project uses maven-git-versioning-extension to set project version automatically. Check its intellij-setup documentation.

JDK

JDK8 is required to compile this project. JDK9 is required to compile this project with Java 9 module support.

JDK8

mvn clean install

JDK9

Maven profile J9-module enables two pass compilation:

  1. Compile all sources with Java 9 target version (including module-info.java).
  2. Recompile all sources (except module-info.java) with Java 8 target version.

After this all classes will have Java 8 bytecode (version 52), while module-info.class will have Java 9 bytecode (version 53).

mvn clean install -PJ9-module

jaffree's People

Contributors

cromefire avatar dan-mikkel avatar dependabot[bot] avatar entropycoder avatar github-actions[bot] avatar guss77 avatar hccake avatar jonfryd avatar kokorin avatar libremente avatar marklassau avatar st-h avatar vicnevicne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jaffree's Issues

`-n` appended to ffmpeg command. How to remove?

Is there a way to not append -n to the command that's generated. I actually want to append -y
.

Java codfe

FFmpeg.atPath(FFMPEG_BIN)
        .addInput(UrlInput.fromPath(VIDEO_MP4_IN))
        .addOutput(UrlOutput.toPath(VIDEO_MP4_OUT)
            .addArgument("-an").addArguments("-vf", "scale=320:240,format=gray")
            .addArguments("-pix_fmt", "yuv420p").addArguments("-c:v", "h264")
//            .addArgument("-y")
        )

Resulting ffmpeg command

/usr/local/Cellar/ffmpeg/4.0/bin/ffmpeg -loglevel 32 -i src/test/resources/TruePositive3.mp4 -n -an -vf scale=320:240,format=gray -pix_fmt yuv420p -c:v h264 src/test/resources/TruePositive3_reduced.mov

BTW, most if not all of the sample code snippets in the README have compile errors, and seem to be using an outdated API.

Revisit stream ID verification

It seems that despite an exception is thrown if stream IDs do not start with zero, this exception isn't rethrown correctly. Which leads to

INFO writer-stderr com.github.kokorin.jaffree.ffmpeg.FFmpegResultReader - [nut @ 00000000011924a0] No main startcode found. INFO writer-stderr com.github.kokorin.jaffree.ffmpeg.FFmpegResultReader - tcp://127.0.0.1:54679: Invalid data found when processing input DEBUG writer-stdout reader com.github.kokorin.jaffree.process.ProcessHandler - StdOut thread has finished WARN writer-stderr com.github.kokorin.jaffree.process.ProcessHandler - Failed to process stderr

Transcode failure with long files

My use case is transcoding an hour long audio file (wav to aac) using PipeOutput.

FFmpeg.atPath(Paths.get(ffmpegBinDirectory)) .addInput(PipeInput.pumpFrom(input)) .addOutput(PipeOutput.pumpTo(output).setFormat("mp4")) .addArguments("-movflags", "frag_keyframe+faststart") .addArguments("-c:a", "aac") .execute();

The file is totally unplayable when using PipeOutput, however if I use UrlOutput then the file is playable.

Releasing resources used by FFmpegResult

If i try to delete the input file provided to the FFmpegResult instance after its execution will result java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process.
How can we gracefully release the resources programmatically once the job is done ?

Support ffmpeg stopping

Currently it's only possible to stop ffmpeg by throwing an exception in ProgressListener.

stopping ffmpeg instance

How to interrupt main running thread to stop instance .
it's returning FFmpegResult object, how to access ProcessHandler class method.
I want to close video programatically rather than closing the window.

Fix javadoc warnings

[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:55: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:34: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:42: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:50: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:50: warning - invalid usage of tag >
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:55: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:65: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:65: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:68: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:68: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:73: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:73: warning - invalid usage of tag >
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:73: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:73: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:78: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:157: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:58: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:46: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:47: warning - invalid usage of tag >
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:58: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:65: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:65: warning - invalid usage of tag >
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:67: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\process\Executor.java:30: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\MainHeader.java:58: warning - invalid usage of tag <
[WARNING] C:\Projects\Jaffree\src\main\java\com\github\kokorin\jaffree\nut\FrameCode.java:55: warning - invalid usage of tag <

Javadoc

Is it possible to add Javadoc? Some methods are difficult to understand what they do.
Thanks.

PS: This is nice FFMPEG wrapper.

Can't convert audio only file (mp3)

2019-03-28 07:48:07.564 WARN [FFmpeg-async-runner][] c.g.k.j.p.ProcessHandler - Waiting for process has been interrupted
java.lang.InterruptedException: null
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.UNIXProcess.waitFor(UNIXProcess.java:395)
	at com.github.kokorin.jaffree.process.ProcessHandler.interactWithProcess(ProcessHandler.java:112)
	at com.github.kokorin.jaffree.process.ProcessHandler.execute(ProcessHandler.java:85)
	at com.github.kokorin.jaffree.ffmpeg.FFmpeg.execute(FFmpeg.java:161)
	at com.github.kokorin.jaffree.ffmpeg.FFmpeg$1.call(FFmpeg.java:176)
	at com.github.kokorin.jaffree.ffmpeg.FFmpeg$1.call(FFmpeg.java:173)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.lang.Thread.run(Thread.java:745)
2019-03-28 07:48:07.564 WARN [FFmpeg-async-runner][] c.g.k.j.p.Executor - Interrupting ALIVE thread: StdOut

Probably problem parsing ffmpeg output being (for audio only):
size= 11986kB time=00:24:55.80 bitrate= 65.6kbits/s speed=55.8x

The filter complex does not put te quotation marks and null problem

I try to trim videos and concat them. Doesn't put quatation marks and null=concat instead of only using concat. And [0:v]trim=start=0.0:end=2.0:setpts:PTS-STARTPTS[0]; after could be setpts=PTS-STARTPTS but couldn't. I want to create the following command like that
./ffmpeg -i ../Desktop/ramb.mp4 -filter_complex "[0:v]trim=start=0:end=5,setpts=PTS-STARTPTS[0]; [0:v]trim=start=10 : end=15,setpts=PTS-STARTPTS[1]; [0][1]concat[2]; [0:v]trim=start=25,setpts=PTS-STARTPTS[3]; [2][3]concat [out1]" -map [out1] ../Desktop/cut_ramb2.mp4

but command constructed like this:
ffmpeg -i /var/www/contents/rawVideo/30_Rambo.mp4 -y -filter_complex [0:v]trim=start=0.0:end=2.0:setpts:PTS-STARTPTS[0]; [0:v]trim=start=5.0:end=15.0:setpts:PTS-STARTPTS[1]; [0][1]null=concat[2]; [0:v]trim=start=40.0:end=45.0:setpts:PTS-STARTPTS[3]; [2][3]null=concat[4] -map [4] /var/www/contents/trimmedVideo/trimmed_30_Rambo.mp4

and I am using Jaffre-wrapper in Java Code like this:

FilterChain filterChain = new FilterChain();

            filterChain.addFilter(
                    Filter.fromInputLink(StreamSpecifier.withInputIndexAndType(0,StreamType.VIDEO)).
                            setName("trim").
                            addArgument("start",start.toString()).
                            addArgument("end",end.toString()).
                            addArgument("setpts").
                            addArgument("PTS-STARTPTS").
                            addOutputLink(String.valueOf(outIndex++))
            );
            filterChainList.add(filterChain);

            if(i != 0){
                FilterChain filterChain1 = new FilterChain();
                filterChain1.addFilter(
                        Filter.fromInputLink(String.valueOf(outIndex-2)).
                                addInputLink(String.valueOf(outIndex-1)).
                                addArgument("concat").
                                addOutputLink(String.valueOf(outIndex++))
                );
                filterChainList.add(filterChain1);
            }`

return FilterGraph.of().addFilterChains(filterChainList);

FilterGraph filterGraph = createFilterChain();

    FFmpegResult result = FFmpeg.atPath(ffmpegBin)
            .addInput(UrlInput.fromPath(input))
            .setOverwriteOutput(true)
            .setFilter(filterGraph.getValue())
            .addArgument("-map")
            .addArgument("[" + out + "]")
            .addOutput(UrlOutput.toPath(output))
            .execute();`

Thanks for this project. This project helped me a lot.

Rational must accept 0/0 values

Today I found that it's possible to have the following in playable MP4 video
avg_frame_rate=0/0
Last Jaffree version throws exception

Execute Jaffree profiling

It's required to profile next cases:

  1. Simple converting video from one format to another
  2. Programmatic video production with Jaffree
  3. Programmatic video consumption with Jaffree

DBase logs warning when Display Aspect Ratio is not defined in probed result

Hi @kokorin,

I've noticed that DBase logs the error below when I'm attempting to get a display aspect ratio which is not set for a particular video file. Would it make sense to make an explicit check for "N/A" and just return null before attempting to parse?

11:22:56.682 [main] WARN com.github.kokorin.jaffree.ffprobe.data.DBase - Failed to parse rational number: N/A
java.lang.NumberFormatException: For input string: "N/A"
	at com.github.kokorin.jaffree.Rational.valueOf(Rational.java:165)
	at com.github.kokorin.jaffree.ffprobe.data.DBase$RationalConverter.convert(DBase.java:158)
	at com.github.kokorin.jaffree.ffprobe.data.DBase$RationalConverter.convert(DBase.java:140)
	at com.github.kokorin.jaffree.ffprobe.data.DBase.getValue(DBase.java:44)
	at com.github.kokorin.jaffree.ffprobe.data.DBase.getRatio(DBase.java:82)
	at com.github.kokorin.jaffree.ffprobe.Stream.getDisplayAspectRatio(Stream.java:121)

I'm doing a stream.getSection().getString("display_aspect_ratio").equalsIgnoreCase("N/A") check in my own code for now, but IMO it would be nice if Jaffree handled this.

[QUESTION] How include an optional data input

Hi @kokorin, tks for this amazing library!
I'm using it to create a dynamic generator video/sound streamer:

public FFmpegResult startAndWait() throws IOException {
        FFmpeg ffmpeg = FFmpeg.atPath(Paths.get(System.getProperty("natives.ffmpeg.path"))) //
                .addInput(FrameInput.withProducer(new VideoProducer()).setFrameRate(framerate).setReadAtFrameRate(true)) //
                .addInput(FrameInput.withProducer(new AudioProducer()).setFrameRate(framerate).setReadAtFrameRate(true)) //
                .addOutput(//
                        UrlOutput.toUrl("udp://127.0.0.1:14667?pkt_size=1316")//
                                .setPixelFormat("yuv420p")//
                                .setFrameRate(framerate)//
                                .addArguments("-preset", "ultrafast")//
                                .addArguments("-tune", "zerolatency")//
                                .addArguments("-b", "900k")
                                .addArguments("-g", String.valueOf(2 * framerate))//
                                .setCodec(StreamType.VIDEO, OSDetector.isMac() ? "h264_videotoolbox" : "libx264")//
                                .setFormat("mpegts")//
                );

        return ffmpeg.execute();
    }

This code is working fine, I can consume it on VLC with address udp://@:14667 without drop frames and with good quality of video and sound :), thank you again \o/ \o/ \o/.

But I can't make a "dynamic" input work, let me explain my idea:

If I include a new UDP input, like:

addInput(UrlInput.fromUrl("udp://localhost:14666"))

And after that I create another instance of ffmpeg with output to this address (external source):

FFmpeg ffmpeg = FFmpeg.atPath(Paths.get(System.getProperty("natives.ffmpeg.path"))) //
        .addInput(UrlInput.fromPath(Paths.get("./sound.mp3")).setReadAtFrameRate(true)) //
        .addOutput(//
                UrlOutput.toUrl("udp://127.0.0.1:14666?pkt_size=1316")//
                        .addArguments("-tune", "zerolatency")//
                        .setFrameRate(25)
                        .setFormat("mpegts")//
        );

ffmpeg.execute();

The resulting of first ffmpeg drops a lot of frames, delay some seconds to show first frame and play only some parts of external sound (sound.mp3).

My question: Can ffmpeg "ignore" no data on an input? Or... Can I tell to ffmpeg to don't use this input if no data (bytes received) and only use it if data is present? (better if input fps is ignored too).

playing 64 video streams

I am trying to play 64 video feeds simultaneously, but the performance degrades as the number of streams played increases. Is there an optimization method for the same?

NumberFormatException For input string: "N/A"

java.lang.NumberFormatException: For input string: "N/A" at com.github.kokorin.jaffree.Rational.valueOf(Rational.java:165) at com.github.kokorin.jaffree.ffprobe.data.DBase$RationalConverter.convert(DBase.java:158) at com.github.kokorin.jaffree.ffprobe.data.DBase$RationalConverter.convert(DBase.java:140) at com.github.kokorin.jaffree.ffprobe.data.DBase.getValue(DBase.java:44) at com.github.kokorin.jaffree.ffprobe.data.DBase.getRatio(DBase.java:82)

if (value != null && !value.isEmpty() && !value.equals("0/0")) may add the "N/A" judge

Feature request: Piping raw video in/out of custom process

Hi @kokorin,

I'd love to have support in Jaffree for piping raw video in and out of my own custom process, specifically to be able to do something similar to this shell command:

ffmpeg -i input.mp4 -f rawvideo -pix_fmt yuv444p pipe: | ./custom-video-processor | ffmpeg -f rawvideo -r 29.97 -s 320x240 -pix_fmt yuv444p -i pipe: -f mov output.mp4

I've made my own hacky implementation by copying your FFmpeg class as a proof of concept. It gets the job done, but it would great to either see a proper implementation that allows the above or at least it would be nice if you could also invoke setStdInWriter as part of creating ProcessHandler in execute(), which would allow people to subclass FFmpeg easily for a use case like this.

Make Frame immutable

Frame reuse (as well as Image or samples) can lead to errors while producing video.

Immutable will make it more clear.

No result + non zero status: 1

Caused by: java.lang.RuntimeException: Process has ended with no result and non zero status: 1
	at com.github.kokorin.jaffree.process.ProcessHandler.execute(ProcessHandler.java:220)
	at com.github.kokorin.jaffree.ffmpeg.FFmpeg.execute(FFmpeg.java:143)
	at commands.Commands.webToGifConv(Commands.java:61)
	at commands.Commands.gifInputStream(Commands.java:88)
	at commands.Commands.onGifCommand(Commands.java:129)
	... 11 common frames omitted

with the setttings of

Path BIN = Paths.get("C:/ffmpeg/bin/");
		Path INPUT = Paths.get(path+"temp.webp");
		Path OUTPUT = Paths.get(path+"temp.gif");
		FFmpeg result = FFmpeg.atPath(BIN)
				.addInput(UrlInput.fromPath(INPUT))
				.addOutput(UrlOutput.toPath(OUTPUT)
						.setCodec(StreamType.VIDEO, "libx264"));
		result.execute();

I'm probably doing something incorrectly, which is pretty sstraightfoward, butt if anyonee could help me i'd be nice.

Iphone videos

Some iPhone video are rotated 90ยฐ. FFProbe output is as follow:

<stream>
<side_data_list>
                <side_data side_data_type="Display Matrix" displaymatrix="
00000000:            0       65536           0
00000001:       -65536           0           0
00000002:     70778880           0  1073741824
" rotation="-90"/>
</side_data_list>
</stream>

Jaffree can't get this rotation info due to current Jaffree XML schema definition.

FFMpeg is using this attribute when transcoding and inverts height and width automatically. If we force scale filter, it is not auto inverting height and width (as expected) but still rotates the frames. We should know this rotation parameter to specify the correct scale dimensions.

Add capability to parse additional FFmpeg output

First off, thanks for creating this project. I came here from ffmpeg-cli-wrapper and finally found most of the flexibility I need when using FFmpeg. However, I came along one thing I am missing:

The ability to retrieve and parse FFmpeg command line output (usually errout). For instance when using the loudnorm filter, which essentially is a two pass job that requires to retrieve and parse the parameters from the first run and use them for the second run.

I was thinking about adding a capability to add a custom OutputParser to FFmpegResultReader:

public FFmpegResult read(InputStream stdOut) {
        //just read stdOut fully
        BufferedReader reader = new BufferedReader(new InputStreamReader(stdOut));
        String errorMessage = null;

        String line;
        FFmpegResult result = null;

        try {
            while ((line = reader.readLine()) != null) {

                if (outputParser != null) {
                    outputParser.parse(line);
                }
...

OutputParser is just an interface:

public interface OutputParser {
    public void parse(String line);
}

in the end this will be invoked like:

LoudnormParser loudnormParser = new LoudnormParser()

FFmpegResult result = FFmpeg.atPath()
                .setOutputParser(loudnormParser)
                .addInput(UrlInput.fromPath(Paths.get("/Users/steve/Desktop/3-part Harm 1b_keyCmin_56bpm.wav")))
                .addArguments("-af", "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json")
                .addOutput(new NullOutput())
                .execute();

loudnormParser.getInputThreshold();

where LoundnormParser is an implementation of OutputParser.
When the command has been executed, one can obtain parsed values from the OutputParser implementation.

I will be happy to submit a PR if this sounds like a reasonable thing to add to you. And please let me know in case you come along better names ๐Ÿ˜ธ

0.8.2: metadata read problem

com.github.kokorin.jaffree.ffprobe.Stream.getTag() is empty for video stream having metadata.
ex:

Stream #0:0(und): Video: hevc (hvc1 / 0x31637668), yuv420p(tv, bt709), 1920x1080, 7709 kb/s, 29.97 fps, 29.97 tbr, 600 tbn, 600 tbc (default)
    Metadata:
      rotate          : 90
      creation_time   : 2019-03-18T07:54:08.000000Z
      handler_name    : Core Media Data Handler
      encoder         : HEVC
    Side data:
      displaymatrix: rotation of -90.00 degrees

getTag() had "rotate" metadata in tag list before (version 0.8.0).

Now we must rely only on getSideDataList() -> x.getRotation() for video rotation detection (then real width / height after ffmpeg conversion).

Not sure this is universal.

FFmpegResultReader throws exception for warnings

ffmpeg -ss 0 -t 1 -i short.wav -y -f null -c copy NUL
gives the following output:

size=N/A time=00:00:00.00 bitrate=N/A speed=N/A
video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used)

-c:v copy or removing -t 1 fix output

Investigate NUT applicability

NUT can speed up programmatic video creation and consumption, due to NUT can handle RGB24 pixel format, not YUV420<->RGB24 conversion in Java is needed.

issues with ffprobe showFormat and multiline tags

I came across a file which seems to have tags that contain values that span multiple lines. This seems to cause the DataParser to error:

Caused by: java.lang.RuntimeException: key=value was expected but got: 
	at com.github.kokorin.jaffree.ffprobe.data.DataParser.parseLine(DataParser.java:54)
	at com.github.kokorin.jaffree.ffprobe.data.DataParser.parse(DataParser.java:151)
	at com.github.kokorin.jaffree.ffprobe.FFprobeResultReader.read(FFprobeResultReader.java:40)
	at com.github.kokorin.jaffree.ffprobe.FFprobeResultReader.read(FFprobeResultReader.java:32)
	at com.github.kokorin.jaffree.process.ProcessHandler$3.run(ProcessHandler.java:186)
	at com.github.kokorin.jaffree.process.Executor$1.run(Executor.java:49)

with showFormat enabled:

ffmpeg.setShowFormat(true)

running ffprobe with options ffprobe -loglevel 16 -show_format -show_private_data shows this output (long text shortened with ...):

[FORMAT]
filename=/tmp/cvma7ou0kk5infmok4403378daciqkia/cvma7ou0kk5infmok4403378daciqkia.mp3
nb_streams=2
nb_programs=0
format_name=mp3
format_long_name=MP2/3 (MPEG audio layer 2/3)
start_time=0.011995
duration=122.044082
size=2029181
bit_rate=133012
probe_score=51
TAG:comment=The most popular...way.

Samples...provide.

Then,...format
TAG:encoder=Lavf56.40.101
[/FORMAT]

I was thinking about modifying the dataParser so that it if it does not find a new tag, it will add the content to the previous value. However, that probably will blow up as easy as the multiline value containing a =. Maybe a better approach would be to only parse values we know are relevant for ffprobes result and ignore everything else? Or we just ignore multiline tags, by simply returning instead of throwing an error? what do you think?

The file is available here https://www.dropbox.com/s/wsczg7umwfx0isc/multiline.mp3?dl=0

I was also having some issues running the tests locally, mostly because I was unable to find the input files required for testing. Are they available for download somewhere?

Improve NullOutput

It must distinct Operation System ( /dev/null in Linux and Nul in Windows).
Also it must allow to set video and audio codecs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.