├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md └── workflows │ └── maven.yml ├── .gitignore ├── LICENCE ├── README.md ├── pom.xml ├── src ├── main │ └── java │ │ └── net │ │ └── bramp │ │ ├── commons │ │ └── lang3 │ │ │ └── math │ │ │ └── gson │ │ │ └── FractionAdapter.java │ │ └── ffmpeg │ │ ├── FFcommon.java │ │ ├── FFmpeg.java │ │ ├── FFmpegException.java │ │ ├── FFmpegExecutor.java │ │ ├── FFmpegUtils.java │ │ ├── FFprobe.java │ │ ├── Preconditions.java │ │ ├── ProcessFunction.java │ │ ├── RunProcessFunction.java │ │ ├── adapter │ │ ├── BooleanTypeAdapter.java │ │ ├── FFmpegPacketsAndFramesAdapter.java │ │ └── FFmpegStreamSideDataAdapter.java │ │ ├── builder │ │ ├── AbstractFFmpegInputBuilder.java │ │ ├── AbstractFFmpegOutputBuilder.java │ │ ├── AbstractFFmpegStreamBuilder.java │ │ ├── AudioCodec.java │ │ ├── FFmpegBuilder.java │ │ ├── FFmpegFileInputBuilder.java │ │ ├── FFmpegHlsOutputBuilder.java │ │ ├── FFmpegOutputBuilder.java │ │ ├── FFprobeBuilder.java │ │ ├── MetadataSpecifier.java │ │ ├── StreamSpecifier.java │ │ ├── StreamSpecifierType.java │ │ ├── Strict.java │ │ └── VideoCodec.java │ │ ├── gson │ │ └── LowercaseEnumTypeAdapterFactory.java │ │ ├── info │ │ ├── ChannelLayout.java │ │ ├── Codec.java │ │ ├── Filter.java │ │ ├── FilterPattern.java │ │ ├── Format.java │ │ ├── IndividualChannel.java │ │ ├── InfoParser.java │ │ ├── PixelFormat.java │ │ └── StandardChannelLayout.java │ │ ├── io │ │ ├── CRC32InputStream.java │ │ ├── LoggingFilterReader.java │ │ └── ProcessUtils.java │ │ ├── job │ │ ├── FFmpegJob.java │ │ ├── SinglePassFFmpegJob.java │ │ └── TwoPassFFmpegJob.java │ │ ├── modelmapper │ │ ├── Mapper.java │ │ └── NotDefaultCondition.java │ │ ├── nut │ │ ├── Frame.java │ │ ├── FrameCode.java │ │ ├── IndexPacket.java │ │ ├── InfoPacket.java │ │ ├── MainHeaderPacket.java │ │ ├── NutDataInputStream.java │ │ ├── NutReader.java │ │ ├── NutReaderListener.java │ │ ├── Packet.java │ │ ├── PacketFooter.java │ │ ├── PacketHeader.java │ │ ├── RawHandler.java │ │ ├── Stream.java │ │ ├── StreamHeaderPacket.java │ │ └── SyncPointPacket.java │ │ ├── options │ │ ├── AudioEncodingOptions.java │ │ ├── EncodingOptions.java │ │ ├── MainEncodingOptions.java │ │ └── VideoEncodingOptions.java │ │ ├── probe │ │ ├── FFmpegChapter.java │ │ ├── FFmpegChapterTag.java │ │ ├── FFmpegDisposition.java │ │ ├── FFmpegError.java │ │ ├── FFmpegFormat.java │ │ ├── FFmpegFrame.java │ │ ├── FFmpegFrameOrPacket.java │ │ ├── FFmpegPacket.java │ │ ├── FFmpegProbeResult.java │ │ └── FFmpegStream.java │ │ ├── progress │ │ ├── AbstractSocketProgressParser.java │ │ ├── Progress.java │ │ ├── ProgressListener.java │ │ ├── ProgressParser.java │ │ ├── StreamProgressParser.java │ │ ├── TcpProgressParser.java │ │ ├── TcpProgressParserRunnable.java │ │ ├── UdpProgressParser.java │ │ └── UdpProgressParserRunnable.java │ │ └── shared │ │ └── CodecType.java └── test │ ├── java │ └── net │ │ └── bramp │ │ ├── commons │ │ └── lang3 │ │ │ └── math │ │ │ └── gson │ │ │ └── FractionAdapterTest.java │ │ └── ffmpeg │ │ ├── ExamplesTest.java │ │ ├── FFmpegAvTest.java │ │ ├── FFmpegExecutorTest.java │ │ ├── FFmpegTest.java │ │ ├── FFmpegUtilsTest.java │ │ ├── FFprobeAvTest.java │ │ ├── FFprobeTest.java │ │ ├── Helper.java │ │ ├── InputOutputTest.java │ │ ├── PreconditionsCheckInvalidNotEmptyTest.java │ │ ├── PreconditionsCheckInvalidStreamTest.java │ │ ├── PreconditionsCheckValidNotEmptyTest.java │ │ ├── PreconditionsCheckValidStreamTest.java │ │ ├── ReadmeTest.java │ │ ├── adapter │ │ ├── BooleanTypeAdapterTest.java │ │ └── FFmpegStreamSideDataAdapterTest.java │ │ ├── builder │ │ ├── AbstractFFmpegInputBuilderTest.java │ │ ├── AbstractFFmpegOutputBuilderTest.java │ │ ├── AbstractFFmpegStreamBuilderTest.java │ │ ├── FFmpegBuilderTest.java │ │ ├── FFmpegBuilderTwoPassTest.java │ │ ├── FFmpegFileInputBuilderTest.java │ │ ├── FFmpegHlsOutputBuilderTest.java │ │ ├── FFmpegOutputBuilderTest.java │ │ ├── FFprobeBuilderTest.java │ │ ├── FormatDecimalIntegerTest.java │ │ ├── MetadataSpecTest.java │ │ └── StreamSpecTest.java │ │ ├── fixtures │ │ ├── ChannelLayouts.java │ │ ├── Codecs.java │ │ ├── Filters.java │ │ ├── Formats.java │ │ ├── PixelFormats.java │ │ ├── Progresses.java │ │ └── Samples.java │ │ ├── info │ │ ├── CodecTest.java │ │ └── FFmpegGetInfoTest.java │ │ ├── io │ │ ├── HexOutputStream.java │ │ ├── LoggerOutputStream.java │ │ └── ProcessUtilsTest.java │ │ ├── lang │ │ ├── MockProcess.java │ │ └── NewProcessAnswer.java │ │ ├── modelmapper │ │ └── MapperTest.java │ │ ├── nut │ │ ├── NutReaderTest.java │ │ └── RawHandlerStreamToAudioFormatTest.java │ │ └── progress │ │ ├── AbstractProgressParserTest.java │ │ ├── RecordingProgressListener.java │ │ ├── StreamProgressParserTest.java │ │ ├── TcpProgressParserTest.java │ │ └── UdpProgressParserTest.java │ └── resources │ └── net │ └── bramp │ └── ffmpeg │ ├── fixtures │ ├── avconv-version │ ├── avprobe-version │ ├── book_with_chapters.m4b │ ├── chapters_with_long_id.m4b │ ├── ffmpeg-codecs │ ├── ffmpeg-filters │ ├── ffmpeg-formats │ ├── ffmpeg-layouts │ ├── ffmpeg-no-such-file │ ├── ffmpeg-pix_fmts │ ├── ffmpeg-progress-0 │ ├── ffmpeg-progress-1 │ ├── ffmpeg-progress-2 │ ├── ffmpeg-progress-na │ ├── ffmpeg-version │ ├── ffprobe-Always On My Mind [Program Only] - Adelen.mp4 │ ├── ffprobe-big_buck_bunny_720p_1mb.mp4 │ ├── ffprobe-big_buck_bunny_720p_1mb_frames.mp4 │ ├── ffprobe-big_buck_bunny_720p_1mb_packets.mp4 │ ├── ffprobe-big_buck_bunny_720p_1mb_packets_and_frames.mp4 │ ├── ffprobe-disposition_all_true │ ├── ffprobe-divide-by-zero │ ├── ffprobe-side_data_list │ ├── ffprobe-start_pts_test │ └── ffprobe-version │ └── samples │ ├── README.md │ ├── big_buck_bunny_720p_1mb.mp4 │ ├── test.mp3 │ └── testscreen.jpg └── tools └── codec_enum_generator.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bramp 4 | buy_me_a_coffee: bramp 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | ```java 13 | // Example code 14 | ``` 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Version (if applicable):** 20 | - OS: [e.g. Linux] 21 | - Java Version [e.g. 8] 22 | - FFmpeg version [e.g. 6.1.1] 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | 6 | --- 7 | 8 | **Is your feature request related to a problem? Please describe.** 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | **Describe the solution you'd like** 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | A clear and concise description of any alternative solutions or features you've considered. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Have a question on how to use ffmpeg-cli-wrapper 4 | labels: question 5 | 6 | --- 7 | 8 | The ffmpeg-cli-wrapper library is a simple wrapper around the ffmpeg command line. If you have questions on how to use the library, please check out the [usage](https://github.com/bramp/ffmpeg-cli-wrapper#usage), or [random examples](https://github.com/bramp/ffmpeg-cli-wrapper/wiki/Random-Examples). If that doesn't solve your problem please fill out the following: 9 | 10 | **The question** 11 | A clear and concise description of what you are trying to acheive. 12 | 13 | **Example ffmpeg command** 14 | ```shell 15 | ffmpeg -i input.mp4 output.mp4 16 | ``` 17 | 18 | **What you have tried** 19 | What you have tried so far, with example code, or error messages. 20 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | # Enable debugging to help resolve: 16 | # https://github.com/federicocarboni/setup-ffmpeg/issues/19 17 | environment: debug 18 | strategy: 19 | matrix: 20 | # Long term supported versions 21 | java-version: [8, 11, 17, 21] 22 | 23 | # TODO Should we test locales? The old travis setup did, see: 24 | # https://github.com/bramp/ffmpeg-cli-wrapper/pull/55 25 | 26 | name: JDK ${{ matrix.java-version }} 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - name: Set up FFmpeg 32 | uses: FedericoCarboni/setup-ffmpeg@v3 33 | id: setup-ffmpeg 34 | with: 35 | ffmpeg-version: release 36 | 37 | - name: Set up JDK ${{ matrix.java-version }} 38 | uses: actions/setup-java@v3 39 | with: 40 | java-version: ${{ matrix.java-version }} 41 | distribution: 'temurin' 42 | cache: maven 43 | 44 | - name: Compile with Maven 45 | run: mvn --batch-mode --update-snapshots compile 46 | 47 | - name: Test with Maven, Package and Verify with Maven 48 | run: mvn --batch-mode --update-snapshots verify -Dgpg.skip 49 | 50 | # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive 51 | - name: Update dependency graph 52 | uses: advanced-security/maven-dependency-submission-action@v4 53 | continue-on-error: true 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | ffmpeg-release-64bit-static 3 | 4 | # Maven 5 | target/ 6 | pom.xml.tag 7 | pom.xml.releaseBackup 8 | pom.xml.versionsBackup 9 | pom.xml.next 10 | release.properties 11 | dependency-reduced-pom.xml 12 | buildNumber.properties 13 | .mvn/timing.properties 14 | apidocs 15 | 16 | # IntelliJ 17 | *.iml 18 | .idea/ 19 | 20 | # Testing files 21 | *.log 22 | *.log.mbtree 23 | output.mp4 24 | tmp/ 25 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Andrew Brampton 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/commons/lang3/math/gson/FractionAdapter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.commons.lang3.math.gson; 2 | 3 | import com.google.errorprone.annotations.Immutable; 4 | import com.google.gson.TypeAdapter; 5 | import com.google.gson.stream.JsonReader; 6 | import com.google.gson.stream.JsonToken; 7 | import com.google.gson.stream.JsonWriter; 8 | import java.io.IOException; 9 | import org.apache.commons.lang3.math.Fraction; 10 | 11 | /** 12 | * GSON TypeAdapter for Apache Commons Math Fraction Object 13 | * 14 | * @author bramp 15 | */ 16 | @Immutable 17 | public class FractionAdapter extends TypeAdapter { 18 | 19 | /** If set, 0/0 returns this value, instead of throwing a ArithmeticException */ 20 | @SuppressWarnings( 21 | "Immutable") // TODO Remove when https://github.com/google/error-prone/issues/512 is fixed 22 | private final Fraction zeroByZero; 23 | 24 | /** If set, N/0 returns this value, instead of throwing a ArithmeticException */ 25 | @SuppressWarnings( 26 | "Immutable") // TODO Remove when https://github.com/google/error-prone/issues/512 is fixed 27 | private final Fraction divideByZero; 28 | 29 | public FractionAdapter() { 30 | this(Fraction.ZERO, Fraction.ZERO); 31 | } 32 | 33 | private FractionAdapter(Fraction zeroByZero, Fraction divideByZero) { 34 | this.zeroByZero = zeroByZero; 35 | this.divideByZero = divideByZero; 36 | } 37 | 38 | @Override 39 | public Fraction read(JsonReader reader) throws IOException { 40 | JsonToken next = reader.peek(); 41 | 42 | if (next == JsonToken.NULL) { 43 | reader.nextNull(); 44 | return null; 45 | } 46 | 47 | if (next == JsonToken.NUMBER) { 48 | return Fraction.getFraction(reader.nextDouble()); 49 | } 50 | 51 | String fraction = reader.nextString().trim(); 52 | 53 | // Ambiguous as to what 0/0 is, but FFmpeg seems to think it's zero 54 | if (zeroByZero != null && "0/0".equals(fraction)) { 55 | return zeroByZero; 56 | } 57 | 58 | // Another edge cases is invalid files sometimes output 1/0. 59 | if (divideByZero != null && fraction.endsWith("/0")) { 60 | return divideByZero; 61 | } 62 | 63 | return Fraction.getFraction(fraction); 64 | } 65 | 66 | @Override 67 | public void write(JsonWriter writer, Fraction value) throws IOException { 68 | if (value == null) { 69 | writer.nullValue(); 70 | return; 71 | } 72 | writer.value(value.toProperString()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/FFmpegException.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import net.bramp.ffmpeg.probe.FFmpegError; 4 | 5 | import java.io.IOException; 6 | 7 | public class FFmpegException extends IOException { 8 | 9 | private static final long serialVersionUID = 3048288225568984942L; 10 | private final FFmpegError error; 11 | 12 | public FFmpegException(String message, FFmpegError error) { 13 | super(message); 14 | this.error = error; 15 | } 16 | 17 | public FFmpegError getError() { 18 | return error; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/FFmpegExecutor.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.io.IOException; 6 | import net.bramp.ffmpeg.builder.FFmpegBuilder; 7 | import net.bramp.ffmpeg.job.FFmpegJob; 8 | import net.bramp.ffmpeg.job.SinglePassFFmpegJob; 9 | import net.bramp.ffmpeg.job.TwoPassFFmpegJob; 10 | import net.bramp.ffmpeg.progress.ProgressListener; 11 | 12 | public class FFmpegExecutor { 13 | 14 | final FFmpeg ffmpeg; 15 | final FFprobe ffprobe; 16 | 17 | public FFmpegExecutor() throws IOException { 18 | this(new FFmpeg(), new FFprobe()); 19 | } 20 | 21 | public FFmpegExecutor(FFmpeg ffmpeg) throws IOException { 22 | this(ffmpeg, new FFprobe()); 23 | } 24 | 25 | public FFmpegExecutor(FFmpeg ffmpeg, FFprobe ffprobe) { 26 | this.ffmpeg = checkNotNull(ffmpeg); 27 | this.ffprobe = checkNotNull(ffprobe); 28 | } 29 | 30 | public FFmpegJob createJob(FFmpegBuilder builder) { 31 | return new SinglePassFFmpegJob(ffmpeg, builder); 32 | } 33 | 34 | public FFmpegJob createJob(FFmpegBuilder builder, ProgressListener listener) { 35 | return new SinglePassFFmpegJob(ffmpeg, builder, listener); 36 | } 37 | 38 | /** 39 | * Creates a two pass job, which will execute FFmpeg twice to produce a better quality output. 40 | * More info: https://trac.ffmpeg.org/wiki/x264EncodingGuide#twopass 41 | * 42 | * @param builder The FFmpegBuilder 43 | * @return A new two-pass FFmpegJob 44 | */ 45 | public FFmpegJob createTwoPassJob(FFmpegBuilder builder) { 46 | return new TwoPassFFmpegJob(ffmpeg, builder); 47 | } 48 | 49 | public FFmpegJob createTwoPassJob(FFmpegBuilder builder, ProgressListener listener) { 50 | return new TwoPassFFmpegJob(ffmpeg, builder, listener); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/FFprobe.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import com.google.gson.Gson; 5 | import net.bramp.ffmpeg.builder.FFprobeBuilder; 6 | import net.bramp.ffmpeg.io.LoggingFilterReader; 7 | import net.bramp.ffmpeg.probe.FFmpegProbeResult; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import javax.annotation.CheckReturnValue; 12 | import javax.annotation.Nonnull; 13 | import javax.annotation.Nullable; 14 | import java.io.IOException; 15 | import java.io.Reader; 16 | import java.util.List; 17 | 18 | import static com.google.common.base.Preconditions.checkNotNull; 19 | 20 | /** 21 | * Wrapper around FFprobe 22 | * 23 | * @author bramp 24 | */ 25 | public class FFprobe extends FFcommon { 26 | 27 | static final Logger LOG = LoggerFactory.getLogger(FFprobe.class); 28 | 29 | static final String FFPROBE = "ffprobe"; 30 | static final String DEFAULT_PATH = MoreObjects.firstNonNull(System.getenv("FFPROBE"), FFPROBE); 31 | 32 | static final Gson gson = FFmpegUtils.getGson(); 33 | 34 | public FFprobe() throws IOException { 35 | this(DEFAULT_PATH, new RunProcessFunction()); 36 | } 37 | 38 | public FFprobe(@Nonnull ProcessFunction runFunction) throws IOException { 39 | this(DEFAULT_PATH, runFunction); 40 | } 41 | 42 | public FFprobe(@Nonnull String path) throws IOException { 43 | this(path, new RunProcessFunction()); 44 | } 45 | 46 | public FFprobe(@Nonnull String path, @Nonnull ProcessFunction runFunction) { 47 | super(path, runFunction); 48 | } 49 | 50 | public FFmpegProbeResult probe(String mediaPath) throws IOException { 51 | return probe(mediaPath, null); 52 | } 53 | 54 | /** 55 | * Returns true if the binary we are using is the true ffprobe. This is to avoid conflict with 56 | * avprobe (from the libav project), that some symlink to ffprobe. 57 | * 58 | * @return true iff this is the official ffprobe binary. 59 | * @throws IOException If a I/O error occurs while executing ffprobe. 60 | */ 61 | public boolean isFFprobe() throws IOException { 62 | return version().startsWith("ffprobe"); 63 | } 64 | 65 | /** 66 | * Throws an exception if this is an unsupported version of ffprobe. 67 | * 68 | * @throws IllegalArgumentException if this is not the official ffprobe binary. 69 | * @throws IOException If a I/O error occurs while executing ffprobe. 70 | */ 71 | private void checkIfFFprobe() throws IllegalArgumentException, IOException { 72 | if (!isFFprobe()) { 73 | throw new IllegalArgumentException( 74 | "This binary '" + path + "' is not a supported version of ffprobe"); 75 | } 76 | } 77 | 78 | @Override 79 | public void run(List args) throws IOException { 80 | checkIfFFprobe(); 81 | super.run(args); 82 | } 83 | 84 | public FFmpegProbeResult probe(String mediaPath, @Nullable String userAgent) throws IOException { 85 | return probe(this.builder().setInput(mediaPath).setUserAgent(userAgent)); 86 | } 87 | 88 | public FFmpegProbeResult probe(FFprobeBuilder builder) throws IOException { 89 | checkNotNull(builder); 90 | return probe(builder.build()); 91 | } 92 | 93 | public FFmpegProbeResult probe(String mediaPath, @Nullable String userAgent, @Nullable String... extraArgs) throws IOException { 94 | return probe(this.builder().setInput(mediaPath).setUserAgent(userAgent).addExtraArgs(extraArgs).build()); 95 | } 96 | 97 | // TODO Add Probe Inputstream 98 | public FFmpegProbeResult probe(List args) throws IOException { 99 | checkIfFFprobe(); 100 | 101 | Process p = runFunc.run(path(args)); 102 | try { 103 | Reader reader = wrapInReader(p); 104 | if (LOG.isDebugEnabled()) { 105 | reader = new LoggingFilterReader(reader, LOG); 106 | } 107 | 108 | FFmpegProbeResult result = gson.fromJson(reader, FFmpegProbeResult.class); 109 | 110 | throwOnError(p, result); 111 | 112 | if (result == null) { 113 | throw new IllegalStateException("Gson returned null, which shouldn't happen :("); 114 | } 115 | 116 | return result; 117 | 118 | } finally { 119 | p.destroy(); 120 | } 121 | } 122 | 123 | @CheckReturnValue 124 | public FFprobeBuilder builder() { 125 | return new FFprobeBuilder(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/Preconditions.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static com.google.common.base.Preconditions.checkArgument; 4 | import static com.google.common.base.Preconditions.checkNotNull; 5 | 6 | import com.google.common.base.Ascii; 7 | import com.google.common.base.CharMatcher; 8 | import com.google.common.base.Strings; 9 | import com.google.common.collect.ImmutableList; 10 | import java.net.URI; 11 | import java.util.List; 12 | import javax.annotation.Nullable; 13 | 14 | public final class Preconditions { 15 | 16 | private static final List rtps = ImmutableList.of("rtsp", "rtp", "rtmp"); 17 | private static final List udpTcp = ImmutableList.of("udp", "tcp"); 18 | 19 | Preconditions() { 20 | throw new AssertionError("No instances for you!"); 21 | } 22 | 23 | /** 24 | * Ensures the argument is not null, empty string, or just whitespace. 25 | * 26 | * @param arg The argument 27 | * @param errorMessage The exception message to use if the check fails 28 | * @return The passed in argument if it is not blank 29 | */ 30 | public static String checkNotEmpty(String arg, @Nullable Object errorMessage) { 31 | boolean empty = Strings.isNullOrEmpty(arg) || CharMatcher.whitespace().matchesAllOf(arg); 32 | checkArgument(!empty, errorMessage); 33 | return arg; 34 | } 35 | 36 | /** 37 | * Checks if the URI is valid for streaming to. 38 | * 39 | * @param uri The URI to check 40 | * @return The passed in URI if it is valid 41 | * @throws IllegalArgumentException if the URI is not valid. 42 | */ 43 | public static URI checkValidStream(URI uri) throws IllegalArgumentException { 44 | String scheme = checkNotNull(uri).getScheme(); 45 | scheme = Ascii.toLowerCase(checkNotNull(scheme, "URI is missing a scheme")); 46 | 47 | if (rtps.contains(scheme)) { 48 | return uri; 49 | } 50 | 51 | if (udpTcp.contains(scheme)) { 52 | if (uri.getPort() == -1) { 53 | throw new IllegalArgumentException("must set port when using udp or tcp scheme"); 54 | } 55 | return uri; 56 | } 57 | 58 | throw new IllegalArgumentException("not a valid output URL, must use rtp/tcp/udp scheme"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/ProcessFunction.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | /** 7 | * Runs a process returning a Reader to its stdout 8 | * 9 | * @author bramp 10 | */ 11 | public interface ProcessFunction { 12 | Process run(List args) throws IOException; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/RunProcessFunction.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.base.Preconditions; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.List; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * Simple function that creates a Process with the arguments, and returns a BufferedReader reading 13 | * stdout 14 | * 15 | * @author bramp 16 | */ 17 | public class RunProcessFunction implements ProcessFunction { 18 | 19 | static final Logger LOG = LoggerFactory.getLogger(RunProcessFunction.class); 20 | 21 | File workingDirectory; 22 | 23 | @Override 24 | public Process run(List args) throws IOException { 25 | Preconditions.checkNotNull(args, "Arguments must not be null"); 26 | Preconditions.checkArgument(!args.isEmpty(), "No arguments specified"); 27 | 28 | if (LOG.isInfoEnabled()) { 29 | LOG.info("{}", Joiner.on(" ").join(args)); 30 | } 31 | 32 | ProcessBuilder builder = new ProcessBuilder(args); 33 | if (workingDirectory != null) { 34 | builder.directory(workingDirectory); 35 | } 36 | builder.redirectErrorStream(true); 37 | return builder.start(); 38 | } 39 | 40 | public RunProcessFunction setWorkingDirectory(String workingDirectory) { 41 | this.workingDirectory = new File(workingDirectory); 42 | return this; 43 | } 44 | 45 | public RunProcessFunction setWorkingDirectory(File workingDirectory) { 46 | this.workingDirectory = workingDirectory; 47 | return this; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/adapter/BooleanTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.adapter; 2 | 3 | import java.lang.reflect.Type; 4 | import com.google.gson.*; 5 | 6 | public class BooleanTypeAdapter implements JsonDeserializer { 7 | @Override 8 | public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 9 | if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isBoolean()) { 10 | return json.getAsBoolean(); 11 | } 12 | 13 | if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) { 14 | String jsonValue = json.getAsString(); 15 | if (jsonValue.equalsIgnoreCase("true")) { 16 | return true; 17 | } else if (jsonValue.equalsIgnoreCase("false")) { 18 | return false; 19 | } else { 20 | return null; 21 | } 22 | } 23 | 24 | return json.getAsInt() != 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/adapter/FFmpegPacketsAndFramesAdapter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.adapter; 2 | 3 | import com.google.gson.*; 4 | import net.bramp.ffmpeg.probe.FFmpegFrame; 5 | import net.bramp.ffmpeg.probe.FFmpegFrameOrPacket; 6 | import net.bramp.ffmpeg.probe.FFmpegPacket; 7 | 8 | import java.lang.reflect.Type; 9 | 10 | public class FFmpegPacketsAndFramesAdapter implements JsonDeserializer { 11 | @Override 12 | public FFmpegFrameOrPacket deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { 13 | if (jsonElement instanceof JsonObject) { 14 | final String objectType = ((JsonObject) jsonElement).get("type").getAsString(); 15 | 16 | if (objectType.equals("packet")) { 17 | return jsonDeserializationContext.deserialize(jsonElement, FFmpegPacket.class); 18 | } else { 19 | return jsonDeserializationContext.deserialize(jsonElement, FFmpegFrame.class); 20 | } 21 | } 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/adapter/FFmpegStreamSideDataAdapter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.adapter; 2 | 3 | import com.google.gson.*; 4 | import net.bramp.ffmpeg.probe.FFmpegStream; 5 | 6 | import java.lang.reflect.AnnotatedType; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Type; 9 | 10 | public class FFmpegStreamSideDataAdapter implements JsonDeserializer { 11 | @Override 12 | public FFmpegStream.SideData deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { 13 | if (!(jsonElement instanceof JsonObject)) return null; 14 | try { 15 | Object sideData = Class.forName(FFmpegStream.SideData.class.getName()).getConstructor().newInstance(); 16 | Field[] fields = Class.forName(FFmpegStream.SideData.class.getName()).getFields(); 17 | for (Field field: fields) { 18 | String fieldName = field.getName(); 19 | AnnotatedType annotatedType = field.getAnnotatedType(); 20 | Object deserialize = jsonDeserializationContext.deserialize(((JsonObject) jsonElement).get(fieldName), annotatedType.getType()); 21 | field.set(sideData, deserialize); 22 | } 23 | return (FFmpegStream.SideData) sideData; 24 | } catch (Exception exception) { 25 | return null; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/AbstractFFmpegInputBuilder.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.bramp.ffmpeg.options.EncodingOptions; 5 | import net.bramp.ffmpeg.probe.FFmpegProbeResult; 6 | 7 | import javax.annotation.CheckReturnValue; 8 | 9 | public abstract class AbstractFFmpegInputBuilder> extends AbstractFFmpegStreamBuilder { 10 | private final FFmpegProbeResult probeResult; 11 | 12 | private boolean readAtNativeFrameRate; 13 | /** 14 | * Number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop. 15 | */ 16 | private int streamLoop; 17 | 18 | protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, String filename) { 19 | this(parent, null, filename); 20 | } 21 | 22 | protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, FFmpegProbeResult probeResult, String filename) { 23 | super(parent, filename); 24 | this.probeResult = probeResult; 25 | } 26 | 27 | public T readAtNativeFrameRate() { 28 | this.readAtNativeFrameRate = true; 29 | return getThis(); 30 | } 31 | 32 | /** 33 | * Sets number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop. 34 | * @param streamLoop loop counter 35 | * @return this 36 | */ 37 | public T setStreamLoop(int streamLoop) { 38 | this.streamLoop = streamLoop; 39 | 40 | return getThis(); 41 | } 42 | 43 | public FFmpegProbeResult getProbeResult() { 44 | return probeResult; 45 | } 46 | 47 | @Override 48 | @CheckReturnValue 49 | @SuppressWarnings("unchecked") 50 | protected T getThis() { 51 | return (T) this; 52 | } 53 | 54 | @Override 55 | public EncodingOptions buildOptions() { 56 | return null; 57 | } 58 | 59 | @Override 60 | protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder args) { 61 | if (this.readAtNativeFrameRate) { 62 | args.add("-re"); 63 | } 64 | 65 | if (this.streamLoop != 0) { 66 | args.add("-stream_loop", Integer.toString(this.streamLoop)); 67 | } 68 | 69 | super.addGlobalFlags(parent, args); 70 | } 71 | 72 | public int getStreamLoop() { 73 | return streamLoop; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/FFmpegFileInputBuilder.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.bramp.ffmpeg.probe.FFmpegProbeResult; 5 | 6 | public class FFmpegFileInputBuilder extends AbstractFFmpegInputBuilder { 7 | public FFmpegFileInputBuilder(FFmpegBuilder parent, String filename) { 8 | super(parent, filename); 9 | } 10 | 11 | public FFmpegFileInputBuilder(FFmpegBuilder parent, String filename, FFmpegProbeResult result) { 12 | super(parent, result, filename); 13 | 14 | } 15 | 16 | @Override 17 | protected void addSourceTarget(int pass, ImmutableList.Builder args) { 18 | if (filename != null && uri != null) { 19 | throw new IllegalStateException("Only one of filename and uri can be set"); 20 | } 21 | 22 | // Input 23 | if (filename != null) { 24 | args.add("-i", filename); 25 | } else if (uri != null) { 26 | args.add("-i", uri.toString()); 27 | } else { 28 | assert false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/FFmpegOutputBuilder.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import java.net.URI; 4 | 5 | public class FFmpegOutputBuilder extends AbstractFFmpegOutputBuilder{ 6 | 7 | public FFmpegOutputBuilder() { 8 | super(); 9 | } 10 | 11 | protected FFmpegOutputBuilder(FFmpegBuilder parent, String filename) { 12 | super(parent, filename); 13 | } 14 | 15 | protected FFmpegOutputBuilder(FFmpegBuilder parent, URI uri) { 16 | super(parent, uri); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/FFprobeBuilder.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.collect.ImmutableList; 5 | 6 | import javax.annotation.CheckReturnValue; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import static com.google.common.base.Preconditions.checkArgument; 11 | import static com.google.common.base.Preconditions.checkNotNull; 12 | import static net.bramp.ffmpeg.Preconditions.checkNotEmpty; 13 | 14 | /** 15 | * Builds a ffprobe command line 16 | */ 17 | public class FFprobeBuilder { 18 | private boolean showFormat = true; 19 | private boolean showStreams = true; 20 | private boolean showChapters = true; 21 | private boolean showFrames = false; 22 | private boolean showPackets = false; 23 | private String userAgent; 24 | private String input; 25 | 26 | private final List extraArgs = new ArrayList<>(); 27 | 28 | public FFprobeBuilder setShowFormat(boolean showFormat) { 29 | this.showFormat = showFormat; 30 | return this; 31 | } 32 | 33 | public FFprobeBuilder setShowStreams(boolean showStreams) { 34 | this.showStreams = showStreams; 35 | return this; 36 | } 37 | 38 | public FFprobeBuilder setShowChapters(boolean showChapters) { 39 | this.showChapters = showChapters; 40 | return this; 41 | } 42 | 43 | public FFprobeBuilder setShowFrames(boolean showFrames) { 44 | this.showFrames = showFrames; 45 | return this; 46 | } 47 | 48 | public FFprobeBuilder setShowPackets(boolean showPackets) { 49 | this.showPackets = showPackets; 50 | return this; 51 | } 52 | 53 | public FFprobeBuilder setUserAgent(String userAgent) { 54 | this.userAgent = userAgent; 55 | return this; 56 | } 57 | 58 | public FFprobeBuilder setInput(String filename) { 59 | checkNotNull(filename); 60 | this.input = filename; 61 | return this; 62 | } 63 | 64 | public FFprobeBuilder addExtraArgs(String... values) { 65 | checkArgument(values != null, "extraArgs can not be null"); 66 | checkArgument(values.length > 0, "one or more values must be supplied"); 67 | checkNotEmpty(values[0], "first extra arg may not be empty"); 68 | 69 | for (String value : values) { 70 | extraArgs.add(checkNotNull(value)); 71 | } 72 | return this; 73 | } 74 | 75 | @CheckReturnValue 76 | public List build() { 77 | ImmutableList.Builder args = new ImmutableList.Builder<>(); 78 | 79 | Preconditions.checkNotNull(input, "Input must be specified"); 80 | 81 | args 82 | .add("-v", "quiet") 83 | .add("-print_format", "json") 84 | .add("-show_error"); 85 | 86 | if (userAgent != null) { 87 | args.add("-user_agent", userAgent); 88 | } 89 | 90 | args.addAll(extraArgs); 91 | 92 | if (showFormat) args.add("-show_format"); 93 | if (showStreams) args.add("-show_streams"); 94 | if (showChapters) args.add("-show_chapters"); 95 | if (showPackets) args.add("-show_packets"); 96 | if (showFrames) args.add("-show_frames"); 97 | 98 | args.add(input); 99 | 100 | return args.build(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/MetadataSpecifier.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | // metadata_spec can be: 4 | // g global (If metadata specifier is omitted, it defaults to global.) 5 | // s[:stream_spec] 6 | // c:chapter_index 7 | // p:program_index 8 | // index is meant to be zero based, by negitive is allowed as dummy values 9 | 10 | import static com.google.common.base.Preconditions.checkArgument; 11 | import static com.google.common.base.Preconditions.checkNotNull; 12 | 13 | import com.google.errorprone.annotations.Immutable; 14 | 15 | /** 16 | * Metadata spec, as described in the "map_metadata" section of 17 | * Main options 18 | */ 19 | @Immutable 20 | public class MetadataSpecifier { 21 | 22 | private final String spec; 23 | 24 | private MetadataSpecifier(String spec) { 25 | this.spec = checkNotNull(spec); 26 | } 27 | 28 | private MetadataSpecifier(String prefix, int index) { 29 | this.spec = checkNotNull(prefix) + ":" + index; 30 | } 31 | 32 | private MetadataSpecifier(String prefix, StreamSpecifier spec) { 33 | this.spec = checkNotNull(prefix) + ":" + checkNotNull(spec).spec(); 34 | } 35 | 36 | public String spec() { 37 | return spec; 38 | } 39 | 40 | public static String checkValidKey(String key) { 41 | checkNotNull(key); 42 | checkArgument(!key.isEmpty(), "key must not be empty"); 43 | checkArgument(key.matches("\\w+"), "key must only contain letters, numbers or _"); 44 | return key; 45 | } 46 | 47 | public static MetadataSpecifier global() { 48 | return new MetadataSpecifier("g"); 49 | } 50 | 51 | public static MetadataSpecifier chapter(int index) { 52 | return new MetadataSpecifier("c", index); 53 | } 54 | 55 | public static MetadataSpecifier program(int index) { 56 | return new MetadataSpecifier("p", index); 57 | } 58 | 59 | public static MetadataSpecifier stream(int index) { 60 | return new MetadataSpecifier("s", StreamSpecifier.stream(index)); 61 | } 62 | 63 | public static MetadataSpecifier stream(StreamSpecifierType type) { 64 | return new MetadataSpecifier("s", StreamSpecifier.stream(type)); 65 | } 66 | 67 | public static MetadataSpecifier stream(StreamSpecifierType stream_type, int stream_index) { 68 | return new MetadataSpecifier("s", StreamSpecifier.stream(stream_type, stream_index)); 69 | } 70 | 71 | public static MetadataSpecifier stream(StreamSpecifier spec) { 72 | checkNotNull(spec); 73 | return new MetadataSpecifier("s", spec); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/StreamSpecifier.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | import static net.bramp.ffmpeg.builder.MetadataSpecifier.checkValidKey; 5 | 6 | /** Stream specifier */ 7 | public class StreamSpecifier { 8 | 9 | private final String spec; 10 | 11 | private StreamSpecifier(String spec) { 12 | this.spec = spec; 13 | } 14 | 15 | public String spec() { 16 | return spec; 17 | } 18 | 19 | /** 20 | * Matches the stream with this index. 21 | * 22 | * @param index The stream index 23 | * @return A new StreamSpecifier 24 | */ 25 | public static StreamSpecifier stream(int index) { 26 | return new StreamSpecifier(String.valueOf(index)); 27 | } 28 | 29 | /** 30 | * Matches all streams of this type. 31 | * 32 | * @param type The stream type 33 | * @return A new StreamSpecifier 34 | */ 35 | public static StreamSpecifier stream(StreamSpecifierType type) { 36 | checkNotNull(type); 37 | return new StreamSpecifier(type.toString()); 38 | } 39 | 40 | /** 41 | * Matches the stream number stream_index of this type. 42 | * 43 | * @param type The stream type 44 | * @param index The stream index 45 | * @return A new StreamSpecifier 46 | */ 47 | public static StreamSpecifier stream(StreamSpecifierType type, int index) { 48 | checkNotNull(type); 49 | return new StreamSpecifier(type.toString() + ":" + index); 50 | } 51 | 52 | /** 53 | * Matches all streams in the program. 54 | * 55 | * @param program_id The program id 56 | * @return A new StreamSpecifier 57 | */ 58 | public static StreamSpecifier program(int program_id) { 59 | return new StreamSpecifier("p:" + program_id); 60 | } 61 | 62 | /** 63 | * Matches the stream with number stream_index in the program with the id program_id. 64 | * 65 | * @param program_id The program id 66 | * @param stream_index The stream index 67 | * @return A new StreamSpecifier 68 | */ 69 | public static StreamSpecifier program(int program_id, int stream_index) { 70 | return new StreamSpecifier("p:" + program_id + ":" + stream_index); 71 | } 72 | 73 | /** 74 | * Match the stream by stream id (e.g. PID in MPEG-TS container). 75 | * 76 | * @param stream_id The stream id 77 | * @return A new StreamSpecifier 78 | */ 79 | public static StreamSpecifier id(int stream_id) { 80 | return new StreamSpecifier("i:" + stream_id); 81 | } 82 | 83 | /** 84 | * Matches all streams with the given metadata tag. 85 | * 86 | * @param key The metadata tag 87 | * @return A new StreamSpecifier 88 | */ 89 | public static StreamSpecifier tag(String key) { 90 | return new StreamSpecifier("m:" + checkValidKey(key)); 91 | } 92 | 93 | /** 94 | * Matches streams with the metadata tag key having the specified value. 95 | * 96 | * @param key The metadata tag 97 | * @param value The metatdata's value 98 | * @return A new StreamSpecifier 99 | */ 100 | public static StreamSpecifier tag(String key, String value) { 101 | checkValidKey(key); 102 | checkNotNull(value); 103 | return new StreamSpecifier("m:" + key + ":" + value); 104 | } 105 | 106 | /** 107 | * Matches streams with usable configuration, the codec must be defined and the essential 108 | * information such as video dimension or audio sample rate must be present. 109 | * 110 | * @return A new StreamSpecifier 111 | */ 112 | public static StreamSpecifier usable() { 113 | return new StreamSpecifier("u"); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/StreamSpecifierType.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | public enum StreamSpecifierType { 4 | /** Video */ 5 | Video("v"), 6 | 7 | /** Video streams which are not attached pictures, video thumbnails or cover arts. */ 8 | PureVideo("V"), 9 | 10 | /** Audio */ 11 | Audio("a"), 12 | 13 | /** Subtitles */ 14 | Subtitle("s"), 15 | 16 | /** Data */ 17 | Data("d"), 18 | 19 | /** Attachment */ 20 | Attachment("t"); 21 | 22 | final String prefix; 23 | 24 | StreamSpecifierType(String prefix) { 25 | this.prefix = prefix; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return prefix; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/builder/Strict.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.base.Ascii; 4 | 5 | public enum Strict { 6 | VERY, // strictly conform to an older more strict version of the specifications or reference 7 | // software 8 | STRICT, // strictly conform to all the things in the specificiations no matter what consequences 9 | NORMAL, // normal 10 | UNOFFICIAL, // allow unofficial extensions 11 | EXPERIMENTAL; 12 | 13 | @Override 14 | public String toString() { 15 | // ffmpeg command line requires these options in lower case 16 | return Ascii.toLowerCase(name()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/gson/LowercaseEnumTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.gson; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import com.google.common.collect.ImmutableMap; 6 | import com.google.errorprone.annotations.Immutable; 7 | import com.google.gson.Gson; 8 | import com.google.gson.TypeAdapter; 9 | import com.google.gson.TypeAdapterFactory; 10 | import com.google.gson.reflect.TypeToken; 11 | import com.google.gson.stream.JsonReader; 12 | import com.google.gson.stream.JsonToken; 13 | import com.google.gson.stream.JsonWriter; 14 | import java.io.IOException; 15 | import java.util.HashMap; 16 | import java.util.Locale; 17 | import java.util.Map; 18 | import javax.annotation.CheckReturnValue; 19 | import javax.annotation.Nonnull; 20 | 21 | /** 22 | * Maps Enums to lowercase strings. 23 | * 24 | *

Adapted from: TypeAdapterFactory 27 | */ 28 | @Immutable 29 | public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory { 30 | 31 | @Immutable 32 | private static class MyTypeAdapter extends TypeAdapter { 33 | 34 | // T is a Enum, thus immutable, however, we can't enforce that type due to the 35 | // TypeAdapterFactory interface 36 | @SuppressWarnings("Immutable") 37 | private final ImmutableMap lowercaseToEnum; 38 | 39 | public MyTypeAdapter(Map lowercaseToEnum) { 40 | this.lowercaseToEnum = ImmutableMap.copyOf(lowercaseToEnum); 41 | } 42 | 43 | @Override 44 | public void write(JsonWriter out, T value) throws IOException { 45 | checkNotNull(out); 46 | 47 | if (value == null) { 48 | out.nullValue(); 49 | } else { 50 | out.value(toLowercase(value)); 51 | } 52 | } 53 | 54 | @Override 55 | public T read(JsonReader reader) throws IOException { 56 | checkNotNull(reader); 57 | 58 | if (reader.peek() == JsonToken.NULL) { 59 | reader.nextNull(); 60 | return null; 61 | } 62 | return lowercaseToEnum.get(reader.nextString()); 63 | } 64 | } 65 | 66 | @CheckReturnValue 67 | @Override 68 | @SuppressWarnings("unchecked") 69 | public TypeAdapter create(Gson gson, TypeToken type) { 70 | checkNotNull(type); 71 | 72 | Class rawType = (Class) type.getRawType(); 73 | if (!rawType.isEnum()) { 74 | return null; 75 | } 76 | 77 | // Setup mapping of consts 78 | final Map lowercaseToEnum = new HashMap<>(); 79 | for (T constant : rawType.getEnumConstants()) { 80 | lowercaseToEnum.put(toLowercase(constant), constant); 81 | } 82 | 83 | return new MyTypeAdapter(lowercaseToEnum); 84 | } 85 | 86 | @CheckReturnValue 87 | private static String toLowercase(@Nonnull Object o) { 88 | return checkNotNull(o).toString().toLowerCase(Locale.UK); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/ChannelLayout.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | public interface ChannelLayout { 4 | String getName(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/Filter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | public class Filter { 7 | /** Is timeline editing supported */ 8 | private final boolean timelineSupported; 9 | 10 | /** Is slice based multi-threading supported */ 11 | private final boolean sliceThreading; 12 | 13 | /** Are there command line options */ 14 | private final boolean commandSupport; 15 | 16 | /** The filters name */ 17 | private final String name; 18 | 19 | /** The input filter pattern */ 20 | private final FilterPattern inputPattern; 21 | 22 | /** The output filter pattern */ 23 | private final FilterPattern outputPattern; 24 | 25 | /** A short description of the filter */ 26 | private final String description; 27 | 28 | public Filter(boolean timelineSupported, boolean sliceThreading, boolean commandSupport, String name, FilterPattern inputPattern, FilterPattern outputPattern, String description) { 29 | this.timelineSupported = timelineSupported; 30 | this.sliceThreading = sliceThreading; 31 | this.commandSupport = commandSupport; 32 | this.name = name; 33 | this.inputPattern = inputPattern; 34 | this.outputPattern = outputPattern; 35 | this.description = description; 36 | } 37 | 38 | public boolean isTimelineSupported() { 39 | return timelineSupported; 40 | } 41 | 42 | public boolean isSliceThreading() { 43 | return sliceThreading; 44 | } 45 | 46 | public boolean isCommandSupport() { 47 | return commandSupport; 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public FilterPattern getInputPattern() { 55 | return inputPattern; 56 | } 57 | 58 | public FilterPattern getOutputPattern() { 59 | return outputPattern; 60 | } 61 | 62 | public String getDescription() { 63 | return description; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object obj) { 68 | return EqualsBuilder.reflectionEquals(this, obj); 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return name; 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | return HashCodeBuilder.reflectionHashCode(this); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/FilterPattern.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import net.bramp.ffmpeg.shared.CodecType; 4 | import org.apache.commons.lang3.builder.EqualsBuilder; 5 | import org.apache.commons.lang3.builder.HashCodeBuilder; 6 | 7 | import java.util.*; 8 | 9 | public class FilterPattern { 10 | /** Indicates whether this pattern represents a source or a sink and therefore has no other options */ 11 | private final boolean sinkOrSource; 12 | 13 | /** Indicates whether this pattern accepts a variable number of streams */ 14 | private final boolean variableStreams; 15 | 16 | /** Contains a pattern matching the stream types supported */ 17 | private final List streams; 18 | 19 | public FilterPattern(String pattern) { 20 | this.sinkOrSource = pattern.contains("|"); 21 | this.variableStreams = pattern.contains("N"); 22 | List streams = new ArrayList<>(); 23 | 24 | for (int i = 0; i < pattern.length(); i++) { 25 | final char c = pattern.charAt(i); 26 | 27 | if (c == '|' || c == 'N') { 28 | // These symbols are handled separately 29 | continue; 30 | } 31 | if (c == 'A') { 32 | streams.add(CodecType.AUDIO); 33 | } else if (c == 'V') { 34 | streams.add(CodecType.VIDEO); 35 | } else { 36 | throw new IllegalStateException("Unsupported character in filter pattern " + c); 37 | } 38 | } 39 | 40 | this.streams = Collections.unmodifiableList(streams); 41 | } 42 | 43 | public boolean isSinkOrSource() { 44 | return sinkOrSource; 45 | } 46 | 47 | public boolean isVariableStreams() { 48 | return variableStreams; 49 | } 50 | 51 | public List getStreams() { 52 | return streams; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object obj) { 57 | return EqualsBuilder.reflectionEquals(this, obj); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | if (isSinkOrSource()) { 63 | return "|"; 64 | } 65 | 66 | if (isVariableStreams()) { 67 | return "N"; 68 | } 69 | 70 | return Arrays.toString(this.streams.toArray()); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return HashCodeBuilder.reflectionHashCode(this); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/Format.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.errorprone.annotations.Immutable; 5 | import org.apache.commons.lang3.builder.EqualsBuilder; 6 | import org.apache.commons.lang3.builder.HashCodeBuilder; 7 | 8 | /** 9 | * Information about supported Format 10 | * 11 | * @author bramp 12 | */ 13 | @Immutable 14 | public class Format { 15 | private final String name; 16 | private final String longName; 17 | 18 | private final boolean canDemux; 19 | private final boolean canMux; 20 | 21 | /** 22 | * @param name short format name 23 | * @param longName long format name 24 | * @param flags is expected to be in the following format: 25 | *

26 |    * D. = Demuxing supported
27 |    * .E = Muxing supported
28 |    * 
29 | */ 30 | public Format(String name, String longName, String flags) { 31 | this.name = Preconditions.checkNotNull(name).trim(); 32 | this.longName = Preconditions.checkNotNull(longName).trim(); 33 | 34 | Preconditions.checkNotNull(flags); 35 | Preconditions.checkArgument(flags.length() == 2, "Format flags is invalid '%s'", flags); 36 | canDemux = flags.charAt(0) == 'D'; 37 | canMux = flags.charAt(1) == 'E'; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return name + " " + longName; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | return EqualsBuilder.reflectionEquals(this, obj); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return HashCodeBuilder.reflectionHashCode(this); 53 | } 54 | 55 | public String getName() { 56 | return name; 57 | } 58 | 59 | public String getLongName() { 60 | return longName; 61 | } 62 | 63 | public boolean getCanDemux() { 64 | return canDemux; 65 | } 66 | 67 | public boolean getCanMux() { 68 | return canMux; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/IndividualChannel.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | public class IndividualChannel implements ChannelLayout { 7 | private final String name; 8 | private final String description; 9 | 10 | public IndividualChannel(String name, String description) { 11 | this.name = name; 12 | this.description = description; 13 | 14 | } 15 | 16 | @Override 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | public String getDescription() { 22 | return description; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return name + " " + description; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | return EqualsBuilder.reflectionEquals(this, obj); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return HashCodeBuilder.reflectionHashCode(this); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/InfoParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.util.*; 6 | 7 | import com.google.common.base.Splitter; 8 | 9 | public final class InfoParser { 10 | private InfoParser() { 11 | throw new AssertionError("No instances for you!"); 12 | } 13 | 14 | public static List parseLayouts(BufferedReader r) throws IOException { 15 | Map individualChannelLookup = new HashMap<>(); 16 | List channelLayouts = new ArrayList<>(); 17 | 18 | String line; 19 | boolean parsingIndividualChannels = false; 20 | boolean parsingChannelLayouts = false; 21 | 22 | while ((line = r.readLine()) != null) { 23 | if (line.startsWith("NAME") || line.isEmpty()) { 24 | // Skip header and empty lines 25 | continue; 26 | } else if (line.equals("Individual channels:")) { 27 | parsingIndividualChannels = true; 28 | parsingChannelLayouts = false; 29 | } else if (line.equals("Standard channel layouts:")) { 30 | parsingIndividualChannels = false; 31 | parsingChannelLayouts = true; 32 | } else if (parsingIndividualChannels) { 33 | String[] s = line.split(" ", 2); 34 | IndividualChannel individualChannel = new IndividualChannel(s[0], s[1].trim()); 35 | channelLayouts.add(individualChannel); 36 | individualChannelLookup.put(individualChannel.getName(), individualChannel); 37 | } else if (parsingChannelLayouts) { 38 | String[] s = line.split(" ", 2); 39 | List decomposition = new ArrayList<>(); 40 | for (String channelName : Splitter.on('+').split(s[1].trim())) { 41 | decomposition.add(individualChannelLookup.get(channelName)); 42 | } 43 | 44 | channelLayouts.add(new StandardChannelLayout(s[0], Collections.unmodifiableList(decomposition))); 45 | } 46 | } 47 | 48 | return channelLayouts; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/PixelFormat.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | public class PixelFormat { 7 | private final String name; 8 | private final int numberOfComponents; 9 | private final int bitsPerPixel; 10 | 11 | private final boolean canDecode; 12 | private final boolean canEncode; 13 | private final boolean hardwareAccelerated; 14 | private final boolean palettedFormat; 15 | private final boolean bitstreamFormat; 16 | 17 | public PixelFormat(String name, int numberOfComponents, int bitsPerPixel, String flags) { 18 | this.name = name; 19 | this.numberOfComponents = numberOfComponents; 20 | this.bitsPerPixel = bitsPerPixel; 21 | 22 | this.canDecode = flags.charAt(0) == 'I'; 23 | this.canEncode = flags.charAt(1) == 'O'; 24 | this.hardwareAccelerated = flags.charAt(2) == 'H'; 25 | this.palettedFormat = flags.charAt(3) == 'P'; 26 | this.bitstreamFormat = flags.charAt(4) == 'B'; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return name; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object obj) { 36 | return EqualsBuilder.reflectionEquals(this, obj); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return HashCodeBuilder.reflectionHashCode(this); 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public int getBitsPerPixel() { 49 | return bitsPerPixel; 50 | } 51 | 52 | public int getNumberOfComponents() { 53 | return numberOfComponents; 54 | } 55 | 56 | public boolean canEncode() { 57 | return canEncode; 58 | } 59 | 60 | public boolean canDecode() { 61 | return canDecode; 62 | } 63 | 64 | public boolean isHardwareAccelerated() { 65 | return hardwareAccelerated; 66 | } 67 | 68 | public boolean isPalettedFormat() { 69 | return palettedFormat; 70 | } 71 | 72 | public boolean isBitstreamFormat() { 73 | return bitstreamFormat; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/info/StandardChannelLayout.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | import java.util.List; 7 | 8 | public class StandardChannelLayout implements ChannelLayout { 9 | private final String name; 10 | private final List decomposition; 11 | 12 | public StandardChannelLayout(String name, List decomposition) { 13 | this.name = name; 14 | this.decomposition = decomposition; 15 | } 16 | 17 | @Override 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public List getDecomposition() { 23 | return decomposition; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return name; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | return EqualsBuilder.reflectionEquals(this, obj); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return HashCodeBuilder.reflectionHashCode(this); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/io/CRC32InputStream.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import java.io.FilterInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.zip.CRC32; 7 | 8 | /** 9 | * Calculates the CRC32 for all bytes read through the input stream. Using the java.util.zip.CRC32 10 | * class to calculate the checksum. 11 | */ 12 | public class CRC32InputStream extends FilterInputStream { 13 | 14 | final CRC32 crc = new CRC32(); 15 | 16 | public CRC32InputStream(InputStream in) { 17 | super(in); 18 | } 19 | 20 | public void resetCrc() { 21 | crc.reset(); 22 | } 23 | 24 | public long getValue() { 25 | return crc.getValue(); 26 | } 27 | 28 | @Override 29 | public int read() throws IOException { 30 | int b = in.read(); 31 | if (b >= 0) { 32 | crc.update(b); 33 | } 34 | return b; 35 | } 36 | 37 | @Override 38 | public int read(byte[] b) throws IOException { 39 | int len = in.read(b); 40 | crc.update(b, 0, len); 41 | return len; 42 | } 43 | 44 | @Override 45 | public int read(byte[] b, int off, int len) throws IOException { 46 | int actual = in.read(b, off, len); 47 | crc.update(b, off, actual); 48 | return actual; 49 | } 50 | 51 | @Override 52 | public long skip(long n) throws IOException { 53 | long i = 0; 54 | while (i < n) { 55 | read(); 56 | i++; 57 | } 58 | return i; 59 | } 60 | 61 | @Override 62 | public synchronized void mark(int readlimit) { 63 | throw new UnsupportedOperationException("mark not supported"); 64 | } 65 | 66 | @Override 67 | public synchronized void reset() throws IOException { 68 | throw new UnsupportedOperationException("reset not supported"); 69 | } 70 | 71 | @Override 72 | public boolean markSupported() { 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/io/LoggingFilterReader.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import java.io.FilterReader; 4 | import java.io.IOException; 5 | import java.io.Reader; 6 | import org.slf4j.Logger; 7 | 8 | /** 9 | * Wraps a Reader, and logs full lines of input as it is read. 10 | * 11 | * @author bramp 12 | */ 13 | public class LoggingFilterReader extends FilterReader { 14 | 15 | static final char LOG_CHAR = '\n'; 16 | 17 | final Logger logger; 18 | final StringBuilder buffer = new StringBuilder(); 19 | 20 | public LoggingFilterReader(Reader in, Logger logger) { 21 | super(in); 22 | this.logger = logger; 23 | } 24 | 25 | protected void log() { 26 | if (buffer.length() > 0) { 27 | // TODO Change from debug, to a user defined level 28 | logger.debug(buffer.toString()); 29 | buffer.setLength(0); 30 | } 31 | } 32 | 33 | private static int indexOf(char[] array, char c, int off, int len) { 34 | for (int i = off; i < off + len; i++) { 35 | if (array[i] == c) { 36 | return i; 37 | } 38 | } 39 | return -1; 40 | } 41 | 42 | @Override 43 | public int read(char[] cbuf, int off, int len) throws IOException { 44 | int ret = super.read(cbuf, off, len); 45 | if (ret != -1) { 46 | buffer.append(cbuf, off, ret); 47 | } 48 | 49 | // If end of stream, or contains new line 50 | if (ret == -1 || indexOf(cbuf, LOG_CHAR, off, ret) != -1) { 51 | // BUG this will log a unfinished line, if a string such as 52 | // "line \n unfinished" is read. 53 | log(); 54 | } 55 | 56 | return ret; 57 | } 58 | 59 | @Override 60 | public int read() throws IOException { 61 | int ret = super.read(); 62 | if (ret != -1) { 63 | buffer.append((char) ret); 64 | } 65 | 66 | // If end of stream, or contains new line 67 | if (ret == -1 || ret == LOG_CHAR) { 68 | log(); 69 | } 70 | return ret; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/io/ProcessUtils.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | /** 7 | * A collection of utility methods for dealing with processes. 8 | * 9 | * @author bramp 10 | */ 11 | public final class ProcessUtils { 12 | private ProcessUtils() { 13 | throw new AssertionError("No instances for you!"); 14 | } 15 | 16 | /** 17 | * Waits until a process finishes or a timeout occurs 18 | * 19 | * @param p process 20 | * @param timeout timeout in given unit 21 | * @param unit time unit 22 | * @return the process exit value 23 | * @throws TimeoutException if a timeout occurs 24 | */ 25 | public static int waitForWithTimeout(final Process p, long timeout, TimeUnit unit) 26 | throws TimeoutException { 27 | 28 | try { 29 | p.waitFor(timeout, unit); 30 | 31 | } catch (InterruptedException e) { 32 | Thread.currentThread().interrupt(); 33 | } 34 | 35 | if (p.isAlive()) { 36 | throw new TimeoutException("Process did not finish within timeout"); 37 | } 38 | 39 | return p.exitValue(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/job/FFmpegJob.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.job; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import javax.annotation.Nullable; 6 | import net.bramp.ffmpeg.FFmpeg; 7 | import net.bramp.ffmpeg.progress.ProgressListener; 8 | 9 | /** 10 | * A FFmpegJob is a single job that can be run by FFmpeg. It can be a single pass, or a two pass job. 11 | * 12 | * @author bramp 13 | */ 14 | public abstract class FFmpegJob implements Runnable { 15 | 16 | public enum State { 17 | WAITING, 18 | RUNNING, 19 | FINISHED, 20 | FAILED, 21 | } 22 | 23 | final FFmpeg ffmpeg; 24 | final ProgressListener listener; 25 | 26 | State state = State.WAITING; 27 | 28 | public FFmpegJob(FFmpeg ffmpeg) { 29 | this(ffmpeg, null); 30 | } 31 | 32 | public FFmpegJob(FFmpeg ffmpeg, @Nullable ProgressListener listener) { 33 | this.ffmpeg = checkNotNull(ffmpeg); 34 | this.listener = listener; 35 | } 36 | 37 | public State getState() { 38 | return state; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/job/SinglePassFFmpegJob.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.job; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import com.google.common.base.Throwables; 6 | import java.util.List; 7 | import javax.annotation.Nullable; 8 | import net.bramp.ffmpeg.FFmpeg; 9 | import net.bramp.ffmpeg.builder.FFmpegBuilder; 10 | import net.bramp.ffmpeg.progress.ProgressListener; 11 | 12 | public class SinglePassFFmpegJob extends FFmpegJob { 13 | 14 | public final FFmpegBuilder builder; 15 | 16 | public SinglePassFFmpegJob(FFmpeg ffmpeg, FFmpegBuilder builder) { 17 | this(ffmpeg, builder, null); 18 | } 19 | 20 | public SinglePassFFmpegJob( 21 | FFmpeg ffmpeg, FFmpegBuilder builder, @Nullable ProgressListener listener) { 22 | super(ffmpeg, listener); 23 | this.builder = checkNotNull(builder); 24 | 25 | // Build the args now (but throw away the results). This allows the illegal arguments to be 26 | // caught early, but also allows the ffmpeg command to actually alter the arguments when 27 | // running. 28 | @SuppressWarnings("unused") 29 | List unused = this.builder.build(); 30 | } 31 | 32 | @Override 33 | public void run() { 34 | 35 | state = State.RUNNING; 36 | 37 | try { 38 | ffmpeg.run(builder, listener); 39 | state = State.FINISHED; 40 | 41 | } catch (Throwable t) { 42 | state = State.FAILED; 43 | 44 | Throwables.throwIfUnchecked(t); 45 | throw new RuntimeException(t); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/job/TwoPassFFmpegJob.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.job; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import com.google.common.base.Throwables; 6 | import java.io.IOException; 7 | import java.nio.file.DirectoryStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.util.List; 12 | import java.util.UUID; 13 | import javax.annotation.Nullable; 14 | import net.bramp.ffmpeg.FFmpeg; 15 | import net.bramp.ffmpeg.builder.FFmpegBuilder; 16 | import net.bramp.ffmpeg.progress.ProgressListener; 17 | 18 | public class TwoPassFFmpegJob extends FFmpegJob { 19 | 20 | final String passlogPrefix; 21 | final FFmpegBuilder builder; 22 | 23 | public TwoPassFFmpegJob(FFmpeg ffmpeg, FFmpegBuilder builder) { 24 | this(ffmpeg, builder, null); 25 | } 26 | 27 | public TwoPassFFmpegJob( 28 | FFmpeg ffmpeg, FFmpegBuilder builder, @Nullable ProgressListener listener) { 29 | super(ffmpeg, listener); 30 | 31 | // Random prefix so multiple runs don't clash 32 | this.passlogPrefix = UUID.randomUUID().toString(); 33 | this.builder = checkNotNull(builder).setPassPrefix(passlogPrefix); 34 | 35 | // Build the args now (but throw away the results). This allows the illegal arguments to be 36 | // caught early, but also allows the ffmpeg command to actually alter the arguments when 37 | // running. 38 | @SuppressWarnings("unused") 39 | List unused = this.builder.setPass(1).build(); 40 | } 41 | 42 | protected void deletePassLog() throws IOException { 43 | final Path cwd = Paths.get(""); 44 | try (DirectoryStream stream = Files.newDirectoryStream(cwd, passlogPrefix + "*.log*")) { 45 | for (Path p : stream) { 46 | Files.deleteIfExists(p); 47 | } 48 | } 49 | } 50 | 51 | @Override 52 | public void run() { 53 | state = State.RUNNING; 54 | 55 | try { 56 | try { 57 | // Two pass 58 | final boolean override = builder.getOverrideOutputFiles(); 59 | 60 | FFmpegBuilder b1 = builder.setPass(1).overrideOutputFiles(true); 61 | ffmpeg.run(b1, listener); 62 | 63 | FFmpegBuilder b2 = builder.setPass(2).overrideOutputFiles(override); 64 | ffmpeg.run(b2, listener); 65 | 66 | } finally { 67 | deletePassLog(); 68 | } 69 | state = State.FINISHED; 70 | 71 | } catch (Throwable t) { 72 | state = State.FAILED; 73 | 74 | Throwables.throwIfUnchecked(t); 75 | throw new RuntimeException(t); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/modelmapper/Mapper.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.modelmapper; 2 | 3 | import static net.bramp.ffmpeg.modelmapper.NotDefaultCondition.notDefault; 4 | 5 | import net.bramp.ffmpeg.builder.AbstractFFmpegOutputBuilder; 6 | import net.bramp.ffmpeg.builder.AbstractFFmpegStreamBuilder; 7 | import net.bramp.ffmpeg.builder.FFmpegOutputBuilder; 8 | import net.bramp.ffmpeg.options.AudioEncodingOptions; 9 | import net.bramp.ffmpeg.options.EncodingOptions; 10 | import net.bramp.ffmpeg.options.MainEncodingOptions; 11 | import net.bramp.ffmpeg.options.VideoEncodingOptions; 12 | import org.modelmapper.ModelMapper; 13 | import org.modelmapper.TypeMap; 14 | import org.modelmapper.config.Configuration; 15 | import org.modelmapper.convention.NameTokenizers; 16 | 17 | /** 18 | * Copies values from one type of object to another 19 | * 20 | * @author bramp 21 | */ 22 | public class Mapper { 23 | 24 | private Mapper() { 25 | throw new InstantiationError("Must not instantiate this class"); 26 | } 27 | 28 | private static final ModelMapper mapper = newModelMapper(); 29 | 30 | private static TypeMap createTypeMap( 31 | ModelMapper mapper, Class sourceType, Class destinationType, Configuration config) { 32 | 33 | return mapper 34 | .createTypeMap(sourceType, destinationType, config) 35 | // We setPropertyCondition because ModelMapper seems to ignore this in 36 | // the config 37 | .setPropertyCondition(config.getPropertyCondition()); 38 | } 39 | 40 | private static ModelMapper newModelMapper() { 41 | final ModelMapper mapper = new ModelMapper(); 42 | 43 | Configuration config = 44 | mapper 45 | .getConfiguration() 46 | .copy() 47 | .setFieldMatchingEnabled(true) 48 | .setPropertyCondition(notDefault) 49 | .setSourceNameTokenizer(NameTokenizers.UNDERSCORE); 50 | 51 | createTypeMap(mapper, MainEncodingOptions.class, FFmpegOutputBuilder.class, config); 52 | createTypeMap(mapper, AudioWrapper.class, FFmpegOutputBuilder.class, config); 53 | createTypeMap(mapper, VideoWrapper.class, FFmpegOutputBuilder.class, config); 54 | 55 | return mapper; 56 | } 57 | 58 | /** Simple wrapper object, to inject the word "audio" in the property name */ 59 | static class AudioWrapper { 60 | public final AudioEncodingOptions audio; 61 | 62 | AudioWrapper(AudioEncodingOptions audio) { 63 | this.audio = audio; 64 | } 65 | } 66 | 67 | /** Simple wrapper object, to inject the word "video" in the property name */ 68 | static class VideoWrapper { 69 | public final VideoEncodingOptions video; 70 | 71 | VideoWrapper(VideoEncodingOptions video) { 72 | this.video = video; 73 | } 74 | } 75 | 76 | public static > void map( 77 | MainEncodingOptions opts, T dest) { 78 | mapper.map(opts, dest); 79 | } 80 | 81 | public static > void map( 82 | AudioEncodingOptions opts, T dest) { 83 | mapper.map(new AudioWrapper(opts), dest); 84 | } 85 | 86 | public static > void map( 87 | VideoEncodingOptions opts, T dest) { 88 | mapper.map(new VideoWrapper(opts), dest); 89 | } 90 | 91 | public static > void map( 92 | EncodingOptions opts, T dest) { 93 | map(opts.getMain(), dest); 94 | 95 | if (opts.getAudio().isEnabled()) { 96 | map(opts.getAudio(), dest); 97 | } 98 | if (opts.getVideo().isEnabled()) { 99 | map(opts.getVideo(), dest); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/modelmapper/NotDefaultCondition.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.modelmapper; 2 | 3 | import com.google.common.base.Defaults; 4 | import com.google.common.base.Objects; 5 | import com.google.errorprone.annotations.Immutable; 6 | import org.modelmapper.Condition; 7 | import org.modelmapper.spi.MappingContext; 8 | 9 | /** 10 | * Only maps properties which are not their type's default value. 11 | * 12 | * @param source type 13 | * @param destination type 14 | * @author bramp 15 | */ 16 | @Immutable 17 | public class NotDefaultCondition implements Condition { 18 | 19 | public static final NotDefaultCondition notDefault = new NotDefaultCondition<>(); 20 | 21 | @Override 22 | public boolean applies(MappingContext context) { 23 | return !Objects.equal(context.getSource(), Defaults.defaultValue(context.getSourceType())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/FrameCode.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import com.google.common.base.MoreObjects; 4 | 5 | public class FrameCode { 6 | 7 | long flags; 8 | int streamId; 9 | int dataSizeMul; 10 | int dataSizeLsb; 11 | long ptsDelta; 12 | int reservedCount; 13 | long matchTimeDelta; 14 | int headerIdx; 15 | 16 | @Override 17 | public String toString() { 18 | return MoreObjects.toStringHelper(this) 19 | .add("flags", flags) 20 | .add("id", streamId) 21 | .add("dataSizeMul", dataSizeMul) 22 | .add("dataSizeLsb", dataSizeLsb) 23 | .add("ptsDelta", ptsDelta) 24 | .add("reservedCount", reservedCount) 25 | .add("matchTimeDelta", matchTimeDelta) 26 | .add("headerIdx", headerIdx) 27 | .toString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/IndexPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | // TODO 4 | public class IndexPacket {} 5 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/InfoPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | // TODO 4 | public class InfoPacket {} 5 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/NutReaderListener.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | public interface NutReaderListener { 4 | 5 | /** 6 | * Executes when a new stream is found. 7 | * 8 | * @param stream The stream 9 | */ 10 | void stream(Stream stream); 11 | 12 | /** 13 | * Executes when a new frame is found. 14 | * 15 | * @param frame A single Frame 16 | */ 17 | void frame(Frame frame); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/Packet.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import java.io.IOException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class Packet { 9 | 10 | static final Logger LOG = LoggerFactory.getLogger(Packet.class); 11 | 12 | public enum Startcode { 13 | MAIN(0x7A561F5F04ADL + (((long) ('N' << 8) + 'M') << 48)), 14 | STREAM(0x11405BF2F9DBL + (((long) ('N' << 8) + 'S') << 48)), 15 | SYNCPOINT(0xE4ADEECA4569L + (((long) ('N' << 8) + 'K') << 48)), 16 | INDEX(0xDD672F23E64EL + (((long) ('N' << 8) + 'X') << 48)), 17 | INFO(0xAB68B596BA78L + (((long) ('N' << 8) + 'I') << 48)); 18 | 19 | private final long startcode; 20 | 21 | Startcode(long startcode) { 22 | this.startcode = startcode; 23 | } 24 | 25 | public long value() { 26 | return startcode; 27 | } 28 | 29 | public boolean equalsCode(long startcode) { 30 | return this.startcode == startcode; 31 | } 32 | 33 | /** 34 | * Returns the Startcode enum for this code. 35 | * 36 | * @param startcode The numeric code for this Startcode. 37 | * @return The Startcode 38 | */ 39 | public static Startcode of(long startcode) { 40 | for (Startcode c : Startcode.values()) { 41 | if (c.equalsCode(startcode)) { 42 | return c; 43 | } 44 | } 45 | return null; 46 | } 47 | 48 | public static boolean isPossibleStartcode(long startcode) { 49 | return (startcode & 0xFFL) == 'N'; 50 | } 51 | 52 | public static String toString(long startcode) { 53 | Startcode c = of(startcode); 54 | if (c != null) { 55 | return c.name(); 56 | } 57 | return String.format("%X", startcode); 58 | } 59 | } 60 | 61 | public final PacketHeader header = new PacketHeader(); 62 | public final PacketFooter footer = new PacketFooter(); 63 | 64 | protected void readBody(NutDataInputStream in) throws IOException { 65 | // Default implementation does nothing 66 | } 67 | 68 | public void read(NutDataInputStream in, long startcode) throws IOException { 69 | header.read(in, startcode); 70 | readBody(in); 71 | seekToPacketFooter(in); 72 | footer.read(in); 73 | } 74 | 75 | public void seekToPacketFooter(NutDataInputStream in) throws IOException { 76 | long current = in.offset(); 77 | if (current > header.end) { 78 | throw new IOException("Can not seek backwards at:" + current + " end:" + header.end); 79 | } 80 | // TODO Fix this to not cast longs to ints 81 | in.skipBytes((int) (header.end - current)); 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return MoreObjects.toStringHelper(this).add("header", header).add("footer", footer).toString(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/PacketFooter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import java.io.IOException; 5 | 6 | public class PacketFooter { 7 | int checksum; 8 | 9 | public void read(NutDataInputStream in) throws IOException { 10 | long expected = in.getCRC(); 11 | checksum = in.readInt(); 12 | if (checksum != expected) { 13 | // throw new IOException(String.format("invalid packet checksum %X want %X", expected, 14 | // checksum)); 15 | Packet.LOG.debug("invalid packet checksum {} want {}", expected, checksum); 16 | } 17 | in.resetCRC(); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return MoreObjects.toStringHelper(this).add("checksum", checksum).toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/PacketHeader.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import java.io.IOException; 5 | 6 | public class PacketHeader { 7 | 8 | long startcode; 9 | long forwardPtr; 10 | int checksum; // header checksum 11 | 12 | long end; // End byte of packet 13 | 14 | public void read(NutDataInputStream in, long startcode) throws IOException { 15 | this.startcode = startcode; 16 | forwardPtr = in.readVarLong(); 17 | if (forwardPtr > 4096) { 18 | long expected = in.getCRC(); 19 | checksum = in.readInt(); 20 | if (checksum != expected) { 21 | // TODO This code path has never been tested. 22 | throw new IOException( 23 | String.format("invalid header checksum %X want %X", expected, checksum)); 24 | } 25 | } 26 | 27 | in.resetCRC(); 28 | end = in.offset() + forwardPtr - 4; // 4 bytes for footer CRC 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | MoreObjects.ToStringHelper helper = 34 | MoreObjects.toStringHelper(this) 35 | .add("startcode", Packet.Startcode.toString(startcode)) 36 | .add("forwardPtr", forwardPtr); 37 | 38 | if (forwardPtr > 4096) { 39 | helper = helper.add("checksum", checksum); 40 | } 41 | return helper.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/Stream.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import java.io.IOException; 4 | import org.apache.commons.lang3.math.Fraction; 5 | 6 | public class Stream { 7 | final StreamHeaderPacket header; 8 | 9 | final Fraction timeBase; 10 | long last_pts = 0; 11 | 12 | public Stream(MainHeaderPacket header, StreamHeaderPacket streamHeader) throws IOException { 13 | this.header = streamHeader; 14 | if (streamHeader.timeBaseId >= header.timeBase.length) { 15 | throw new IOException( 16 | "Invalid timeBaseId " + streamHeader.timeBaseId + " must be < " + header.timeBase.length); 17 | } 18 | this.timeBase = header.timeBase[streamHeader.timeBaseId]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/StreamHeaderPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import org.apache.commons.lang3.math.Fraction; 7 | 8 | public class StreamHeaderPacket extends Packet { 9 | 10 | public static final int VIDEO = 0; 11 | public static final int AUDIO = 1; 12 | public static final int SUBTITLE = 2; 13 | public static final int USER_DATA = 3; 14 | 15 | int id; 16 | long type; // One of VIDEO/AUDIO/SUBTITLE/USER_DATA // TODO Convert to enum. 17 | byte[] fourcc; 18 | int timeBaseId; 19 | int msbPtsShift; 20 | int maxPtsDistance; 21 | long decodeDelay; 22 | long flags; 23 | byte[] codecSpecificData; 24 | 25 | // If video 26 | int width; 27 | int height; 28 | int sampleWidth; 29 | int sampleHeight; 30 | long colorspaceType; 31 | 32 | // If audio 33 | Fraction sampleRate = Fraction.ZERO; 34 | int channels; 35 | 36 | protected static String fourccToString(byte[] fourcc) { 37 | return new String(fourcc, StandardCharsets.ISO_8859_1); 38 | } 39 | 40 | @Override 41 | protected void readBody(NutDataInputStream in) throws IOException { 42 | 43 | id = in.readVarInt(); 44 | type = in.readVarLong(); 45 | fourcc = in.readVarArray(); 46 | 47 | if (fourcc.length != 2 && fourcc.length != 4) { 48 | // TODO In future fourcc could be a different size, but for sanity checking lets leave this 49 | // check in. 50 | throw new IOException("Unexpected fourcc length: " + fourcc.length); 51 | } 52 | 53 | timeBaseId = in.readVarInt(); 54 | msbPtsShift = in.readVarInt(); 55 | if (msbPtsShift >= 16) { 56 | throw new IOException("invalid msbPtsShift " + msbPtsShift + " want < 16"); 57 | } 58 | maxPtsDistance = in.readVarInt(); 59 | decodeDelay = in.readVarLong(); 60 | flags = in.readVarLong(); 61 | codecSpecificData = in.readVarArray(); 62 | 63 | if (type == VIDEO) { 64 | width = in.readVarInt(); 65 | height = in.readVarInt(); 66 | 67 | if (width == 0 || height == 0) { 68 | throw new IOException("invalid video dimensions " + width + "x" + height); 69 | } 70 | 71 | sampleWidth = in.readVarInt(); 72 | sampleHeight = in.readVarInt(); 73 | 74 | // Both MUST be 0 if unknown otherwise both MUST be nonzero. 75 | if ((sampleWidth == 0 || sampleHeight == 0) && sampleWidth != sampleHeight) { 76 | throw new IOException( 77 | "invalid video sample dimensions " + sampleWidth + "x" + sampleHeight); 78 | } 79 | 80 | colorspaceType = in.readVarLong(); 81 | 82 | } else if (type == AUDIO) { 83 | int samplerateNum = in.readVarInt(); 84 | int samplerateDenom = in.readVarInt(); 85 | sampleRate = Fraction.getFraction(samplerateNum, samplerateDenom); 86 | channels = in.readVarInt(); 87 | } 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return MoreObjects.toStringHelper(this) 93 | .add("header", header) 94 | .add("id", id) 95 | .add("type", type) 96 | .add("fourcc", fourccToString(fourcc)) 97 | .add("timeBaseId", timeBaseId) 98 | .add("msbPtsShift", msbPtsShift) 99 | .add("maxPtsDistance", maxPtsDistance) 100 | .add("decodeDelay", decodeDelay) 101 | .add("flags", flags) 102 | .add("codecSpecificData", codecSpecificData) 103 | .add("width", width) 104 | .add("height", height) 105 | .add("sampleWidth", sampleWidth) 106 | .add("sampleHeight", sampleHeight) 107 | .add("colorspaceType", colorspaceType) 108 | .add("sampleRate", sampleRate) 109 | .add("channels", channels) 110 | .add("footer", footer) 111 | .toString(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/nut/SyncPointPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | // TODO 4 | public class SyncPointPacket {} 5 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/options/AudioEncodingOptions.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.options; 2 | 3 | import java.beans.ConstructorProperties; 4 | 5 | /** 6 | * Encoding options for audio 7 | * 8 | * @author bramp 9 | */ 10 | public class AudioEncodingOptions { 11 | /** @deprecated Use {@link #isEnabled()} instead */ 12 | @Deprecated 13 | public final boolean enabled; 14 | /** @deprecated Use {@link #getCodec()} instead */ 15 | @Deprecated 16 | public final String codec; 17 | /** @deprecated Use {@link #getChannels()} instead */ 18 | @Deprecated 19 | public final int channels; 20 | /** @deprecated Use {@link #getSampleRate()} instead */ 21 | @Deprecated 22 | public final int sample_rate; 23 | /** @deprecated Use {@link #getSampleFormat()} instead */ 24 | @Deprecated 25 | public final String sample_format; 26 | /** @deprecated Use {@link #getBitRate()} instead */ 27 | @Deprecated 28 | public final long bit_rate; 29 | /** @deprecated Use {@link #getQuality()} instead */ 30 | @Deprecated 31 | public final Double quality; 32 | 33 | @ConstructorProperties({ 34 | "enabled", 35 | "codec", 36 | "channels", 37 | "sample_rate", 38 | "sample_format", 39 | "bit_rate", 40 | "quality" 41 | }) 42 | public AudioEncodingOptions( 43 | boolean enabled, 44 | String codec, 45 | int channels, 46 | int sample_rate, 47 | String sample_format, 48 | long bit_rate, 49 | Double quality) { 50 | this.enabled = enabled; 51 | this.codec = codec; 52 | this.channels = channels; 53 | this.sample_rate = sample_rate; 54 | this.sample_format = sample_format; 55 | this.bit_rate = bit_rate; 56 | this.quality = quality; 57 | } 58 | 59 | public boolean isEnabled() { 60 | return enabled; 61 | } 62 | 63 | public String getCodec() { 64 | return codec; 65 | } 66 | 67 | public int getChannels() { 68 | return channels; 69 | } 70 | 71 | public int getSampleRate() { 72 | return sample_rate; 73 | } 74 | 75 | public String getSampleFormat() { 76 | return sample_format; 77 | } 78 | 79 | public long getBitRate() { 80 | return bit_rate; 81 | } 82 | 83 | public Double getQuality() { 84 | return quality; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/options/EncodingOptions.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.options; 2 | 3 | import java.beans.ConstructorProperties; 4 | 5 | /** 6 | * Audio, Video and Main encoding options for ffmpeg. 7 | * 8 | * @author bramp 9 | */ 10 | public class EncodingOptions { 11 | /** @deprecated Use {@link #getMain()} instead */ 12 | @Deprecated 13 | public final MainEncodingOptions main; 14 | /** @deprecated Use {@link #getAudio()} instead */ 15 | @Deprecated 16 | public final AudioEncodingOptions audio; 17 | /** @deprecated Use {@link #getVideo()} instead */ 18 | @Deprecated 19 | public final VideoEncodingOptions video; 20 | 21 | @ConstructorProperties({"main", "audio", "video"}) 22 | public EncodingOptions( 23 | MainEncodingOptions main, AudioEncodingOptions audio, VideoEncodingOptions video) { 24 | this.main = main; 25 | this.audio = audio; 26 | this.video = video; 27 | } 28 | 29 | public MainEncodingOptions getMain() { 30 | return main; 31 | } 32 | 33 | public AudioEncodingOptions getAudio() { 34 | return audio; 35 | } 36 | 37 | public VideoEncodingOptions getVideo() { 38 | return video; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/options/MainEncodingOptions.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.options; 2 | 3 | import java.beans.ConstructorProperties; 4 | 5 | /** 6 | * Encoding options that are specific to the main output. 7 | * 8 | * @author bramp 9 | */ 10 | public class MainEncodingOptions { 11 | /** @deprecated Use {@link #getFormat()} instead */ 12 | @Deprecated 13 | public final String format; 14 | /** @deprecated Use {@link #getStartOffset()} instead */ 15 | @Deprecated 16 | public final Long startOffset; 17 | /** @deprecated Use {@link #getDuration()} instead */ 18 | @Deprecated 19 | public final Long duration; 20 | 21 | @ConstructorProperties({"format", "startOffset", "duration"}) 22 | public MainEncodingOptions(String format, Long startOffset, Long duration) { 23 | this.format = format; 24 | this.startOffset = startOffset; 25 | this.duration = duration; 26 | } 27 | 28 | public String getFormat() { 29 | return format; 30 | } 31 | 32 | public Long getStartOffset() { 33 | return startOffset; 34 | } 35 | 36 | public Long getDuration() { 37 | return duration; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/options/VideoEncodingOptions.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.options; 2 | 3 | import java.beans.ConstructorProperties; 4 | import org.apache.commons.lang3.math.Fraction; 5 | 6 | /** 7 | * Encoding options for video 8 | * 9 | * @author bramp 10 | */ 11 | public class VideoEncodingOptions { 12 | /** @deprecated Use {@link #isEnabled()} instead */ 13 | @Deprecated 14 | public final boolean enabled; 15 | /** @deprecated Use {@link #getCodec()} instead */ 16 | @Deprecated 17 | public final String codec; 18 | /** @deprecated Use {@link #getFrameRate()} instead */ 19 | @Deprecated 20 | public final Fraction frame_rate; 21 | /** @deprecated Use {@link #getWidth()} instead */ 22 | @Deprecated 23 | public final int width; 24 | /** @deprecated Use {@link #getHeight()} instead */ 25 | @Deprecated 26 | public final int height; 27 | /** @deprecated Use {@link #getBitRate()} instead */ 28 | @Deprecated 29 | public final long bit_rate; 30 | /** @deprecated Use {@link #getFrames()} instead */ 31 | @Deprecated 32 | public final Integer frames; 33 | /** @deprecated Use {@link #getFilter()} instead */ 34 | @Deprecated 35 | public final String filter; 36 | /** @deprecated Use {@link #getPreset()} instead */ 37 | @Deprecated 38 | public final String preset; 39 | 40 | @ConstructorProperties({ 41 | "enabled", 42 | "codec", 43 | "frame_rate", 44 | "width", 45 | "height", 46 | "bit_rate", 47 | "frames", 48 | "video_filter", 49 | "preset" 50 | }) 51 | public VideoEncodingOptions( 52 | boolean enabled, 53 | String codec, 54 | Fraction frame_rate, 55 | int width, 56 | int height, 57 | long bit_rate, 58 | Integer frames, 59 | String filter, 60 | String preset) { 61 | this.enabled = enabled; 62 | this.codec = codec; 63 | this.frame_rate = frame_rate; 64 | this.width = width; 65 | this.height = height; 66 | this.bit_rate = bit_rate; 67 | this.frames = frames; 68 | this.filter = filter; 69 | this.preset = preset; 70 | } 71 | 72 | public boolean isEnabled() { 73 | return enabled; 74 | } 75 | 76 | public String getCodec() { 77 | return codec; 78 | } 79 | 80 | public Fraction getFrameRate() { 81 | return frame_rate; 82 | } 83 | 84 | public int getWidth() { 85 | return width; 86 | } 87 | 88 | public int getHeight() { 89 | return height; 90 | } 91 | 92 | public long getBitRate() { 93 | return bit_rate; 94 | } 95 | 96 | public Integer getFrames() { 97 | return frames; 98 | } 99 | 100 | public String getFilter() { 101 | return filter; 102 | } 103 | 104 | public String getPreset() { 105 | return preset; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegChapter.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | @SuppressFBWarnings( 6 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 7 | justification = "POJO objects where the fields are populated by gson") 8 | public class FFmpegChapter { 9 | public long id; 10 | public String time_base; 11 | public long start; 12 | public String start_time; 13 | public long end; 14 | public String end_time; 15 | public FFmpegChapterTag tags; 16 | 17 | public long getId() { 18 | return id; 19 | } 20 | 21 | public String getTimeBase() { 22 | return time_base; 23 | } 24 | 25 | public long getStart() { 26 | return start; 27 | } 28 | 29 | public String getStartTime() { 30 | return start_time; 31 | } 32 | 33 | public long getEnd() { 34 | return end; 35 | } 36 | 37 | public String getEndTime() { 38 | return end_time; 39 | } 40 | 41 | public FFmpegChapterTag getTags() { 42 | return tags; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegChapterTag.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | @SuppressFBWarnings( 6 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 7 | justification = "POJO objects where the fields are populated by gson") 8 | public class FFmpegChapterTag { 9 | public String title; 10 | 11 | public String getTitle() { 12 | return title; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegDisposition.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import com.google.gson.annotations.JsonAdapter; 4 | import com.google.gson.annotations.SerializedName; 5 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 6 | import net.bramp.ffmpeg.adapter.BooleanTypeAdapter; 7 | 8 | /** Represents the AV_DISPOSITION_* fields */ 9 | @SuppressFBWarnings( 10 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 11 | justification = "POJO objects where the fields are populated by gson") 12 | public class FFmpegDisposition { 13 | @SerializedName("default") 14 | @JsonAdapter(BooleanTypeAdapter.class) 15 | public boolean _default; 16 | 17 | @JsonAdapter(BooleanTypeAdapter.class) 18 | public boolean dub; 19 | 20 | @JsonAdapter(BooleanTypeAdapter.class) 21 | public boolean original; 22 | 23 | @JsonAdapter(BooleanTypeAdapter.class) 24 | public boolean comment; 25 | 26 | @JsonAdapter(BooleanTypeAdapter.class) 27 | public boolean lyrics; 28 | 29 | @JsonAdapter(BooleanTypeAdapter.class) 30 | public boolean karaoke; 31 | 32 | @JsonAdapter(BooleanTypeAdapter.class) 33 | public boolean forced; 34 | 35 | @JsonAdapter(BooleanTypeAdapter.class) 36 | public boolean hearing_impaired; 37 | 38 | @JsonAdapter(BooleanTypeAdapter.class) 39 | public boolean visual_impaired; 40 | 41 | @JsonAdapter(BooleanTypeAdapter.class) 42 | public boolean clean_effects; 43 | 44 | @JsonAdapter(BooleanTypeAdapter.class) 45 | public boolean attached_pic; 46 | 47 | @JsonAdapter(BooleanTypeAdapter.class) 48 | public boolean timed_thumbnails; 49 | 50 | @JsonAdapter(BooleanTypeAdapter.class) 51 | public boolean non_diegetic; 52 | 53 | @JsonAdapter(BooleanTypeAdapter.class) 54 | public boolean captions; 55 | 56 | @JsonAdapter(BooleanTypeAdapter.class) 57 | public boolean descriptions; 58 | 59 | @JsonAdapter(BooleanTypeAdapter.class) 60 | public boolean metadata; 61 | 62 | @JsonAdapter(BooleanTypeAdapter.class) 63 | public boolean dependent; 64 | 65 | @JsonAdapter(BooleanTypeAdapter.class) 66 | public boolean still_image; 67 | 68 | public boolean isDefault() { 69 | return _default; 70 | } 71 | 72 | public boolean isDub() { 73 | return dub; 74 | } 75 | 76 | public boolean isOriginal() { 77 | return original; 78 | } 79 | 80 | public boolean isComment() { 81 | return comment; 82 | } 83 | 84 | public boolean isLyrics() { 85 | return lyrics; 86 | } 87 | 88 | public boolean isKaraoke() { 89 | return karaoke; 90 | } 91 | 92 | public boolean isForced() { 93 | return forced; 94 | } 95 | 96 | public boolean isHearingImpaired() { 97 | return hearing_impaired; 98 | } 99 | 100 | public boolean isVisualImpaired() { 101 | return visual_impaired; 102 | } 103 | 104 | public boolean isCleanEffects() { 105 | return clean_effects; 106 | } 107 | 108 | public boolean isAttachedPic() { 109 | return attached_pic; 110 | } 111 | 112 | public boolean isTimedThumbnails() { 113 | return timed_thumbnails; 114 | } 115 | 116 | public boolean isNonDiegetic() { 117 | return non_diegetic; 118 | } 119 | 120 | public boolean isCaptions() { 121 | return captions; 122 | } 123 | 124 | public boolean isDescriptions() { 125 | return descriptions; 126 | } 127 | 128 | public boolean isMetadata() { 129 | return metadata; 130 | } 131 | 132 | public boolean isDependent() { 133 | return dependent; 134 | } 135 | 136 | public boolean isStillImage() { 137 | return still_image; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegError.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | import java.io.Serializable; 6 | 7 | @SuppressFBWarnings( 8 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 9 | justification = "POJO objects where the fields are populated by gson") 10 | public class FFmpegError implements Serializable { 11 | private static final long serialVersionUID = 1L; 12 | 13 | public int code; 14 | public String string; 15 | 16 | public int getCode() { 17 | return code; 18 | } 19 | 20 | public String getString() { 21 | return string; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegFormat.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | import java.util.Map; 6 | 7 | @SuppressFBWarnings( 8 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"}, 9 | justification = "POJO objects where the fields are populated by gson") 10 | public class FFmpegFormat { 11 | public String filename; 12 | public int nb_streams; 13 | public int nb_programs; 14 | 15 | public String format_name; 16 | public String format_long_name; 17 | public double start_time; 18 | 19 | // TODO Change this to java.time.Duration 20 | /** 21 | * Duration in seconds 22 | */ 23 | public double duration; 24 | 25 | /** 26 | * File size in bytes 27 | */ 28 | public long size; 29 | 30 | /** 31 | * Bitrate 32 | */ 33 | public long bit_rate; 34 | 35 | public int probe_score; 36 | 37 | public Map tags; 38 | 39 | public String getFilename() { 40 | return filename; 41 | } 42 | 43 | public int getNbStreams() { 44 | return nb_streams; 45 | } 46 | 47 | public int getNbPrograms() { 48 | return nb_programs; 49 | } 50 | 51 | public String getFormatName() { 52 | return format_name; 53 | } 54 | 55 | public String getFormatLongName() { 56 | return format_long_name; 57 | } 58 | 59 | public double getStartTime() { 60 | return start_time; 61 | } 62 | 63 | public double getDuration() { 64 | return duration; 65 | } 66 | 67 | public long getSize() { 68 | return size; 69 | } 70 | 71 | public long getBitRate() { 72 | return bit_rate; 73 | } 74 | 75 | public int getProbeScore() { 76 | return probe_score; 77 | } 78 | 79 | public Map getTags() { 80 | return ImmutableMap.copyOf(tags); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegFrame.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import net.bramp.ffmpeg.shared.CodecType; 5 | 6 | @SuppressFBWarnings( 7 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 8 | justification = "POJO objects where the fields are populated by gson") 9 | public class FFmpegFrame implements FFmpegFrameOrPacket { 10 | public CodecType media_type; 11 | public int stream_index; 12 | public int key_frame; 13 | public long pkt_pts; 14 | public double pkt_pts_time; 15 | public long pkt_dts; 16 | public double pkt_dts_time; 17 | public long best_effort_timestamp; 18 | public float best_effort_timestamp_time; 19 | public long pkt_duration; 20 | public float pkt_duration_time; 21 | public long pkt_pos; 22 | public long pkt_size; 23 | public String sample_fmt; 24 | public int nb_samples; 25 | public int channels; 26 | public String channel_layout; 27 | 28 | public CodecType getMediaType() { 29 | return media_type; 30 | } 31 | 32 | public int getStreamIndex() { 33 | return stream_index; 34 | } 35 | 36 | public int getKeyFrame() { 37 | return key_frame; 38 | } 39 | 40 | public long getPktPts() { 41 | return pkt_pts; 42 | } 43 | 44 | public double getPktPtsTime() { 45 | return pkt_pts_time; 46 | } 47 | 48 | public long getPktDts() { 49 | return pkt_dts; 50 | } 51 | 52 | public double getPktDtsTime() { 53 | return pkt_dts_time; 54 | } 55 | 56 | public long getBestEffortTimestamp() { 57 | return best_effort_timestamp; 58 | } 59 | 60 | public float getBestEffortTimestampTime() { 61 | return best_effort_timestamp_time; 62 | } 63 | 64 | public long getPktDuration() { 65 | return pkt_duration; 66 | } 67 | 68 | public float getPktDurationTime() { 69 | return pkt_duration_time; 70 | } 71 | 72 | public long getPktPos() { 73 | return pkt_pos; 74 | } 75 | 76 | public long getPktSize() { 77 | return pkt_size; 78 | } 79 | 80 | public String getSampleFmt() { 81 | return sample_fmt; 82 | } 83 | 84 | public int getNbSamples() { 85 | return nb_samples; 86 | } 87 | 88 | public int getChannels() { 89 | return channels; 90 | } 91 | 92 | public String getChannelLayout() { 93 | return channel_layout; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegFrameOrPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | public interface FFmpegFrameOrPacket { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegPacket.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import net.bramp.ffmpeg.shared.CodecType; 5 | 6 | @SuppressFBWarnings( 7 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 8 | justification = "POJO objects where the fields are populated by gson") 9 | public class FFmpegPacket implements FFmpegFrameOrPacket { 10 | public CodecType codec_type; 11 | public int stream_index; 12 | public long pts; 13 | public double pts_time; 14 | public long dts; 15 | public double dts_time; 16 | public long duration; 17 | public float duration_time; 18 | public String size; 19 | public String pos; 20 | public String flags; 21 | 22 | public CodecType getCodecType() { 23 | return codec_type; 24 | } 25 | 26 | public int getStreamIndex() { 27 | return stream_index; 28 | } 29 | 30 | public long getPts() { 31 | return pts; 32 | } 33 | 34 | public double getPtsTime() { 35 | return pts_time; 36 | } 37 | 38 | public long getDts() { 39 | return dts; 40 | } 41 | 42 | public double getDtsTime() { 43 | return dts_time; 44 | } 45 | 46 | public long getDuration() { 47 | return duration; 48 | } 49 | 50 | public float getDurationTime() { 51 | return duration_time; 52 | } 53 | 54 | public String getSize() { 55 | return size; 56 | } 57 | 58 | public String getPos() { 59 | return pos; 60 | } 61 | 62 | public String getFlags() { 63 | return flags; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/probe/FFmpegProbeResult.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.probe; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** TODO Make this immutable */ 11 | @SuppressFBWarnings( 12 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"}, 13 | justification = "POJO objects where the fields are populated by gson") 14 | public class FFmpegProbeResult { 15 | public FFmpegError error; 16 | public FFmpegFormat format; 17 | public List streams; 18 | public List chapters; 19 | 20 | private List packets; 21 | private List frames; 22 | public List packets_and_frames; 23 | 24 | public FFmpegError getError() { 25 | return error; 26 | } 27 | 28 | public boolean hasError() { 29 | return error != null; 30 | } 31 | 32 | public FFmpegFormat getFormat() { 33 | return format; 34 | } 35 | 36 | public List getStreams() { 37 | if (streams == null) return Collections.emptyList(); 38 | return ImmutableList.copyOf(streams); 39 | } 40 | 41 | public List getChapters() { 42 | if (chapters == null) return Collections.emptyList(); 43 | return ImmutableList.copyOf(chapters); 44 | } 45 | 46 | public List getPackets() { 47 | if (packets == null) { 48 | if (packets_and_frames != null) { 49 | List tmp = new ArrayList<>(); 50 | for (FFmpegFrameOrPacket packetsAndFrame : packets_and_frames) { 51 | if (packetsAndFrame instanceof FFmpegPacket) { 52 | tmp.add((FFmpegPacket) packetsAndFrame); 53 | } 54 | } 55 | packets = tmp; 56 | } else { 57 | return Collections.emptyList(); 58 | } 59 | } 60 | 61 | return ImmutableList.copyOf(packets); 62 | } 63 | 64 | public List getFrames() { 65 | if (frames == null) { 66 | if (packets_and_frames != null) { 67 | List tmp = new ArrayList<>(); 68 | for (FFmpegFrameOrPacket packetsAndFrame : packets_and_frames) { 69 | if (packetsAndFrame instanceof FFmpegFrame) { 70 | tmp.add((FFmpegFrame) packetsAndFrame); 71 | } 72 | } 73 | frames = tmp; 74 | } else { 75 | return Collections.emptyList(); 76 | } 77 | } 78 | return ImmutableList.copyOf(frames); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/AbstractSocketProgressParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import com.google.common.net.InetAddresses; 6 | import java.io.IOException; 7 | import java.net.InetAddress; 8 | import java.net.URI; 9 | import java.net.URISyntaxException; 10 | import java.util.concurrent.CountDownLatch; 11 | import javax.annotation.CheckReturnValue; 12 | 13 | public abstract class AbstractSocketProgressParser implements ProgressParser { 14 | 15 | final StreamProgressParser parser; 16 | 17 | Thread thread; // Thread for handling incoming connections 18 | 19 | public AbstractSocketProgressParser(ProgressListener listener) { 20 | this.parser = new StreamProgressParser(listener); 21 | } 22 | 23 | /** 24 | * Creates a URL to parse to FFmpeg based on the scheme, address and port. 25 | * 26 | *

TODO Move this method to somewhere better. 27 | * 28 | * @param scheme The scheme to use (e.g. "tcp", "udp", "rtp", "http") 29 | * @param address The address of the server 30 | * @param port The port to connect to 31 | * @return A URI representing the address and port 32 | * @throws URISyntaxException if the URI is invalid 33 | */ 34 | @CheckReturnValue 35 | static URI createUri(String scheme, InetAddress address, int port) throws URISyntaxException { 36 | checkNotNull(address); 37 | return new URI( 38 | scheme, 39 | null /* userInfo */, 40 | InetAddresses.toUriString(address), 41 | port, 42 | null /* path */, 43 | null /* query */, 44 | null /* fragment */); 45 | } 46 | 47 | @CheckReturnValue 48 | protected abstract String getThreadName(); 49 | 50 | protected abstract Runnable getRunnable(CountDownLatch startSignal); 51 | 52 | /** 53 | * Starts the ProgressParser waiting for progress. 54 | * 55 | * @exception IllegalThreadStateException if the parser was already started. 56 | */ 57 | @Override 58 | public synchronized void start() { 59 | if (thread != null) { 60 | throw new IllegalThreadStateException("Parser already started"); 61 | } 62 | 63 | String name = getThreadName() + "(" + getUri().toString() + ")"; 64 | 65 | CountDownLatch startSignal = new CountDownLatch(1); 66 | Runnable runnable = getRunnable(startSignal); 67 | 68 | thread = new Thread(runnable, name); 69 | thread.start(); 70 | 71 | // Block until the thread has started 72 | try { 73 | startSignal.await(); 74 | } catch (InterruptedException e) { 75 | Thread.currentThread().interrupt(); 76 | } 77 | } 78 | 79 | @Override 80 | public void stop() throws IOException { 81 | if (thread != null) { 82 | thread.interrupt(); // This unblocks processStream(); 83 | 84 | try { 85 | thread.join(); 86 | } catch (InterruptedException e) { 87 | Thread.currentThread().interrupt(); 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void close() throws IOException { 94 | stop(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/ProgressListener.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | /** Captures output from the ffmpeg command line as status occurs. */ 4 | // TODO Consider adding other stats. Start, end, stream, error 5 | public interface ProgressListener { 6 | // Called every time there is status, typically once a second, and at the end. 7 | // The Progress.status will normally be CONTINUE, until after the last frame, when it will be END. 8 | void progress(Progress progress); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/ProgressParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.net.URI; 6 | 7 | /** Parses the FFmpeg progress fields */ 8 | public interface ProgressParser extends Closeable { 9 | 10 | void start() throws IOException; 11 | 12 | void stop() throws IOException; 13 | 14 | /** 15 | * The URL to parse to FFmpeg to communicate with this parser 16 | * 17 | * @return The URI to communicate with FFmpeg. 18 | */ 19 | URI getUri(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/StreamProgressParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import com.google.common.base.Charsets; 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.Reader; 11 | 12 | public class StreamProgressParser { 13 | 14 | final ProgressListener listener; 15 | 16 | public StreamProgressParser(ProgressListener listener) { 17 | this.listener = checkNotNull(listener); 18 | } 19 | 20 | private static BufferedReader wrapInBufferedReader(Reader reader) { 21 | checkNotNull(reader); 22 | 23 | if (reader instanceof BufferedReader) { 24 | return (BufferedReader) reader; 25 | } 26 | 27 | return new BufferedReader(reader); 28 | } 29 | 30 | public void processStream(InputStream stream) throws IOException { 31 | checkNotNull(stream); 32 | processReader(new InputStreamReader(stream, Charsets.UTF_8)); 33 | } 34 | 35 | public void processReader(Reader reader) throws IOException { 36 | final BufferedReader in = wrapInBufferedReader(reader); 37 | 38 | String line; 39 | Progress p = new Progress(); 40 | while ((line = in.readLine()) != null) { 41 | if (p.parseLine(line)) { 42 | listener.progress(p); 43 | p = new Progress(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/TcpProgressParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.ServerSocket; 6 | import java.net.URI; 7 | import java.net.URISyntaxException; 8 | import java.util.concurrent.CountDownLatch; 9 | 10 | public class TcpProgressParser extends AbstractSocketProgressParser { 11 | 12 | final ServerSocket server; 13 | final URI address; 14 | 15 | public TcpProgressParser(ProgressListener listener) throws IOException, URISyntaxException { 16 | this(listener, 0, InetAddress.getLoopbackAddress()); 17 | } 18 | 19 | public TcpProgressParser(ProgressListener listener, int port, InetAddress addr) 20 | throws IOException, URISyntaxException { 21 | super(listener); 22 | this.server = new ServerSocket(port, 0, addr); 23 | this.address = createUri("tcp", server.getInetAddress(), server.getLocalPort()); 24 | } 25 | 26 | @Override 27 | public synchronized void stop() throws IOException { 28 | if (server.isClosed()) { 29 | // Allow double stop, and ignore 30 | return; 31 | } 32 | 33 | server.close(); // This unblocks server.accept(); 34 | super.stop(); 35 | } 36 | 37 | @Override 38 | protected String getThreadName() { 39 | return "TcpProgressParser"; 40 | } 41 | 42 | @Override 43 | protected Runnable getRunnable(CountDownLatch startSignal) { 44 | return new TcpProgressParserRunnable(parser, server, startSignal); 45 | } 46 | 47 | @Override 48 | public URI getUri() { 49 | return address; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/TcpProgressParserRunnable.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | import java.net.SocketException; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | class TcpProgressParserRunnable implements Runnable { 13 | 14 | final StreamProgressParser parser; 15 | final ServerSocket server; 16 | final CountDownLatch startSignal; 17 | 18 | public TcpProgressParserRunnable( 19 | StreamProgressParser parser, ServerSocket server, CountDownLatch startSignal) { 20 | this.parser = checkNotNull(parser); 21 | this.server = checkNotNull(server); 22 | this.startSignal = checkNotNull(startSignal); 23 | } 24 | 25 | @Override 26 | public void run() { 27 | while (!server.isClosed() && !Thread.currentThread().isInterrupted()) { 28 | try { 29 | // There is a subtle race condition, where ffmpeg can start up, and close before this thread 30 | // is scheduled. This happens more often on Travis than a unloaded system. 31 | startSignal.countDown(); 32 | 33 | try (Socket socket = server.accept()) { 34 | try (InputStream stream = socket.getInputStream()) { 35 | parser.processStream(stream); 36 | } 37 | } 38 | 39 | } catch (SocketException e) { 40 | // Most likley a Socket closed exception, which we can safely ignore 41 | 42 | } catch (IOException e) { 43 | // We have no good way to report this back to the user... yet 44 | // TODO Report to the user that this failed in some way 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/UdpProgressParser.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.io.IOException; 6 | import java.net.DatagramSocket; 7 | import java.net.InetAddress; 8 | import java.net.SocketException; 9 | import java.net.URI; 10 | import java.net.URISyntaxException; 11 | import java.util.concurrent.CountDownLatch; 12 | 13 | public class UdpProgressParser extends AbstractSocketProgressParser { 14 | 15 | final DatagramSocket socket; 16 | final URI address; 17 | 18 | public UdpProgressParser(ProgressListener listener) throws SocketException, URISyntaxException { 19 | this(listener, 0, InetAddress.getLoopbackAddress()); 20 | } 21 | 22 | public UdpProgressParser(ProgressListener listener, int port, InetAddress addr) 23 | throws SocketException, URISyntaxException { 24 | 25 | super(listener); 26 | 27 | this.socket = new DatagramSocket(port, checkNotNull(addr)); 28 | this.address = createUri("udp", socket.getLocalAddress(), socket.getLocalPort()); 29 | 30 | this.socket.setBroadcast(false); 31 | // this.socket.setSoTimeout(); // TODO Setup timeouts 32 | } 33 | 34 | @Override 35 | public synchronized void stop() throws IOException { 36 | if (socket.isClosed()) { 37 | // Allow double stop, and ignore 38 | return; 39 | } 40 | 41 | socket.close(); 42 | super.stop(); 43 | } 44 | 45 | @Override 46 | protected String getThreadName() { 47 | return "UdpProgressParser"; 48 | } 49 | 50 | @Override 51 | protected Runnable getRunnable(CountDownLatch startSignal) { 52 | return new UdpProgressParserRunnable(parser, socket, startSignal); 53 | } 54 | 55 | @Override 56 | public URI getUri() { 57 | return address; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/progress/UdpProgressParserRunnable.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.IOException; 7 | import java.net.DatagramPacket; 8 | import java.net.DatagramSocket; 9 | import java.net.SocketException; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | class UdpProgressParserRunnable implements Runnable { 13 | 14 | static final int MAX_PACKET_SIZE = 1500; 15 | 16 | final StreamProgressParser parser; 17 | final DatagramSocket socket; 18 | final CountDownLatch startSignal; 19 | 20 | public UdpProgressParserRunnable( 21 | StreamProgressParser parser, DatagramSocket socket, CountDownLatch startSignal) { 22 | this.parser = checkNotNull(parser); 23 | this.socket = checkNotNull(socket); 24 | this.startSignal = checkNotNull(startSignal); 25 | } 26 | 27 | @Override 28 | public void run() { 29 | final byte[] buf = new byte[MAX_PACKET_SIZE]; 30 | final DatagramPacket packet = new DatagramPacket(buf, buf.length); 31 | 32 | while (!socket.isClosed() && !Thread.currentThread().isInterrupted()) { 33 | startSignal.countDown(); 34 | 35 | try { 36 | // TODO This doesn't handle the case of a progress being split across two packets 37 | socket.receive(packet); 38 | 39 | if (packet.getLength() == 0) { 40 | continue; 41 | } 42 | 43 | final ByteArrayInputStream in = 44 | new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()); 45 | parser.processStream(in); 46 | 47 | } catch (SocketException e) { 48 | // Most likley a Socket closed exception, which we can safely ignore 49 | 50 | } catch (IOException e) { 51 | // We have no good way to report this back to the user... yet 52 | // TODO Report to the user that this failed in some way 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/bramp/ffmpeg/shared/CodecType.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.shared; 2 | 3 | public enum CodecType { 4 | VIDEO, 5 | AUDIO, 6 | SUBTITLE, 7 | DATA, 8 | ATTACHMENT 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/commons/lang3/math/gson/FractionAdapterTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.commons.lang3.math.gson; 2 | 3 | import static org.hamcrest.CoreMatchers.equalTo; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | 6 | import com.google.common.collect.ImmutableList; 7 | import com.google.gson.Gson; 8 | import com.google.gson.GsonBuilder; 9 | import java.util.List; 10 | import org.apache.commons.lang3.math.Fraction; 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | 14 | public class FractionAdapterTest { 15 | static Gson gson; 16 | 17 | @BeforeClass 18 | public static void setupGson() { 19 | GsonBuilder builder = new GsonBuilder(); 20 | builder.registerTypeAdapter(Fraction.class, new FractionAdapter()); 21 | gson = builder.create(); 22 | } 23 | 24 | private static class TestData { 25 | final String s; 26 | final Fraction f; 27 | 28 | public TestData(String s, Fraction f) { 29 | this.s = s; 30 | this.f = f; 31 | } 32 | } 33 | 34 | static final List readTests = 35 | ImmutableList.of( 36 | new TestData("null", null), 37 | new TestData("1", Fraction.getFraction(1, 1)), 38 | new TestData("1.0", Fraction.getFraction(1, 1)), 39 | new TestData("2", Fraction.getFraction(2, 1)), 40 | new TestData("0.5", Fraction.getFraction(1, 2)), 41 | new TestData("\"1\"", Fraction.getFraction(1, 1)), 42 | new TestData("\"1.0\"", Fraction.getFraction(1, 1)), 43 | new TestData("\"2\"", Fraction.getFraction(2, 1)), 44 | new TestData("\"0.5\"", Fraction.getFraction(1, 2)), 45 | new TestData("\"1/2\"", Fraction.getFraction(1, 2)), 46 | new TestData("\"1 1/2\"", Fraction.getFraction(1, 1, 2))); 47 | 48 | // Divide by zero 49 | static final List zerosTests = 50 | ImmutableList.of( 51 | new TestData("\"0/0\"", Fraction.ZERO), new TestData("\"1/0\"", Fraction.ZERO)); 52 | 53 | static final List writeTests = 54 | ImmutableList.of( 55 | new TestData("0", Fraction.ZERO), 56 | new TestData("1", Fraction.getFraction(1, 1)), 57 | new TestData("2", Fraction.getFraction(2, 1)), 58 | new TestData("1/2", Fraction.getFraction(1, 2)), 59 | new TestData("1 1/2", Fraction.getFraction(1, 1, 2))); 60 | 61 | @Test 62 | public void testRead() { 63 | for (TestData test : readTests) { 64 | Fraction f = gson.fromJson(test.s, Fraction.class); 65 | assertThat(f, equalTo(test.f)); 66 | } 67 | } 68 | 69 | @Test 70 | public void testZerosRead() { 71 | for (TestData test : zerosTests) { 72 | Fraction f = gson.fromJson(test.s, Fraction.class); 73 | assertThat(f, equalTo(test.f)); 74 | } 75 | } 76 | 77 | @Test 78 | public void testWrites() { 79 | for (TestData test : writeTests) { 80 | String json = gson.toJson(test.f); 81 | assertThat(json, equalTo('"' + test.s + '"')); 82 | } 83 | } 84 | 85 | @Test 86 | public void testWriteNull() { 87 | String json = gson.toJson(null); 88 | assertThat(json, equalTo("null")); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/FFmpegAvTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static net.bramp.ffmpeg.FFmpegTest.argThatHasItem; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.mockito.Mockito.when; 6 | 7 | import java.io.IOException; 8 | import java.util.Collections; 9 | import net.bramp.ffmpeg.lang.NewProcessAnswer; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.mockito.Mock; 14 | import org.mockito.junit.MockitoJUnitRunner; 15 | 16 | /** Tests what happens when using avconv */ 17 | @RunWith(MockitoJUnitRunner.class) 18 | public class FFmpegAvTest { 19 | 20 | @Mock ProcessFunction runFunc; 21 | 22 | FFmpeg ffmpeg; 23 | 24 | @Before 25 | public void before() throws IOException { 26 | when(runFunc.run(argThatHasItem("-version"))) 27 | .thenAnswer(new NewProcessAnswer("avconv-version")); 28 | 29 | ffmpeg = new FFmpeg(runFunc); 30 | } 31 | 32 | @Test 33 | public void testVersion() throws Exception { 34 | assertEquals( 35 | "avconv version 11.4, Copyright (c) 2000-2014 the Libav developers", ffmpeg.version()); 36 | assertEquals( 37 | "avconv version 11.4, Copyright (c) 2000-2014 the Libav developers", ffmpeg.version()); 38 | } 39 | 40 | /** 41 | * We don't support avconv, so all methods should throw an exception. 42 | */ 43 | @Test(expected = IllegalArgumentException.class) 44 | public void testProbeVideo() throws IOException { 45 | ffmpeg.run(Collections.emptyList()); 46 | } 47 | 48 | @Test(expected = IllegalArgumentException.class) 49 | public void testCodecs() throws IOException { 50 | ffmpeg.codecs(); 51 | } 52 | 53 | @Test(expected = IllegalArgumentException.class) 54 | public void testFormats() throws IOException { 55 | ffmpeg.formats(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static net.bramp.ffmpeg.FFmpegUtils.*; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import org.junit.Test; 8 | 9 | public class FFmpegUtilsTest { 10 | 11 | @Test(expected = AssertionError.class) 12 | public void testAbstractUtilsClass() { 13 | new FFmpegUtils(); 14 | } 15 | 16 | @Test 17 | @SuppressWarnings({"deprecation", "InlineMeInliner"}) 18 | public void testMillisecondsToString() { 19 | assertEquals("00:01:03.123", millisecondsToString(63123)); 20 | assertEquals("00:01:03", millisecondsToString(63000)); 21 | assertEquals("01:23:45.678", millisecondsToString(5025678)); 22 | assertEquals("00:00:00", millisecondsToString(0)); 23 | assertEquals("00:00:00.001", millisecondsToString(1)); 24 | } 25 | 26 | @Test(expected = IllegalArgumentException.class) 27 | @SuppressWarnings({"deprecation", "InlineMeInliner"}) 28 | public void testMillisecondsToStringNegative() { 29 | millisecondsToString(-1); 30 | } 31 | 32 | @Test(expected = IllegalArgumentException.class) 33 | @SuppressWarnings({"deprecation", "InlineMeInliner"}) 34 | public void testMillisecondsToStringNegativeMinValue() { 35 | millisecondsToString(Long.MIN_VALUE); 36 | } 37 | 38 | @Test 39 | public void testToTimecode() { 40 | assertEquals("00:00:00", toTimecode(0, TimeUnit.NANOSECONDS)); 41 | assertEquals("00:00:00.000000001", toTimecode(1, TimeUnit.NANOSECONDS)); 42 | assertEquals("00:00:00.000001", toTimecode(1, TimeUnit.MICROSECONDS)); 43 | assertEquals("00:00:00.001", toTimecode(1, TimeUnit.MILLISECONDS)); 44 | assertEquals("00:00:01", toTimecode(1, TimeUnit.SECONDS)); 45 | assertEquals("00:01:00", toTimecode(1, TimeUnit.MINUTES)); 46 | assertEquals("01:00:00", toTimecode(1, TimeUnit.HOURS)); 47 | } 48 | 49 | @Test 50 | public void testFromTimecode() { 51 | assertEquals(63123000000L, fromTimecode("00:01:03.123")); 52 | assertEquals(63000000000L, fromTimecode("00:01:03")); 53 | assertEquals(5025678000000L, fromTimecode("01:23:45.678")); 54 | assertEquals(0, fromTimecode("00:00:00")); 55 | } 56 | 57 | @Test 58 | public void testParseBitrate() { 59 | assertEquals(12300, parseBitrate("12.3kbits/s")); 60 | assertEquals(1000, parseBitrate("1kbits/s")); 61 | assertEquals(123, parseBitrate("0.123kbits/s")); 62 | assertEquals(-1, parseBitrate("N/A")); 63 | } 64 | 65 | @Test(expected = IllegalArgumentException.class) 66 | public void testParseBitrateInvalidEmpty() { 67 | parseBitrate(""); 68 | } 69 | 70 | @Test(expected = IllegalArgumentException.class) 71 | public void testParseBitrateInvalidNumber() { 72 | parseBitrate("12.3"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/FFprobeAvTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static net.bramp.ffmpeg.FFmpegTest.argThatHasItem; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.mockito.Mockito.when; 6 | 7 | import com.google.gson.Gson; 8 | import java.io.IOException; 9 | import net.bramp.ffmpeg.fixtures.Samples; 10 | import net.bramp.ffmpeg.lang.NewProcessAnswer; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.mockito.Mock; 15 | import org.mockito.junit.MockitoJUnitRunner; 16 | 17 | /** Tests what happens when using avprobe */ 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class FFprobeAvTest { 20 | 21 | @Mock ProcessFunction runFunc; 22 | 23 | FFprobe ffprobe; 24 | 25 | static final Gson gson = FFmpegUtils.getGson(); 26 | 27 | @Before 28 | public void before() throws IOException { 29 | when(runFunc.run(argThatHasItem("-version"))) 30 | .thenAnswer(new NewProcessAnswer("avprobe-version")); 31 | 32 | ffprobe = new FFprobe(runFunc); 33 | } 34 | 35 | @Test 36 | public void testVersion() throws Exception { 37 | assertEquals( 38 | "avprobe version 11.4, Copyright (c) 2007-2014 the Libav developers", ffprobe.version()); 39 | assertEquals( 40 | "avprobe version 11.4, Copyright (c) 2007-2014 the Libav developers", ffprobe.version()); 41 | } 42 | 43 | @Test(expected = IllegalArgumentException.class) 44 | public void testProbeVideo() throws IOException { 45 | ffprobe.probe(Samples.big_buck_bunny_720p_1mb); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/Helper.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import static com.google.common.base.Preconditions.checkNotNull; 4 | import static com.google.common.collect.Iterators.asEnumeration; 5 | 6 | import com.google.common.base.Function; 7 | import com.google.common.collect.Iterables; 8 | import java.io.InputStream; 9 | import java.io.SequenceInputStream; 10 | import java.util.List; 11 | import javax.annotation.Nullable; 12 | 13 | /** Random test helper methods. */ 14 | public class Helper { 15 | 16 | static final Function resourceLoader = 17 | new Function() { 18 | @Nullable 19 | @Override 20 | public InputStream apply(@Nullable String input) { 21 | return loadResource(input); 22 | } 23 | }; 24 | 25 | /** 26 | * Simple wrapper around "new SequenceInputStream", so the user doesn't have to deal with the 27 | * horribly dated Enumeration type. 28 | */ 29 | public static InputStream sequenceInputStream(Iterable input) { 30 | checkNotNull(input); 31 | return new SequenceInputStream(asEnumeration(input.iterator())); 32 | } 33 | 34 | public static InputStream loadResource(String name) { 35 | checkNotNull(name); 36 | return FFmpegTest.class.getResourceAsStream("fixtures/" + name); 37 | } 38 | 39 | /** 40 | * Loads all resources, and returns one stream containing them all. 41 | */ 42 | public static InputStream combineResource(List names) { 43 | checkNotNull(names); 44 | return sequenceInputStream(Iterables.transform(names, resourceLoader)); 45 | } 46 | 47 | public static List subList(List input, int start) { 48 | checkNotNull(input); 49 | 50 | return input.subList(start, input.size()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/PreconditionsCheckInvalidNotEmptyTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | @RunWith(Parameterized.class) 10 | public class PreconditionsCheckInvalidNotEmptyTest { 11 | @Parameterized.Parameters(name = "{0}") 12 | public static List data() { 13 | return Arrays.asList(null, "", " ", "\n", " \n "); 14 | } 15 | 16 | private final String input; 17 | 18 | public PreconditionsCheckInvalidNotEmptyTest(String input) { 19 | this.input = input; 20 | } 21 | 22 | @Test(expected = IllegalArgumentException.class) 23 | public void testUri() { 24 | Preconditions.checkNotEmpty(input, "test must throw exception"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/PreconditionsCheckInvalidStreamTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import java.net.URI; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.Parameterized; 9 | import org.junit.runners.Parameterized.Parameters; 10 | 11 | @RunWith(Parameterized.class) 12 | public class PreconditionsCheckInvalidStreamTest { 13 | 14 | @Parameters(name = "{0}") 15 | public static List data() { 16 | return Arrays.asList( 17 | // Illegal schemes 18 | "http://www.example.com/", 19 | "https://live.twitch.tv/app/live_", 20 | "ftp://236.0.0.1:2000", 21 | 22 | // Missing ports 23 | "udp://10.1.0.102/", 24 | "tcp://127.0.0.1/"); 25 | } 26 | 27 | private final URI uri; 28 | 29 | public PreconditionsCheckInvalidStreamTest(String url) { 30 | this.uri = URI.create(url); 31 | } 32 | 33 | @Test(expected = IllegalArgumentException.class) 34 | public void testUri() { 35 | Preconditions.checkValidStream(uri); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/PreconditionsCheckValidNotEmptyTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | @RunWith(Parameterized.class) 10 | public class PreconditionsCheckValidNotEmptyTest { 11 | @Parameterized.Parameters(name = "{0}") 12 | public static List data() { 13 | return Arrays.asList("bob", " hello "); 14 | } 15 | 16 | private final String input; 17 | 18 | public PreconditionsCheckValidNotEmptyTest(String input) { 19 | this.input = input; 20 | } 21 | 22 | @Test 23 | public void testUri() { 24 | Preconditions.checkNotEmpty(input, "test must not throw exception"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/PreconditionsCheckValidStreamTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg; 2 | 3 | import java.net.URI; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.Parameterized; 9 | import org.junit.runners.Parameterized.Parameters; 10 | 11 | @RunWith(Parameterized.class) 12 | public class PreconditionsCheckValidStreamTest { 13 | 14 | @Parameters(name = "{0}") 15 | public static List data() { 16 | return Arrays.asList( 17 | "udp://10.1.0.102:1234", 18 | "tcp://127.0.0.1:2000", 19 | "udp://236.0.0.1:2000", 20 | "rtmp://live.twitch.tv/app/live_", 21 | "rtmp:///live/myStream.sdp", 22 | "rtp://127.0.0.1:1234", 23 | "rtsp://localhost:8888/live.sdp", 24 | "rtsp://localhost:8888/live.sdp?tcp", 25 | 26 | // Some others 27 | "UDP://10.1.0.102:1234"); 28 | } 29 | 30 | private final URI uri; 31 | 32 | public PreconditionsCheckValidStreamTest(String url) { 33 | this.uri = URI.create(url); 34 | } 35 | 36 | @Test 37 | public void testUri() { 38 | Preconditions.checkValidStream(uri); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/adapter/BooleanTypeAdapterTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.adapter; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.annotations.JsonAdapter; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertTrue; 10 | import static org.junit.Assert.assertFalse; 11 | 12 | public class BooleanTypeAdapterTest { 13 | static class Set { 14 | @JsonAdapter(BooleanTypeAdapter.class) 15 | public boolean a; 16 | } 17 | 18 | static Gson gson; 19 | 20 | @BeforeClass 21 | public static void setupGson() { 22 | gson = new GsonBuilder().create(); 23 | } 24 | 25 | @Test 26 | public void testReadTrue() { 27 | Set s = gson.fromJson("{\"a\":true}", Set.class); 28 | assertTrue(s.a); 29 | } 30 | 31 | @Test 32 | public void testReadFalse() { 33 | Set s = gson.fromJson("{\"a\":false}", Set.class); 34 | assertFalse(s.a); 35 | } 36 | 37 | @Test 38 | public void testReadStringTrue() { 39 | Set s = gson.fromJson("{\"a\":\"true\"}", Set.class); 40 | assertTrue(s.a); 41 | } 42 | 43 | @Test 44 | public void testReadStringFalse() { 45 | Set s = gson.fromJson("{\"a\":\"false\"}", Set.class); 46 | assertFalse(s.a); 47 | } 48 | 49 | @Test 50 | public void testRead1() { 51 | Set s = gson.fromJson("{\"a\":1}", Set.class); 52 | assertTrue(s.a); 53 | } 54 | 55 | @Test 56 | public void testRead0() { 57 | Set s = gson.fromJson("{\"a\":0}", Set.class); 58 | assertFalse(s.a); 59 | } 60 | 61 | @Test 62 | public void testReadNull() { 63 | Set s = gson.fromJson("{\"a\":null}", Set.class); 64 | assertFalse(s.a); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/adapter/FFmpegStreamSideDataAdapterTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.adapter; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.bramp.ffmpeg.probe.FFmpegStream; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class FFmpegStreamSideDataAdapterTest { 12 | static Gson gson; 13 | @BeforeClass 14 | public static void setGson() { 15 | GsonBuilder builder = new GsonBuilder(); 16 | builder.registerTypeAdapter(FFmpegStream.SideData.class, new FFmpegStreamSideDataAdapter()); 17 | gson = builder.create(); 18 | } 19 | 20 | @Test 21 | public void testNumberFormatException() { 22 | FFmpegStream.SideData sideData = gson.fromJson("{\"side_data_type\": \"Display Matrix\", \"displaymatrix\": \"\n00000000: 0 0 0\n00000001: 0 0 0\n00000002: 0 0 1073741824\n\", \"rotation\": -9223372036854775808}", FFmpegStream.SideData.class); 23 | assertEquals(sideData.side_data_type, "Display Matrix"); 24 | assertEquals(sideData.displaymatrix, "\n00000000: 0 0 0\n00000001: 0 0 0\n00000002: 0 0 1073741824\n"); 25 | assertEquals(sideData.rotation, 0); 26 | } 27 | 28 | @Test 29 | public void testNormalVideo() { 30 | FFmpegStream.SideData sideData = gson.fromJson("{\"side_data_type\": \"Display Matrix\", \"displaymatrix\": \"\n00000000: 0 -65536 0\n00000001: 65536 0 0\n00000002: 0 0 1073741824\n\", \"rotation\": 90}", FFmpegStream.SideData.class); 31 | assertEquals(sideData.side_data_type, "Display Matrix"); 32 | assertEquals(sideData.displaymatrix, "\n00000000: 0 -65536 0\n00000001: 65536 0 0\n00000002: 0 0 1073741824\n"); 33 | assertEquals(sideData.rotation, 90); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/AbstractFFmpegInputBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | import static org.hamcrest.core.Is.is; 10 | 11 | public abstract class AbstractFFmpegInputBuilderTest extends AbstractFFmpegStreamBuilderTest { 12 | @Override 13 | protected abstract AbstractFFmpegInputBuilder getBuilder(); 14 | 15 | @Test 16 | public void testReadAtNativeFrameRate() { 17 | List command = getBuilder() 18 | .readAtNativeFrameRate() 19 | .build(0); 20 | 21 | assertThat(removeCommon(command), is(ImmutableList.of("-re"))); 22 | } 23 | 24 | @Test 25 | public void testSetStreamLoopInfinit() { 26 | List command = getBuilder() 27 | .setStreamLoop(-1) 28 | .build(0); 29 | 30 | assertThat(removeCommon(command), is(ImmutableList.of("-stream_loop", "-1"))); 31 | } 32 | 33 | @Test 34 | public void testSetStreamLoopCounter() { 35 | List command = getBuilder() 36 | .setStreamLoop(2) 37 | .build(0); 38 | 39 | assertThat(removeCommon(command), is(ImmutableList.of("-stream_loop", "2"))); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/AbstractFFmpegOutputBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | 8 | import static org.hamcrest.core.Is.is; 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | 11 | public abstract class AbstractFFmpegOutputBuilderTest extends AbstractFFmpegStreamBuilderTest { 12 | 13 | @Override 14 | protected abstract AbstractFFmpegOutputBuilder getBuilder(); 15 | 16 | @Test 17 | public void testConstantRateFactor() { 18 | List command = getBuilder() 19 | .setConstantRateFactor(5) 20 | .build(0); 21 | 22 | assertThat(removeCommon(command), is(ImmutableList.of("-crf", "5"))); 23 | } 24 | 25 | @Test 26 | public void testAudioSampleFormat() { 27 | List command = getBuilder() 28 | .setAudioSampleFormat("asf") 29 | .build(0); 30 | 31 | assertThat(removeCommon(command), is(ImmutableList.of("-sample_fmt", "asf"))); 32 | } 33 | 34 | @Test 35 | public void testAudioBitrate() { 36 | List command = getBuilder() 37 | .setAudioBitRate(1_000) 38 | .build(0); 39 | 40 | assertThat(removeCommon(command), is(ImmutableList.of("-b:a", "1000"))); 41 | } 42 | 43 | @Test 44 | public void testAudioQuality() { 45 | List command = getBuilder() 46 | .setAudioQuality(5) 47 | .build(0); 48 | 49 | assertThat(removeCommon(command), is(ImmutableList.of("-qscale:a", "5"))); 50 | } 51 | 52 | @Test 53 | public void testSetAudioBitStreamFilter() { 54 | List command = getBuilder() 55 | .setAudioBitStreamFilter("filter") 56 | .build(0); 57 | 58 | assertThat(removeCommon(command), is(ImmutableList.of("-bsf:a", "filter"))); 59 | } 60 | 61 | @Test 62 | public void testSetVideoBitRate() { 63 | List command = getBuilder() 64 | .setVideoBitRate(1_000_000) 65 | .build(0); 66 | 67 | assertThat(removeCommon(command), is(ImmutableList.of("-b:v", "1000000"))); 68 | } 69 | 70 | @Test 71 | public void testSetVideoQuality() { 72 | List command = getBuilder() 73 | .setVideoQuality(20) 74 | .build(0); 75 | 76 | assertThat(removeCommon(command), is(ImmutableList.of("-qscale:v", "20"))); 77 | } 78 | 79 | @Test 80 | public void testSetVideoPreset() { 81 | List command = getBuilder() 82 | .setVideoPreset("main") 83 | .build(0); 84 | 85 | assertThat(removeCommon(command), is(ImmutableList.of("-vpre", "main"))); 86 | } 87 | 88 | @Test 89 | public void testSetVideoFilter() { 90 | List command = getBuilder() 91 | .setVideoFilter("filter") 92 | .build(0); 93 | 94 | assertThat(removeCommon(command), is(ImmutableList.of("-vf", "filter"))); 95 | } 96 | 97 | @Test 98 | public void testSetVideoBitStreamFilter() { 99 | List command = getBuilder() 100 | .setVideoBitStreamFilter("bit-stream-filter") 101 | .build(0); 102 | 103 | assertThat(removeCommon(command), is(ImmutableList.of("-bsf:v", "bit-stream-filter"))); 104 | } 105 | 106 | @Test 107 | public void testSetComplexFilter() { 108 | List command = getBuilder() 109 | .setComplexFilter("complex-filter") 110 | .build(0); 111 | 112 | assertThat(removeCommon(command), is(ImmutableList.of("-filter_complex", "complex-filter"))); 113 | } 114 | 115 | @Test 116 | public void testSetBFrames() { 117 | List command = getBuilder() 118 | .setBFrames(2) 119 | .build(0); 120 | 121 | assertThat(removeCommon(command), is(ImmutableList.of("-bf", "2"))); 122 | } 123 | } -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/FFmpegBuilderTwoPassTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | 8 | import static net.bramp.ffmpeg.builder.AbstractFFmpegStreamBuilder.DEVNULL; 9 | import static org.hamcrest.core.Is.is; 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | 12 | public class FFmpegBuilderTwoPassTest { 13 | 14 | @Test 15 | public void firstPass() { 16 | List command = new FFmpegBuilder() 17 | .addInput("input.mp4") 18 | .done() 19 | .addOutput("output.mp4") 20 | .setVideoBitRate(1_000_000) 21 | .setFormat("mp4") 22 | .done() 23 | .setPass(1) 24 | .build(); 25 | 26 | assertThat(command, is(ImmutableList.of("-y", "-v", "error", "-an", "-i", "input.mp4", "-pass", "1", "-f", "mp4", "-b:v", "1000000", "-an", DEVNULL))); 27 | } 28 | 29 | @Test 30 | public void secondPass() { 31 | List command = new FFmpegBuilder() 32 | .addInput("input.mp4") 33 | .done() 34 | .addOutput("output.mp4") 35 | .setVideoBitRate(1_000_000) 36 | .setFormat("mp4") 37 | .done() 38 | .setPass(2) 39 | .build(); 40 | 41 | assertThat(command, is(ImmutableList.of("-y", "-v", "error", "-i", "input.mp4", "-pass", "2", "-f", "mp4", "-b:v", "1000000", "output.mp4"))); 42 | } 43 | 44 | @Test(expected = IllegalArgumentException.class) 45 | public void firstPassNoBitrate() { 46 | List ignored = new FFmpegBuilder() 47 | .addInput("input.mp4") 48 | .done() 49 | .addOutput("output.mp4") 50 | .setFormat("mp4") 51 | .done() 52 | .setPass(1) 53 | .build(); 54 | } 55 | 56 | @Test(expected = IllegalArgumentException.class) 57 | public void secondPassNoBitrate() { 58 | List ignored = new FFmpegBuilder() 59 | .addInput("input.mp4") 60 | .done() 61 | .addOutput("output.mp4") 62 | .setFormat("mp4") 63 | .done() 64 | .setPass(2) 65 | .build(); 66 | } 67 | 68 | @Test(expected = IllegalArgumentException.class) 69 | public void firstPassNoFormat() { 70 | List ignored = new FFmpegBuilder() 71 | .addInput("input.mp4") 72 | .done() 73 | .addOutput("output.mp4") 74 | .setVideoBitRate(1_000_000) 75 | .done() 76 | .setPass(1) 77 | .build(); 78 | } 79 | 80 | @Test(expected = IllegalArgumentException.class) 81 | public void secondPassNoFormat() { 82 | List ignored = new FFmpegBuilder() 83 | .addInput("input.mp4") 84 | .done() 85 | .addOutput("output.mp4") 86 | .setVideoBitRate(1_000_000) 87 | .done() 88 | .setPass(2) 89 | .build(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/FFmpegFileInputBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | 8 | import static org.hamcrest.core.Is.is; 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class FFmpegFileInputBuilderTest extends AbstractFFmpegInputBuilderTest { 13 | @Override 14 | protected AbstractFFmpegInputBuilder getBuilder() { 15 | return new FFmpegBuilder().addInput("input.mp4"); 16 | } 17 | 18 | @Override 19 | protected List removeCommon(List command) { 20 | assertEquals(command.get(command.size() - 1), "input.mp4"); 21 | assertEquals(command.get(command.size() - 2), "-i"); 22 | 23 | return command.subList(0, command.size() - 2); 24 | } 25 | 26 | @Test 27 | public void testFileName() { 28 | List command = new FFmpegBuilder().addInput("input.mp4").build(0); 29 | 30 | assertThat(command, is(ImmutableList.of("-i", "input.mp4"))); 31 | } 32 | } -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/FFmpegOutputBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import java.util.List; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | public class FFmpegOutputBuilderTest extends AbstractFFmpegOutputBuilderTest { 8 | 9 | @Override 10 | protected AbstractFFmpegOutputBuilder getBuilder() { 11 | return new FFmpegBuilder().addInput("input.mp4").done().addOutput("output.mp4"); 12 | } 13 | 14 | @Override 15 | protected List removeCommon(List command) { 16 | assertEquals("output.mp4", command.get(command.size() - 1)); 17 | 18 | return command.subList(0, command.size() - 1); 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/FFprobeBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import java.util.List; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertThrows; 10 | 11 | public class FFprobeBuilderTest { 12 | @Test 13 | public void testDefaultFFprobeConfiguration() { 14 | final List args = new FFprobeBuilder().setInput("input").build(); 15 | 16 | assertEquals( 17 | args, 18 | ImmutableList.of( 19 | "-v", 20 | "quiet", 21 | "-print_format", 22 | "json", 23 | "-show_error", 24 | "-show_format", 25 | "-show_streams", 26 | "-show_chapters", 27 | "input")); 28 | } 29 | 30 | @Test 31 | public void testPacketsAndFramesEnabled() { 32 | final List args = new FFprobeBuilder().setInput("input").setShowPackets(true).setShowFrames(true).build(); 33 | 34 | assertEquals( 35 | args, 36 | ImmutableList.of( 37 | "-v", 38 | "quiet", 39 | "-print_format", 40 | "json", 41 | "-show_error", 42 | "-show_format", 43 | "-show_streams", 44 | "-show_chapters", 45 | "-show_packets", 46 | "-show_frames", 47 | "input")); 48 | } 49 | 50 | @Test 51 | public void testDefaultOptionsDisabled() { 52 | final List args = new FFprobeBuilder() 53 | .setInput("input") 54 | .setShowChapters(false) 55 | .setShowStreams(false) 56 | .setShowFormat(false) 57 | .build(); 58 | 59 | assertEquals( 60 | args, 61 | ImmutableList.of( 62 | "-v", 63 | "quiet", 64 | "-print_format", 65 | "json", 66 | "-show_error", 67 | "input")); 68 | } 69 | 70 | @Test 71 | public void testSpecifyUserAgent() { 72 | final List args = new FFprobeBuilder() 73 | .setInput("input") 74 | .setShowChapters(false) 75 | .setShowStreams(false) 76 | .setShowFormat(false) 77 | .setUserAgent("user agent") 78 | .build(); 79 | 80 | assertEquals( 81 | args, 82 | ImmutableList.of( 83 | "-v", 84 | "quiet", 85 | "-print_format", 86 | "json", 87 | "-show_error", 88 | "-user_agent", 89 | "user agent", 90 | "input")); 91 | } 92 | 93 | @Test 94 | public void throwsExceptionIfInputIsNull() 95 | { 96 | final FFprobeBuilder builder = new FFprobeBuilder(); 97 | assertThrows(NullPointerException.class, () -> builder.setInput(null)); 98 | } 99 | 100 | @Test 101 | public void throwsExceptionIfNoInputIsGiven() 102 | { 103 | final FFprobeBuilder builder = new FFprobeBuilder(); 104 | assertThrows(NullPointerException.class, builder::build); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/FormatDecimalIntegerTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.core.IsEqual.equalTo; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.junit.runners.Parameterized; 11 | 12 | @RunWith(Parameterized.class) 13 | public class FormatDecimalIntegerTest { 14 | 15 | @Parameterized.Parameters(name = "{0}") 16 | public static List data() { 17 | return Arrays.asList( 18 | new Object[][] { 19 | {0.0, "0"}, 20 | {1.0, "1"}, 21 | {-1.0, "-1"}, 22 | {0.1, "0.1"}, 23 | {1.1, "1.1"}, 24 | {1.10, "1.1"}, 25 | {1.001, "1.001"}, 26 | {100, "100"}, 27 | {100.01, "100.01"}, 28 | }); 29 | } 30 | 31 | final double input; 32 | final String expected; 33 | 34 | public FormatDecimalIntegerTest(double input, String expected) { 35 | this.input = input; 36 | this.expected = expected; 37 | } 38 | 39 | @Test 40 | public void formatDecimalInteger() throws Exception { 41 | String got = FFmpegOutputBuilder.formatDecimalInteger(input); 42 | 43 | assertThat(got, equalTo(expected)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/MetadataSpecTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import static net.bramp.ffmpeg.builder.MetadataSpecifier.*; 4 | import static net.bramp.ffmpeg.builder.StreamSpecifier.id; 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.Matchers.is; 7 | 8 | import org.junit.Test; 9 | 10 | public class MetadataSpecTest { 11 | 12 | @Test 13 | public void testMetaSpec() { 14 | assertThat(global().spec(), is("g")); 15 | assertThat(chapter(1).spec(), is("c:1")); 16 | assertThat(program(1).spec(), is("p:1")); 17 | assertThat(stream(1).spec(), is("s:1")); 18 | assertThat(stream(id(1)).spec(), is("s:i:1")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/builder/StreamSpecTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.builder; 2 | 3 | import static net.bramp.ffmpeg.builder.StreamSpecifier.*; 4 | import static net.bramp.ffmpeg.builder.StreamSpecifierType.*; 5 | import static org.hamcrest.MatcherAssert.assertThat; 6 | import static org.hamcrest.core.Is.is; 7 | 8 | import org.junit.Test; 9 | 10 | public class StreamSpecTest { 11 | 12 | @Test 13 | public void testStreamSpec() { 14 | assertThat(stream(1).spec(), is("1")); 15 | assertThat(stream(Video).spec(), is("v")); 16 | 17 | assertThat(stream(Video, 1).spec(), is("v:1")); 18 | assertThat(stream(PureVideo, 1).spec(), is("V:1")); 19 | assertThat(stream(Audio, 1).spec(), is("a:1")); 20 | assertThat(stream(Subtitle, 1).spec(), is("s:1")); 21 | assertThat(stream(Data, 1).spec(), is("d:1")); 22 | assertThat(stream(Attachment, 1).spec(), is("t:1")); 23 | 24 | assertThat(program(1).spec(), is("p:1")); 25 | assertThat(program(1, 2).spec(), is("p:1:2")); 26 | 27 | assertThat(id(1).spec(), is("i:1")); 28 | 29 | assertThat(tag("key").spec(), is("m:key")); 30 | assertThat(tag("key", "value").spec(), is("m:key:value")); 31 | assertThat(usable().spec(), is("u")); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/fixtures/Progresses.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.fixtures; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.bramp.ffmpeg.progress.Progress; 5 | 6 | public final class Progresses { 7 | 8 | private Progresses() { 9 | throw new AssertionError("No instances for you!"); 10 | } 11 | 12 | public static final ImmutableList allFiles = 13 | ImmutableList.of( 14 | "ffmpeg-progress-0", 15 | "ffmpeg-progress-1", 16 | "ffmpeg-progress-2"); 17 | 18 | public static final ImmutableList naProgressFile = ImmutableList.of("ffmpeg-progress-na"); 19 | 20 | public static final ImmutableList allProgresses = 21 | ImmutableList.of( 22 | new Progress(5, 0.0f, 800, 48, 512000000, 0, 0, 1.01f, Progress.Status.CONTINUE), 23 | new Progress(118, 23.4f, -1, -1, 5034667000L, 0, 0, -1, Progress.Status.CONTINUE), 24 | new Progress(132, 23.1f, 1935500, 1285168, 5312000000L, 0, 0, 0.929f, Progress.Status.END)); 25 | 26 | public static final ImmutableList naProgresses = ImmutableList.of( 27 | new Progress(0, 0.0f, -1, -1, -1, 0, 0, -1, Progress.Status.END) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/fixtures/Samples.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.fixtures; 2 | 3 | import net.bramp.ffmpeg.builder.FFprobeBuilder; 4 | 5 | public final class Samples { 6 | private Samples() { 7 | throw new AssertionError("No instances for you!"); 8 | } 9 | 10 | // Test sample files (only a handful to keep the repo small) 11 | public static final String TEST_PREFIX = "src/test/resources/net/bramp/ffmpeg/samples/"; 12 | 13 | public static final String base_big_buck_bunny_720p_1mb = "big_buck_bunny_720p_1mb.mp4"; 14 | public static final String base_testscreen_jpg = "testscreen.jpg"; 15 | public static final String base_test_mp3 = "test.mp3"; 16 | 17 | public static final String big_buck_bunny_720p_1mb = TEST_PREFIX + base_big_buck_bunny_720p_1mb; 18 | public static final String testscreen_jpg = TEST_PREFIX + base_testscreen_jpg; 19 | public static final String test_mp3 = TEST_PREFIX + base_test_mp3; 20 | 21 | private static final String book_m4b = "book_with_chapters.m4b"; 22 | public static final String book_with_chapters = TEST_PREFIX + book_m4b; 23 | private static final String base_side_data_list = "side_data_list"; 24 | public static final String side_data_list = TEST_PREFIX + base_side_data_list; 25 | private static final String base_disposition_all_true = "disposition_all_true"; 26 | public static final String disposition_all_true = TEST_PREFIX + base_disposition_all_true; 27 | 28 | // We don't have the following files 29 | public static final String FAKE_PREFIX = "fake/"; 30 | 31 | public static final String always_on_my_mind = 32 | FAKE_PREFIX + "Always On My Mind [Program Only] - Adelen.mp4"; 33 | 34 | public static final String start_pts_test = FAKE_PREFIX + "start_pts_test_1mb.ts"; 35 | 36 | public static final String divide_by_zero = FAKE_PREFIX + "Divide By Zero.mp4"; 37 | public static final String big_buck_bunny_720p_1mb_with_packets = FAKE_PREFIX + "big_buck_bunny_720p_1mb_packets.mp4"; 38 | public static final String big_buck_bunny_720p_1mb_with_frames = FAKE_PREFIX + "big_buck_bunny_720p_1mb_frames.mp4"; 39 | public static final String big_buck_bunny_720p_1mb_with_packets_and_frames = FAKE_PREFIX + "big_buck_bunny_720p_1mb_packets_and_frames.mp4"; 40 | 41 | public static final String chapters_with_long_id = FAKE_PREFIX + "chapters_with_long_id.m4b"; 42 | 43 | // TODO Change to a temp directory 44 | // TODO Generate random names, so we can run tests concurrently 45 | public static final String output_mp4 = "output.mp4"; 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/info/CodecTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | 6 | import org.junit.Test; 7 | 8 | import net.bramp.ffmpeg.shared.CodecType; 9 | 10 | public class CodecTest { 11 | @Test 12 | public void testCodecConstructor() { 13 | Codec c1 = new Codec("012v", "Uncompressed 4:2:2 10-bit", "D.VI.S"); 14 | assertThat(c1.getName(), is("012v")); 15 | assertThat(c1.getLongName(), is("Uncompressed 4:2:2 10-bit")); 16 | assertThat(c1.getCanDecode(), is(true)); 17 | assertThat(c1.getCanEncode(), is(false)); 18 | assertThat(c1.getType(), is(CodecType.VIDEO)); 19 | assertThat(c1.isIntraFrameOnly(), is(true)); 20 | assertThat(c1.supportsLossyCompression(), is(false)); 21 | assertThat(c1.losslessCompression, is(true)); 22 | 23 | Codec c2 = new Codec("4xm", "4X Movie", "D.V.L."); 24 | assertThat(c2.getName(), is("4xm")); 25 | assertThat(c2.getLongName(), is("4X Movie")); 26 | assertThat(c2.getCanDecode(), is(true)); 27 | assertThat(c2.getCanEncode(), is(false)); 28 | assertThat(c2.getType(), is(CodecType.VIDEO)); 29 | assertThat(c2.isIntraFrameOnly(), is(false)); 30 | assertThat(c2.supportsLossyCompression(), is(true)); 31 | assertThat(c2.supportsLosslessCompression(), is(false)); 32 | 33 | Codec c3 = new Codec("alias_pix", "Alias/Wavefront PIX image", "DEVI.S"); 34 | assertThat(c3.getName(), is("alias_pix")); 35 | assertThat(c3.getLongName(), is("Alias/Wavefront PIX image")); 36 | assertThat(c3.getCanDecode(), is(true)); 37 | assertThat(c3.getCanEncode(), is(true)); 38 | assertThat(c3.getType(), is(CodecType.VIDEO)); 39 | assertThat(c3.isIntraFrameOnly(), is(true)); 40 | assertThat(c3.supportsLossyCompression(), is(false)); 41 | assertThat(c3.supportsLosslessCompression(), is(true)); 42 | 43 | Codec c4 = new Codec("binkaudio_rdft", "Bink Audio (RDFT)", "D.AIL."); 44 | assertThat(c4.getType(), is(CodecType.AUDIO)); 45 | 46 | Codec c6 = new Codec("mov_text", "MOV text", "DES..."); 47 | assertThat(c6.getType(), is(CodecType.SUBTITLE)); 48 | 49 | Codec c7 = new Codec("bin_data", "binary data", "..D..."); 50 | assertThat(c7.getType(), is(CodecType.DATA)); 51 | } 52 | 53 | @Test(expected = IllegalArgumentException.class) 54 | public void testBadDecodeValue() { 55 | new Codec("test", "test", "X.V..."); 56 | } 57 | 58 | @Test(expected = IllegalArgumentException.class) 59 | public void testBadEncodeValue() { 60 | new Codec("test", "test", ".XV..."); 61 | } 62 | 63 | @Test(expected = IllegalArgumentException.class) 64 | public void testBadCodecValue() { 65 | new Codec("test", "test", "..X..."); 66 | } 67 | 68 | @Test(expected = IllegalArgumentException.class) 69 | public void testBadIntraFrameOnlyValue() { 70 | new Codec("test", "test", "..VX.."); 71 | } 72 | 73 | @Test(expected = IllegalArgumentException.class) 74 | public void testBadLossyValue() { 75 | new Codec("test", "test", "..V.X."); 76 | } 77 | 78 | @Test(expected = IllegalArgumentException.class) 79 | public void testBadLosslessValue() { 80 | new Codec("test", "test", "..V..X"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/info/FFmpegGetInfoTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.info; 2 | 3 | import static net.bramp.ffmpeg.FFmpegTest.argThatHasItem; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.Matchers.*; 6 | import static org.mockito.Mockito.when; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import net.bramp.ffmpeg.FFmpeg; 12 | import net.bramp.ffmpeg.ProcessFunction; 13 | import net.bramp.ffmpeg.lang.NewProcessAnswer; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.mockito.Mock; 18 | import org.mockito.junit.MockitoJUnitRunner; 19 | 20 | @RunWith(MockitoJUnitRunner.class) 21 | public class FFmpegGetInfoTest { 22 | @Mock ProcessFunction runFunc; 23 | 24 | @Before 25 | public void before() throws IOException { 26 | when(runFunc.run(argThatHasItem("-version"))) 27 | .thenAnswer(new NewProcessAnswer("ffmpeg-version")); 28 | 29 | when(runFunc.run(argThatHasItem("-codecs"))).thenAnswer(new NewProcessAnswer("ffmpeg-codecs")); 30 | } 31 | 32 | @Test 33 | public void getFFmpegCodecSupportTest() throws IOException { 34 | List videoCodecs = new ArrayList<>(); 35 | List audioCodecs = new ArrayList<>(); 36 | List subtitleCodecs = new ArrayList<>(); 37 | List dataCodecs = new ArrayList<>(); 38 | List otherCodecs = new ArrayList<>(); 39 | 40 | FFmpeg ffmpeg = new FFmpeg("ffmpeg", runFunc); 41 | ffmpeg.codecs(); 42 | 43 | for (Codec codec : ffmpeg.codecs()) { 44 | switch (codec.getType()) { 45 | case VIDEO: 46 | videoCodecs.add(codec); 47 | break; 48 | case AUDIO: 49 | audioCodecs.add(codec); 50 | break; 51 | case SUBTITLE: 52 | subtitleCodecs.add(codec); 53 | break; 54 | case DATA: 55 | dataCodecs.add(codec); 56 | break; 57 | default: 58 | otherCodecs.add(codec); 59 | } 60 | } 61 | 62 | assertThat(videoCodecs, hasSize(245)); 63 | assertThat(audioCodecs, hasSize(180)); 64 | assertThat(subtitleCodecs, hasSize(26)); 65 | assertThat(dataCodecs, hasSize(8)); 66 | assertThat(otherCodecs, hasSize(0)); 67 | 68 | assertThat(videoCodecs, hasItem(hasProperty("name", equalTo("h264")))); 69 | assertThat(audioCodecs, hasItem(hasProperty("name", equalTo("aac")))); 70 | assertThat(subtitleCodecs, hasItem(hasProperty("name", equalTo("ssa")))); 71 | assertThat(dataCodecs, hasItem(hasProperty("name", equalTo("bin_data")))); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/io/HexOutputStream.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import com.google.common.base.Charsets; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.io.OutputStreamWriter; 7 | import java.io.Writer; 8 | 9 | /** Converts bytes into hex output */ 10 | public class HexOutputStream extends OutputStream { 11 | 12 | final Writer writer; 13 | int count = 0; 14 | 15 | /** 16 | * Creates an output stream filter built on top of the specified underlying output stream. 17 | * 18 | * @param out the underlying output stream to be assigned to the field this.out for later 19 | * use, or null if this instance is to be created without an underlying stream. 20 | */ 21 | public HexOutputStream(OutputStream out) { 22 | writer = new OutputStreamWriter(out, Charsets.UTF_8); 23 | } 24 | 25 | @Override 26 | public void write(int b) throws IOException { 27 | writer.write(String.format("%02X ", b & 0xFF)); 28 | count++; 29 | if (count > 16) { 30 | count = 0; 31 | writer.write("\n"); 32 | } 33 | } 34 | 35 | @Override 36 | public void write(byte[] b, int off, int len) throws IOException { 37 | for (int i = 0; i < len; i++) { 38 | write(b[off++]); 39 | } 40 | } 41 | 42 | @Override 43 | public void write(byte[] b) throws IOException { 44 | write(b, 0, b.length); 45 | } 46 | 47 | @Override 48 | public void flush() throws IOException { 49 | writer.flush(); 50 | } 51 | 52 | @Override 53 | public void close() throws IOException { 54 | writer.close(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/io/LoggerOutputStream.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.nio.charset.StandardCharsets; 7 | import org.slf4j.Logger; 8 | import org.slf4j.event.Level; 9 | 10 | public class LoggerOutputStream extends OutputStream { 11 | 12 | final Logger logger; 13 | final Level level; 14 | 15 | final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 16 | 17 | public LoggerOutputStream(Logger logger, Level level) { 18 | this.logger = logger; 19 | this.level = level; 20 | } 21 | 22 | @Override 23 | public void write(int b) throws IOException { 24 | buffer.write(b); 25 | if (b == '\n') { 26 | String line = buffer.toString(StandardCharsets.UTF_8.name()); 27 | switch (level) { 28 | case TRACE: 29 | logger.trace(line); 30 | break; 31 | case DEBUG: 32 | logger.debug(line); 33 | break; 34 | case INFO: 35 | logger.info(line); 36 | break; 37 | case WARN: 38 | logger.warn(line); 39 | break; 40 | case ERROR: 41 | logger.error(line); 42 | break; 43 | } 44 | buffer.reset(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/io/ProcessUtilsTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.io; 2 | 3 | import net.bramp.ffmpeg.FFmpeg; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.TimeoutException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertThrows; 12 | 13 | public class ProcessUtilsTest { 14 | @Test 15 | public void testProcessFinishesBeforeTimeout() throws Exception { 16 | String ffmpeg = new FFmpeg().getPath(); 17 | ProcessBuilder processBuilder = new ProcessBuilder(ffmpeg, "-y", "-v", "quiet", "-f", "lavfi", "-i", "testsrc=duration=1:size=1280x720:rate=10", "-c:v", "libx264", "-t", "1", "output.mp4"); 18 | Process process = processBuilder.start(); 19 | 20 | int exitValue = ProcessUtils.waitForWithTimeout(process, 5, TimeUnit.SECONDS); 21 | 22 | assertEquals(0, exitValue); 23 | } 24 | 25 | @Test 26 | public void testProcessDoesNotFinishBeforeTimeout() throws IOException { 27 | String ffmpeg = new FFmpeg().getPath(); 28 | ProcessBuilder processBuilder = new ProcessBuilder(ffmpeg, "-y", "-v", "quiet", "-f", "lavfi", "-i", "testsrc=duration=10:size=1280x720:rate=30", "-c:v", "libx264", "-t", "10", "output.mp4"); 29 | Process process = processBuilder.start(); 30 | 31 | assertThrows(TimeoutException.class, () -> { 32 | ProcessUtils.waitForWithTimeout(process, 1, TimeUnit.MILLISECONDS); 33 | }); 34 | 35 | process.destroy(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/lang/MockProcess.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.lang; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * A Mock Process, which exits with zero, and returns the provided streams. 8 | * 9 | * @author bramp 10 | */ 11 | class MockProcess extends Process { 12 | final OutputStream stdin; 13 | final InputStream stdout; 14 | final InputStream stderr; 15 | 16 | public MockProcess(InputStream stdout) { 17 | this.stdin = null; // TODO make this something 18 | this.stdout = stdout; 19 | this.stderr = null; // TODO make this return nothing. 20 | } 21 | 22 | public MockProcess(OutputStream stdin, InputStream stdout, InputStream stderr) { 23 | this.stdin = stdin; 24 | this.stdout = stdout; 25 | this.stderr = stderr; 26 | } 27 | 28 | @Override 29 | public OutputStream getOutputStream() { 30 | return stdin; 31 | } 32 | 33 | @Override 34 | public InputStream getInputStream() { 35 | return stdout; 36 | } 37 | 38 | @Override 39 | public InputStream getErrorStream() { 40 | return stderr; 41 | } 42 | 43 | @Override 44 | public int waitFor() throws InterruptedException { 45 | return 0; 46 | } 47 | 48 | @Override 49 | public int exitValue() { 50 | return 0; 51 | } 52 | 53 | @Override 54 | public void destroy() {} 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/lang/NewProcessAnswer.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.lang; 2 | 3 | import net.bramp.ffmpeg.Helper; 4 | import org.mockito.invocation.InvocationOnMock; 5 | import org.mockito.stubbing.Answer; 6 | 7 | public class NewProcessAnswer implements Answer { 8 | final String resource; 9 | 10 | final String errResource; 11 | 12 | public NewProcessAnswer(String resource) { 13 | this(resource, null); 14 | } 15 | 16 | public NewProcessAnswer(String resource, String errResource) { 17 | this.resource = resource; 18 | this.errResource = errResource; 19 | } 20 | 21 | @Override 22 | public Process answer(InvocationOnMock invocationOnMock) throws Throwable { 23 | return errResource == null 24 | ? new MockProcess(Helper.loadResource(resource)) 25 | : new MockProcess(null, Helper.loadResource(resource), Helper.loadResource(errResource)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/modelmapper/MapperTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.modelmapper; 2 | 3 | import net.bramp.ffmpeg.builder.FFmpegOutputBuilder; 4 | import net.bramp.ffmpeg.options.AudioEncodingOptions; 5 | import net.bramp.ffmpeg.options.EncodingOptions; 6 | import net.bramp.ffmpeg.options.MainEncodingOptions; 7 | import net.bramp.ffmpeg.options.VideoEncodingOptions; 8 | import org.junit.Test; 9 | 10 | public class MapperTest { 11 | 12 | @Test 13 | public void testMapping() { 14 | MainEncodingOptions main = new MainEncodingOptions("mp4", 0L, null); 15 | AudioEncodingOptions audio = new AudioEncodingOptions(false, null, 0, 0, null, 0, 0.0); 16 | VideoEncodingOptions video = 17 | new VideoEncodingOptions( 18 | true, null, null, 320, 240, 1000, null, "scale='320:trunc(ow/a/2)*2'", null); 19 | 20 | EncodingOptions options = new EncodingOptions(main, audio, video); 21 | 22 | FFmpegOutputBuilder mappedObj = new FFmpegOutputBuilder(); 23 | 24 | Mapper.map(options, mappedObj); 25 | 26 | // TODO Add actual test! 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/nut/NutReaderTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import java.awt.image.BufferedImage; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.TimeUnit; 12 | import javax.imageio.ImageIO; 13 | import javax.sound.sampled.AudioFormat; 14 | import javax.sound.sampled.AudioSystem; 15 | import javax.sound.sampled.LineUnavailableException; 16 | import javax.sound.sampled.SourceDataLine; 17 | import net.bramp.ffmpeg.FFmpeg; 18 | import net.bramp.ffmpeg.builder.FFmpegBuilder; 19 | import net.bramp.ffmpeg.fixtures.Samples; 20 | import org.junit.Rule; 21 | import org.junit.Test; 22 | import org.junit.rules.Timeout; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | // TODO fix "invalid packet checksum" when running test 27 | public class NutReaderTest { 28 | 29 | static final Logger LOG = LoggerFactory.getLogger(NutReaderTest.class); 30 | 31 | final boolean OUTPUT_AUDIO = false; 32 | final boolean OUTPUT_IMAGES = false; 33 | 34 | @Rule public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); 35 | 36 | @Test 37 | public void testNutReader() 38 | throws InterruptedException, ExecutionException, IOException, LineUnavailableException { 39 | 40 | List args = 41 | new FFmpegBuilder() 42 | .setInput(Samples.big_buck_bunny_720p_1mb) 43 | .done() 44 | .addStdoutOutput() 45 | .setFormat("nut") 46 | .setVideoCodec("rawvideo") 47 | // .setVideoPixelFormat("rgb24") // TODO make 24bit / channel work 48 | .setVideoPixelFormat("argb") // 8 bits per channel 49 | .setAudioCodec("pcm_s32le") 50 | .done() 51 | .build(); 52 | 53 | List newArgs = 54 | ImmutableList.builder().add(FFmpeg.DEFAULT_PATH).addAll(args).build(); 55 | 56 | ProcessBuilder builder = new ProcessBuilder(newArgs); 57 | Process p = builder.start(); 58 | 59 | new NutReader( 60 | p.getInputStream(), 61 | new NutReaderListener() { 62 | 63 | SourceDataLine line; 64 | 65 | @Override 66 | public void stream(Stream stream) { 67 | 68 | if (stream.header.type == StreamHeaderPacket.AUDIO) { 69 | 70 | if (!OUTPUT_AUDIO) { 71 | return; 72 | } 73 | 74 | if (line != null) { 75 | throw new RuntimeException("Multiple audio streams not supported"); 76 | } 77 | 78 | // Get System Audio Line 79 | try { 80 | line = AudioSystem.getSourceDataLine(null); 81 | 82 | AudioFormat format = RawHandler.streamToAudioFormat(stream.header); 83 | line.open(format); 84 | line.start(); 85 | 86 | LOG.debug("New audio stream: {}", format); 87 | 88 | } catch (LineUnavailableException e) { 89 | LOG.debug("Failed to open audio device", e); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public void frame(Frame frame) { 96 | LOG.debug("{}", frame); 97 | 98 | final StreamHeaderPacket header = frame.stream.header; 99 | 100 | if (header.type == StreamHeaderPacket.VIDEO) { 101 | BufferedImage img = RawHandler.toBufferedImage(frame); 102 | 103 | if (!OUTPUT_IMAGES) { 104 | return; 105 | } 106 | 107 | try { 108 | ImageIO.write(img, "png", new File(String.format("test-%08d.png", frame.pts))); 109 | } catch (IOException e) { 110 | LOG.error("Failed to write png", e); 111 | } 112 | 113 | } else if (header.type == StreamHeaderPacket.AUDIO) { 114 | if (line != null) { 115 | line.write(frame.data, 0, frame.data.length); 116 | } 117 | } 118 | } 119 | }) 120 | .read(); 121 | 122 | assertEquals(0, p.waitFor()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/nut/RawHandlerStreamToAudioFormatTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.nut; 2 | 3 | import static javax.sound.sampled.AudioFormat.Encoding.*; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.core.IsEqual.equalTo; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import javax.sound.sampled.AudioFormat; 11 | import org.apache.commons.lang3.math.Fraction; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.junit.runners.Parameterized; 15 | 16 | @RunWith(Parameterized.class) 17 | public class RawHandlerStreamToAudioFormatTest { 18 | 19 | @Parameterized.Parameters(name = "{4}") 20 | public static List data() { 21 | return Arrays.asList( 22 | new Object[][] { 23 | {"ALAW", 48000, 1, 2, new AudioFormat(ALAW, 48000, 8, 2, 2, 48000, false)}, 24 | {"ULAW", 48000, 1, 3, new AudioFormat(ULAW, 48000, 8, 3, 3, 48000, false)}, 25 | {"PSD\u0008", 48000, 1, 4, new AudioFormat(PCM_SIGNED, 48000, 8, 4, 4, 48000, false)}, 26 | {"\u0010DUP", 48000, 1, 6, new AudioFormat(PCM_UNSIGNED, 48000, 16, 6, 12, 48000, true)}, 27 | {"PFD ", 48000, 1, 8, new AudioFormat(PCM_FLOAT, 48000, 32, 8, 32, 48000, false)}, 28 | }); 29 | } 30 | 31 | final StreamHeaderPacket stream; 32 | final AudioFormat expected; 33 | 34 | public RawHandlerStreamToAudioFormatTest( 35 | String fourcc, int sampleRateNum, int sampleRateDenom, int channels, AudioFormat expected) { 36 | stream = new StreamHeaderPacket(); 37 | stream.type = StreamHeaderPacket.AUDIO; 38 | stream.fourcc = fourcc.getBytes(StandardCharsets.ISO_8859_1); 39 | stream.sampleRate = Fraction.getFraction(sampleRateNum, sampleRateDenom); 40 | stream.channels = channels; 41 | 42 | this.expected = expected; 43 | } 44 | 45 | @Test 46 | public void testStreamToAudioFormat() { 47 | AudioFormat format = RawHandler.streamToAudioFormat(stream); 48 | 49 | // Compare strings since AudioFormat does not have a good equalsCode(..) method. 50 | assertThat(format.toString(), equalTo(expected.toString())); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/progress/AbstractProgressParserTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | import java.net.URISyntaxException; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | import org.junit.Before; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.rules.Timeout; 16 | 17 | public abstract class AbstractProgressParserTest { 18 | 19 | @Rule public Timeout timeout = new Timeout(10, TimeUnit.SECONDS); 20 | 21 | final List progesses = Collections.synchronizedList(new ArrayList()); 22 | 23 | ProgressParser parser; 24 | URI uri; 25 | 26 | final ProgressListener listener = 27 | new ProgressListener() { 28 | @Override 29 | public void progress(Progress p) { 30 | progesses.add(p); 31 | } 32 | }; 33 | 34 | @Before 35 | public void setupParser() throws IOException, URISyntaxException { 36 | synchronized (progesses) { 37 | progesses.clear(); 38 | } 39 | 40 | parser = newParser(listener); 41 | uri = parser.getUri(); 42 | } 43 | 44 | public abstract ProgressParser newParser(ProgressListener listener) 45 | throws IOException, URISyntaxException; 46 | 47 | @Test 48 | public void testNoConnection() throws IOException, InterruptedException { 49 | parser.start(); 50 | parser.stop(); 51 | assertTrue(progesses.isEmpty()); 52 | } 53 | 54 | @Test 55 | public void testDoubleStop() throws IOException, InterruptedException { 56 | parser.start(); 57 | parser.stop(); 58 | parser.stop(); 59 | assertTrue(progesses.isEmpty()); 60 | } 61 | 62 | @Test(expected = IllegalThreadStateException.class) 63 | public void testDoubleStart() throws IOException { 64 | parser.start(); 65 | parser.start(); 66 | assertTrue(progesses.isEmpty()); 67 | } 68 | 69 | @Test() 70 | public void testStopNoStart() throws IOException { 71 | parser.stop(); 72 | assertTrue(progesses.isEmpty()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/progress/RecordingProgressListener.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import com.google.common.collect.Lists; 4 | import java.util.List; 5 | 6 | /** Test class to keep a record of all progresses. */ 7 | public class RecordingProgressListener implements ProgressListener { 8 | public final List progesses = Lists.newArrayList(); 9 | 10 | @Override 11 | public void progress(Progress p) { 12 | progesses.add(p); 13 | } 14 | 15 | public void reset() { 16 | progesses.clear(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/progress/StreamProgressParserTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static net.bramp.ffmpeg.Helper.combineResource; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.core.IsEqual.equalTo; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import net.bramp.ffmpeg.fixtures.Progresses; 10 | import org.junit.Test; 11 | 12 | public class StreamProgressParserTest { 13 | 14 | RecordingProgressListener listener = new RecordingProgressListener(); 15 | 16 | @Test 17 | public void testNormal() throws IOException { 18 | listener.reset(); 19 | 20 | StreamProgressParser parser = new StreamProgressParser(listener); 21 | 22 | InputStream inputStream = combineResource(Progresses.allFiles); 23 | parser.processStream(inputStream); 24 | 25 | assertThat(listener.progesses, equalTo(Progresses.allProgresses)); 26 | } 27 | 28 | @Test 29 | public void testNaProgressPackets() throws IOException { 30 | listener.reset(); 31 | 32 | StreamProgressParser parser = new StreamProgressParser(listener); 33 | 34 | InputStream inputStream = combineResource(Progresses.naProgressFile); 35 | parser.processStream(inputStream); 36 | 37 | assertThat(listener.progesses, equalTo(Progresses.naProgresses)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/progress/TcpProgressParserTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static net.bramp.ffmpeg.Helper.combineResource; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.Matchers.greaterThan; 6 | import static org.hamcrest.core.IsEqual.equalTo; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | import com.google.common.io.ByteStreams; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | import java.net.Socket; 14 | import java.net.URISyntaxException; 15 | import net.bramp.ffmpeg.fixtures.Progresses; 16 | import org.junit.Test; 17 | 18 | public class TcpProgressParserTest extends AbstractProgressParserTest { 19 | 20 | @Override 21 | public ProgressParser newParser(ProgressListener listener) 22 | throws IOException, URISyntaxException { 23 | return new TcpProgressParser(listener); 24 | } 25 | 26 | @Test 27 | public void testNormal() throws IOException, InterruptedException, URISyntaxException { 28 | parser.start(); 29 | 30 | Socket client = new Socket(uri.getHost(), uri.getPort()); 31 | assertTrue("Socket is connected", client.isConnected()); 32 | 33 | InputStream inputStream = combineResource(Progresses.allFiles); 34 | OutputStream outputStream = client.getOutputStream(); 35 | 36 | long bytes = ByteStreams.copy(inputStream, outputStream); 37 | 38 | // HACK, but give the TcpProgressParser thread time to actually handle the connection/data 39 | // before the client is closed, and the parser is stopped. 40 | Thread.sleep(100); 41 | 42 | client.close(); 43 | parser.stop(); 44 | 45 | assertThat(bytes, greaterThan(0L)); 46 | assertThat(progesses, equalTo(Progresses.allProgresses)); 47 | } 48 | 49 | 50 | 51 | @Test 52 | public void testNaProgressPackets() throws IOException, InterruptedException, URISyntaxException { 53 | parser.start(); 54 | 55 | Socket client = new Socket(uri.getHost(), uri.getPort()); 56 | assertTrue("Socket is connected", client.isConnected()); 57 | 58 | InputStream inputStream = combineResource(Progresses.naProgressFile); 59 | OutputStream outputStream = client.getOutputStream(); 60 | 61 | long bytes = ByteStreams.copy(inputStream, outputStream); 62 | 63 | // HACK, but give the TcpProgressParser thread time to actually handle the connection/data 64 | // before the client is closed, and the parser is stopped. 65 | Thread.sleep(100); 66 | 67 | client.close(); 68 | parser.stop(); 69 | 70 | assertThat(bytes, greaterThan(0L)); 71 | assertThat(progesses, equalTo(Progresses.naProgresses)); 72 | } 73 | 74 | @Test 75 | public void testPrematureDisconnect() 76 | throws IOException { 77 | parser.start(); 78 | new Socket(uri.getHost(), uri.getPort()).close(); 79 | parser.stop(); 80 | 81 | assertTrue(progesses.isEmpty()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/net/bramp/ffmpeg/progress/UdpProgressParserTest.java: -------------------------------------------------------------------------------- 1 | package net.bramp.ffmpeg.progress; 2 | 3 | import static net.bramp.ffmpeg.Helper.loadResource; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.core.IsEqual.equalTo; 6 | 7 | import com.google.common.io.ByteStreams; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.net.DatagramPacket; 11 | import java.net.DatagramSocket; 12 | import java.net.InetAddress; 13 | import java.net.URISyntaxException; 14 | import net.bramp.ffmpeg.fixtures.Progresses; 15 | import org.junit.Test; 16 | 17 | public class UdpProgressParserTest extends AbstractProgressParserTest { 18 | 19 | @Override 20 | public ProgressParser newParser(ProgressListener listener) 21 | throws IOException, URISyntaxException { 22 | return new UdpProgressParser(listener); 23 | } 24 | 25 | @Test 26 | public void testNormal() throws IOException, InterruptedException { 27 | parser.start(); 28 | 29 | final InetAddress addr = InetAddress.getByName(uri.getHost()); 30 | final int port = uri.getPort(); 31 | 32 | try (DatagramSocket socket = new DatagramSocket()) { 33 | // Load each Progress Fixture, and send in a single datagram packet 34 | for (String progressFixture : Progresses.allFiles) { 35 | InputStream inputStream = loadResource(progressFixture); 36 | byte[] bytes = ByteStreams.toByteArray(inputStream); 37 | 38 | DatagramPacket packet = new DatagramPacket(bytes, bytes.length, addr, port); 39 | socket.send(packet); 40 | } 41 | } 42 | 43 | Thread.sleep(1000); // HACK: Wait a short while to avoid closing the receiving socket 44 | 45 | parser.stop(); 46 | 47 | assertThat(progesses, equalTo(Progresses.allProgresses)); 48 | } 49 | 50 | @Test 51 | public void testNaProgressPackets() throws IOException, InterruptedException, URISyntaxException { 52 | parser.start(); 53 | 54 | final InetAddress addr = InetAddress.getByName(uri.getHost()); 55 | final int port = uri.getPort(); 56 | 57 | try (DatagramSocket socket = new DatagramSocket()) { 58 | // Load each Progress Fixture, and send in a single datagram packet 59 | for (String progressFixture : Progresses.naProgressFile) { 60 | InputStream inputStream = loadResource(progressFixture); 61 | byte[] bytes = ByteStreams.toByteArray(inputStream); 62 | 63 | DatagramPacket packet = new DatagramPacket(bytes, bytes.length, addr, port); 64 | socket.send(packet); 65 | } 66 | } 67 | 68 | Thread.sleep(100); // HACK: Wait a short while to avoid closing the receiving socket 69 | 70 | parser.stop(); 71 | 72 | assertThat(progesses, equalTo(Progresses.naProgresses)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/avconv-version: -------------------------------------------------------------------------------- 1 | avconv version 11.4, Copyright (c) 2000-2014 the Libav developers 2 | built on Aug 6 2016 14:03:55 with Apple LLVM version 7.3.0 (clang-703.0.31) 3 | avconv 11.4 4 | libavutil 54. 3. 0 / 54. 3. 0 5 | libavcodec 56. 1. 0 / 56. 1. 0 6 | libavformat 56. 1. 0 / 56. 1. 0 7 | libavdevice 55. 0. 0 / 55. 0. 0 8 | libavfilter 5. 0. 0 / 5. 0. 0 9 | libavresample 2. 1. 0 / 2. 1. 0 10 | libswscale 3. 0. 0 / 3. 0. 0 11 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/avprobe-version: -------------------------------------------------------------------------------- 1 | avprobe version 11.4, Copyright (c) 2007-2014 the Libav developers 2 | built on Aug 6 2016 14:03:55 with Apple LLVM version 7.3.0 (clang-703.0.31) 3 | avprobe 11.4 4 | libavutil 54. 3. 0 / 54. 3. 0 5 | libavcodec 56. 1. 0 / 56. 1. 0 6 | libavformat 56. 1. 0 / 56. 1. 0 7 | libavdevice 55. 0. 0 / 55. 0. 0 8 | libavfilter 5. 0. 0 / 5. 0. 0 9 | libavresample 2. 1. 0 / 2. 1. 0 10 | libswscale 3. 0. 0 / 3. 0. 0 11 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/chapters_with_long_id.m4b: -------------------------------------------------------------------------------- 1 | { 2 | "streams": [ 3 | { 4 | "index": 0, 5 | "codec_name": "aac", 6 | "codec_long_name": "AAC (Advanced Audio Coding)", 7 | "profile": "LC", 8 | "codec_type": "audio", 9 | "codec_time_base": "1/44100", 10 | "codec_tag_string": "mp4a", 11 | "codec_tag": "0x6134706d", 12 | "sample_fmt": "fltp", 13 | "sample_rate": "44100", 14 | "channels": 1, 15 | "channel_layout": "mono", 16 | "bits_per_sample": 0, 17 | "r_frame_rate": "0/0", 18 | "avg_frame_rate": "0/0", 19 | "time_base": "1/44100", 20 | "start_pts": 0, 21 | "start_time": "0.000000", 22 | "duration_ts": 248628224, 23 | "duration": "5637.828209", 24 | "bit_rate": "62993", 25 | "max_bit_rate": "75648", 26 | "nb_frames": "242801", 27 | "disposition": { 28 | "default": 1, 29 | "dub": 0, 30 | "original": 0, 31 | "comment": 0, 32 | "lyrics": 0, 33 | "karaoke": 0, 34 | "forced": 0, 35 | "hearing_impaired": 0, 36 | "visual_impaired": 0, 37 | "clean_effects": 0, 38 | "attached_pic": 0, 39 | "timed_thumbnails": 0 40 | }, 41 | "tags": { 42 | "creation_time": "2016-02-21T08:27:56.000000Z", 43 | "language": "und" 44 | } 45 | }, 46 | { 47 | "index": 1, 48 | "codec_name": "bin_data", 49 | "codec_long_name": "binary data", 50 | "codec_type": "data", 51 | "codec_tag_string": "text", 52 | "codec_tag": "0x74786574", 53 | "r_frame_rate": "0/0", 54 | "avg_frame_rate": "0/0", 55 | "time_base": "1/44100", 56 | "start_pts": 0, 57 | "start_time": "0.000000", 58 | "duration_ts": 248628224, 59 | "duration": "5637.828209", 60 | "bit_rate": "1", 61 | "nb_frames": "24", 62 | "disposition": { 63 | "default": 0, 64 | "dub": 0, 65 | "original": 0, 66 | "comment": 0, 67 | "lyrics": 0, 68 | "karaoke": 0, 69 | "forced": 0, 70 | "hearing_impaired": 0, 71 | "visual_impaired": 0, 72 | "clean_effects": 0, 73 | "attached_pic": 0, 74 | "timed_thumbnails": 0 75 | }, 76 | "tags": { 77 | "creation_time": "2016-02-21T08:28:13.000000Z", 78 | "language": "und" 79 | } 80 | } 81 | ], 82 | "chapters": [ 83 | { 84 | "id": 6613449456311024506, 85 | "time_base": "1/1000000000", 86 | "start": 0, 87 | "start_time": "0.000000", 88 | "end": 435458000000, 89 | "end_time": "435.458000", 90 | "tags": { 91 | "title": "Chapitre 01" 92 | } 93 | }, 94 | { 95 | "id": -4433436293284298339, 96 | "time_base": "1/1000000000", 97 | "start": 435458000000, 98 | "start_time": "435.458000", 99 | "end": 852333000000, 100 | "end_time": "852.333000", 101 | "tags": { 102 | "title": "Chapitre 02" 103 | } 104 | } 105 | ], 106 | "format": { 107 | "filename": "sammyjay_1602_librivox.m4b", 108 | "nb_streams": 2, 109 | "nb_programs": 0, 110 | "format_name": "mov,mp4,m4a,3gp,3g2,mj2", 111 | "format_long_name": "QuickTime / MOV", 112 | "start_time": "0.000000", 113 | "duration": "5637.828209", 114 | "size": "45417172", 115 | "bit_rate": "64446", 116 | "probe_score": 100, 117 | "tags": { 118 | "major_brand": "M4A", 119 | "minor_version": "0", 120 | "compatible_brands": "3gp5isom", 121 | "creation_time": "2016-02-21T08:27:56.000000Z", 122 | "genre": "Audiobook", 123 | "media_type": "2", 124 | "encoder": "Chapter and Verse V 1.5", 125 | "title": "The Adventures of Sammy Jay", 126 | "artist": "Thornton W. Burgess", 127 | "album": "The Adventures of Sammy Jay", 128 | "comment": "https://archive.org/details/sammyjay_1602_librivox", 129 | "copyright": "PD1.0", 130 | "track": "1", 131 | "Encoding Params": "vers" 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-layouts: -------------------------------------------------------------------------------- 1 | Individual channels: 2 | NAME DESCRIPTION 3 | FL front left 4 | FR front right 5 | FC front center 6 | LFE low frequency 7 | BL back left 8 | BR back right 9 | FLC front left-of-center 10 | FRC front right-of-center 11 | BC back center 12 | SL side left 13 | SR side right 14 | TC top center 15 | TFL top front left 16 | TFC top front center 17 | TFR top front right 18 | TBL top back left 19 | TBC top back center 20 | TBR top back right 21 | DL downmix left 22 | DR downmix right 23 | WL wide left 24 | WR wide right 25 | SDL surround direct left 26 | SDR surround direct right 27 | LFE2 low frequency 2 28 | TSL top side left 29 | TSR top side right 30 | BFC bottom front center 31 | BFL bottom front left 32 | BFR bottom front right 33 | 34 | Standard channel layouts: 35 | NAME DECOMPOSITION 36 | mono FC 37 | stereo FL+FR 38 | 2.1 FL+FR+LFE 39 | 3.0 FL+FR+FC 40 | 3.0(back) FL+FR+BC 41 | 4.0 FL+FR+FC+BC 42 | quad FL+FR+BL+BR 43 | quad(side) FL+FR+SL+SR 44 | 3.1 FL+FR+FC+LFE 45 | 5.0 FL+FR+FC+BL+BR 46 | 5.0(side) FL+FR+FC+SL+SR 47 | 4.1 FL+FR+FC+LFE+BC 48 | 5.1 FL+FR+FC+LFE+BL+BR 49 | 5.1(side) FL+FR+FC+LFE+SL+SR 50 | 6.0 FL+FR+FC+BC+SL+SR 51 | 6.0(front) FL+FR+FLC+FRC+SL+SR 52 | hexagonal FL+FR+FC+BL+BR+BC 53 | 6.1 FL+FR+FC+LFE+BC+SL+SR 54 | 6.1(back) FL+FR+FC+LFE+BL+BR+BC 55 | 6.1(front) FL+FR+LFE+FLC+FRC+SL+SR 56 | 7.0 FL+FR+FC+BL+BR+SL+SR 57 | 7.0(front) FL+FR+FC+FLC+FRC+SL+SR 58 | 7.1 FL+FR+FC+LFE+BL+BR+SL+SR 59 | 7.1(wide) FL+FR+FC+LFE+BL+BR+FLC+FRC 60 | 7.1(wide-side) FL+FR+FC+LFE+FLC+FRC+SL+SR 61 | 7.1(top) FL+FR+FC+LFE+BL+BR+TFL+TFR 62 | octagonal FL+FR+FC+BL+BR+BC+SL+SR 63 | cube FL+FR+BL+BR+TFL+TFR+TBL+TBR 64 | hexadecagonal FL+FR+FC+BL+BR+BC+SL+SR+TFL+TFC+TFR+TBL+TBC+TBR+WL+WR 65 | downmix DL+DR 66 | 22.2 FL+FR+FC+LFE+BL+BR+FLC+FRC+BC+SL+SR+TC+TFL+TFC+TFR+TBL+TBC+TBR+LFE2+TSL+TSR+BFC+BFL+BFR 67 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-no-such-file: -------------------------------------------------------------------------------- 1 | toto.mp4: No such file or directory -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-progress-0: -------------------------------------------------------------------------------- 1 | frame=5 2 | fps=0.0 3 | stream_0_0_q=0.0 4 | bitrate= 0.8kbits/s 5 | total_size=48 6 | out_time_ms=512000 7 | out_time=00:00:00.512000 8 | dup_frames=0 9 | drop_frames=0 10 | speed=1.01x 11 | progress=continue 12 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-progress-1: -------------------------------------------------------------------------------- 1 | frame=118 2 | fps=23.4 3 | stream_0_0_q=28.0 4 | bitrate=N/A 5 | total_size=N/A 6 | out_time_ms=5034667 7 | out_time=00:00:05.034667 8 | dup_frames=0 9 | drop_frames=0 10 | speed=N/A 11 | progress=continue 12 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-progress-2: -------------------------------------------------------------------------------- 1 | frame=132 2 | fps=23.1 3 | stream_0_0_q=-1.0 4 | bitrate=1935.5kbits/s 5 | total_size=1285168 6 | out_time_ms=5312000 7 | out_time=00:00:05.312000 8 | dup_frames=0 9 | drop_frames=0 10 | speed=0.929x 11 | progress=end 12 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-progress-na: -------------------------------------------------------------------------------- 1 | frame=0 2 | fps=0 3 | stream_0_0_q=-1.0 4 | bitrate=N/A 5 | total_size=N/A 6 | out_time_ms=N/A 7 | out_time=N/A 8 | dup_frames=0 9 | drop_frames=0 10 | speed=N/A 11 | progress=end 12 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-version: -------------------------------------------------------------------------------- 1 | ffmpeg version 0.10.9-7:0.10.9-1~raring1 2 | built on Oct 4 2013 06:42:21 with gcc 4.7.3 3 | configuration: --arch=amd64 --disable-stripping --enable-pthreads --enable-runtime-cpudetect --extra-version='7:0.10.9-1~raring1' --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --enable-bzlib --enable-libdc1394 --enable-libfreetype --enable-frei0r --enable-gnutls --enable-libgsm --enable-libmp3lame --enable-librtmp --enable-libopencv --enable-libopenjpeg --enable-libpulse --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-vaapi --enable-vdpau --enable-libvorbis --enable-libvpx --enable-zlib --enable-gpl --enable-postproc --enable-libcdio --enable-x11grab --enable-libx264 --shlibdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static 4 | libavutil 51. 35.100 / 51. 35.100 5 | libavcodec 53. 61.100 / 53. 61.100 6 | libavformat 53. 32.100 / 53. 32.100 7 | libavdevice 53. 4.100 / 53. 4.100 8 | libavfilter 2. 61.100 / 2. 61.100 9 | libswscale 2. 1.100 / 2. 1.100 10 | libswresample 0. 6.100 / 0. 6.100 11 | libpostproc 52. 0.100 / 52. 0.100 12 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffprobe-Always On My Mind [Program Only] - Adelen.mp4: -------------------------------------------------------------------------------- 1 | { 2 | "streams": [ 3 | { 4 | "index": 0, 5 | "codec_name": "h264", 6 | "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", 7 | "profile": "High", 8 | "codec_type": "video", 9 | "codec_time_base": "1/50", 10 | "codec_tag_string": "avc1", 11 | "codec_tag": "0x31637661", 12 | "width": 1920, 13 | "height": 1080, 14 | "coded_width": 1920, 15 | "coded_height": 1088, 16 | "has_b_frames": 1, 17 | "sample_aspect_ratio": "1:1", 18 | "display_aspect_ratio": "16:9", 19 | "pix_fmt": "yuv420p", 20 | "level": 40, 21 | "color_range": "tv", 22 | "color_space": "bt709", 23 | "color_transfer": "bt709", 24 | "color_primaries": "bt709", 25 | "chroma_location": "left", 26 | "refs": 2, 27 | "is_avc": "true", 28 | "nal_length_size": "4", 29 | "r_frame_rate": "25/1", 30 | "avg_frame_rate": "25/1", 31 | "time_base": "1/25000", 32 | "start_pts": 1000, 33 | "start_time": "0.040000", 34 | "duration_ts": 4540000, 35 | "duration": "181.600000", 36 | "bit_rate": "17982770", 37 | "bits_per_raw_sample": "8", 38 | "nb_frames": "4540", 39 | "disposition": { 40 | "default": 1, 41 | "dub": 0, 42 | "original": 0, 43 | "comment": 0, 44 | "lyrics": 0, 45 | "karaoke": 0, 46 | "forced": 0, 47 | "hearing_impaired": 0, 48 | "visual_impaired": 0, 49 | "clean_effects": 0, 50 | "attached_pic": 0 51 | }, 52 | "tags": { 53 | "creation_time": "2015-10-03 16:35:06", 54 | "language": "eng", 55 | "handler_name": "MP4 Video Media Handler", 56 | "encoder": "AVC Coding" 57 | } 58 | }, 59 | { 60 | "index": 1, 61 | "codec_name": "aac", 62 | "codec_long_name": "AAC (Advanced Audio Coding)", 63 | "profile": "LC", 64 | "codec_type": "audio", 65 | "codec_time_base": "1/48000", 66 | "codec_tag_string": "mp4a", 67 | "codec_tag": "0x6134706d", 68 | "sample_fmt": "fltp", 69 | "sample_rate": "48000", 70 | "channels": 2, 71 | "channel_layout": "stereo", 72 | "bits_per_sample": 0, 73 | "r_frame_rate": "0/0", 74 | "avg_frame_rate": "0/0", 75 | "time_base": "1/48000", 76 | "start_pts": 0, 77 | "start_time": "0.000000", 78 | "duration_ts": 8718336, 79 | "duration": "181.632000", 80 | "bit_rate": "388637", 81 | "max_bit_rate": "575625", 82 | "nb_frames": "8514", 83 | "disposition": { 84 | "default": 1, 85 | "dub": 0, 86 | "original": 0, 87 | "comment": 0, 88 | "lyrics": 0, 89 | "karaoke": 0, 90 | "forced": 0, 91 | "hearing_impaired": 0, 92 | "visual_impaired": 0, 93 | "clean_effects": 0, 94 | "attached_pic": 0 95 | }, 96 | "tags": { 97 | "creation_time": "2015-10-03 16:35:06", 98 | "language": "eng", 99 | "handler_name": "MP4 Sound Media Handler" 100 | } 101 | } 102 | ], 103 | "format": { 104 | "filename": "c:\\Users\\Bob\\Always On My Mind [Program Only] - Adelén.mp4", 105 | "nb_streams": 2, 106 | "nb_programs": 0, 107 | "format_name": "mov,mp4,m4a,3gp,3g2,mj2", 108 | "format_long_name": "QuickTime / MOV", 109 | "start_time": "0.000000", 110 | "duration": "181.632000", 111 | "size": "417127573", 112 | "bit_rate": "18372426", 113 | "probe_score": 100, 114 | "tags": { 115 | "major_brand": "mp42", 116 | "minor_version": "0", 117 | "compatible_brands": "isom", 118 | "creation_time": "2015-10-03 16:35:06" 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffprobe-disposition_all_true: -------------------------------------------------------------------------------- 1 | { 2 | "streams": [ 3 | { 4 | "index": 0, 5 | "codec_name": "h264", 6 | "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", 7 | "profile": "Baseline", 8 | "codec_type": "video", 9 | "codec_tag_string": "avc1", 10 | "codec_tag": "0x31637661", 11 | "width": 1280, 12 | "height": 720, 13 | "coded_width": 1280, 14 | "coded_height": 720, 15 | "closed_captions": 0, 16 | "film_grain": 0, 17 | "has_b_frames": 0, 18 | "pix_fmt": "yuv420p", 19 | "level": 41, 20 | "color_range": "tv", 21 | "color_space": "bt709", 22 | "color_transfer": "bt709", 23 | "color_primaries": "bt709", 24 | "chroma_location": "left", 25 | "field_order": "progressive", 26 | "refs": 1, 27 | "is_avc": "true", 28 | "nal_length_size": "4", 29 | "id": "0x1", 30 | "r_frame_rate": "25/1", 31 | "avg_frame_rate": "25/1", 32 | "time_base": "1/19200", 33 | "start_pts": 0, 34 | "start_time": "0.000000", 35 | "duration_ts": 100608, 36 | "duration": "5.240000", 37 | "bit_rate": "2228155", 38 | "bits_per_raw_sample": "8", 39 | "nb_frames": "131", 40 | "extradata_size": 29, 41 | "disposition": { 42 | "default": 1, 43 | "dub": 1, 44 | "original": 1, 45 | "comment": 1, 46 | "lyrics": 1, 47 | "karaoke": 1, 48 | "forced": 1, 49 | "hearing_impaired": 1, 50 | "visual_impaired": 1, 51 | "clean_effects": 1, 52 | "attached_pic": 1, 53 | "timed_thumbnails": 1, 54 | "non_diegetic": 1, 55 | "captions": 1, 56 | "descriptions": 1, 57 | "metadata": 1, 58 | "dependent": 1, 59 | "still_image": 1 60 | }, 61 | "tags": { 62 | "language": "und", 63 | "handler_name": "Core Media Video", 64 | "vendor_id": "[0][0][0][0]" 65 | } 66 | } 67 | ], 68 | "format": { 69 | "filename": "Desktop/side_data_list.mp4", 70 | "nb_streams": 2, 71 | "nb_programs": 0, 72 | "format_name": "mov,mp4,m4a,3gp,3g2,mj2", 73 | "format_long_name": "QuickTime / MOV", 74 | "start_time": "0.000000", 75 | "duration": "5.304000", 76 | "size": "1527228", 77 | "bit_rate": "2303511", 78 | "probe_score": 100, 79 | "tags": { 80 | "major_brand": "isom", 81 | "minor_version": "512", 82 | "compatible_brands": "isomiso2avc1mp41", 83 | "encoder": "Lavf58.45.100" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffprobe-divide-by-zero: -------------------------------------------------------------------------------- 1 | { 2 | "streams": [ 3 | { 4 | "index": 0, 5 | "codec_name": "h264", 6 | "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", 7 | "profile": "High", 8 | "codec_type": "video", 9 | "codec_time_base": "1/26", 10 | "codec_tag_string": "[27][0][0][0]", 11 | "codec_tag": "0x001b", 12 | "width": 1920, 13 | "height": 1080, 14 | "coded_width": 1920, 15 | "coded_height": 1088, 16 | "has_b_frames": 0, 17 | "sample_aspect_ratio": "1:1", 18 | "display_aspect_ratio": "16:9", 19 | "pix_fmt": "yuv420p", 20 | "level": 41, 21 | "color_range": "tv", 22 | "color_space": "bt709", 23 | "color_transfer": "bt709", 24 | "color_primaries": "bt709", 25 | "chroma_location": "left", 26 | "refs": 1, 27 | "is_avc": "0", 28 | "nal_length_size": "0", 29 | "id": "0x100", 30 | "r_frame_rate": "167/12", 31 | "avg_frame_rate": "13/1", 32 | "time_base": "1/90000", 33 | "start_pts": 5477657, 34 | "start_time": "60.862856", 35 | "duration_ts": 378588, 36 | "duration": "4.206533", 37 | "bits_per_raw_sample": "8", 38 | "disposition": { 39 | "default": 0, 40 | "dub": 0, 41 | "original": 0, 42 | "comment": 0, 43 | "lyrics": 0, 44 | "karaoke": 0, 45 | "forced": 0, 46 | "hearing_impaired": 0, 47 | "visual_impaired": 0, 48 | "clean_effects": 0, 49 | "attached_pic": 0 50 | } 51 | }, 52 | { 53 | "index": 1, 54 | "codec_name": "aac", 55 | "codec_long_name": "AAC (Advanced Audio Coding)", 56 | "codec_type": "audio", 57 | "codec_time_base": "1/0", 58 | "codec_tag_string": "[15][0][0][0]", 59 | "codec_tag": "0x000f", 60 | "sample_fmt": "fltp", 61 | "sample_rate": "0", 62 | "channels": 0, 63 | "bits_per_sample": 0, 64 | "id": "0x101", 65 | "r_frame_rate": "0/0", 66 | "avg_frame_rate": "0/0", 67 | "time_base": "1/90000", 68 | "start_pts": 5477657, 69 | "start_time": "60.862856", 70 | "duration_ts": 378588, 71 | "duration": "4.206533", 72 | "disposition": { 73 | "default": 0, 74 | "dub": 0, 75 | "original": 0, 76 | "comment": 0, 77 | "lyrics": 0, 78 | "karaoke": 0, 79 | "forced": 0, 80 | "hearing_impaired": 0, 81 | "visual_impaired": 0, 82 | "clean_effects": 0, 83 | "attached_pic": 0 84 | } 85 | } 86 | ], 87 | "format": { 88 | "filename": "1.ts", 89 | "nb_streams": 2, 90 | "nb_programs": 1, 91 | "format_name": "mpegts", 92 | "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)", 93 | "start_time": "60.862856", 94 | "duration": "4.206533", 95 | "size": "574528", 96 | "bit_rate": "1092639", 97 | "probe_score": 100 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffprobe-start_pts_test: -------------------------------------------------------------------------------- 1 | { 2 | "streams": [ 3 | { 4 | "index": 0, 5 | "codec_name": "h264", 6 | "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", 7 | "profile": "Main", 8 | "codec_type": "video", 9 | "codec_time_base": "1/50", 10 | "codec_tag_string": "[27][0][0][0]", 11 | "codec_tag": "0x001b", 12 | "width": 1920, 13 | "height": 1080, 14 | "coded_width": 1920, 15 | "coded_height": 1080, 16 | "has_b_frames": 0, 17 | "sample_aspect_ratio": "0:1", 18 | "display_aspect_ratio": "0:1", 19 | "pix_fmt": "yuvj420p", 20 | "level": 42, 21 | "color_range": "pc", 22 | "color_space": "bt709", 23 | "color_transfer": "bt709", 24 | "color_primaries": "bt709", 25 | "chroma_location": "left", 26 | "field_order": "progressive", 27 | "refs": 1, 28 | "is_avc": "false", 29 | "nal_length_size": "0", 30 | "id": "0x1", 31 | "r_frame_rate": "25/1", 32 | "avg_frame_rate": "25/1", 33 | "time_base": "1/90000", 34 | "start_pts": 8570867078, 35 | "start_time": "95231.856422", 36 | "duration_ts": 31694552, 37 | "duration": "352.161689", 38 | "bits_per_raw_sample": "8", 39 | "disposition": { 40 | "default": 0, 41 | "dub": 0, 42 | "original": 0, 43 | "comment": 0, 44 | "lyrics": 0, 45 | "karaoke": 0, 46 | "forced": 0, 47 | "hearing_impaired": 0, 48 | "visual_impaired": 0, 49 | "clean_effects": 0, 50 | "attached_pic": 0, 51 | "timed_thumbnails": 0 52 | } 53 | }, 54 | { 55 | "index": 1, 56 | "codec_name": "aac", 57 | "codec_long_name": "AAC (Advanced Audio Coding)", 58 | "profile": "LC", 59 | "codec_type": "audio", 60 | "codec_time_base": "1/44100", 61 | "codec_tag_string": "[15][0][0][0]", 62 | "codec_tag": "0x000f", 63 | "sample_fmt": "fltp", 64 | "sample_rate": "44100", 65 | "channels": 2, 66 | "channel_layout": "stereo", 67 | "bits_per_sample": 0, 68 | "id": "0x2", 69 | "r_frame_rate": "0/0", 70 | "avg_frame_rate": "0/0", 71 | "time_base": "1/90000", 72 | "start_pts": 8570867697, 73 | "start_time": "95231.863300", 74 | "duration_ts": 31695687, 75 | "duration": "352.174300", 76 | "bit_rate": "98191", 77 | "disposition": { 78 | "default": 0, 79 | "dub": 0, 80 | "original": 0, 81 | "comment": 0, 82 | "lyrics": 0, 83 | "karaoke": 0, 84 | "forced": 0, 85 | "hearing_impaired": 0, 86 | "visual_impaired": 0, 87 | "clean_effects": 0, 88 | "attached_pic": 0, 89 | "timed_thumbnails": 0 90 | } 91 | } 92 | ], 93 | "format": { 94 | "filename": "TS Test record.ts", 95 | "nb_streams": 2, 96 | "nb_programs": 1, 97 | "format_name": "mpegts", 98 | "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)", 99 | "start_time": "95231.856422", 100 | "duration": "352.181178", 101 | "size": "179003772", 102 | "bit_rate": "4066174", 103 | "probe_score": 50 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/fixtures/ffprobe-version: -------------------------------------------------------------------------------- 1 | ffprobe version 3.0.2 Copyright (c) 2007-2016 the FFmpeg developers 2 | built with Apple LLVM version 7.3.0 (clang-703.0.31) 3 | configuration: --prefix=/Users/bramp/homebrew/Cellar/ffmpeg/3.0.2 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libxvid --enable-libfaac --enable-ffplay --enable-nonfree --enable-vda 4 | libavutil 55. 17.103 / 55. 17.103 5 | libavcodec 57. 24.102 / 57. 24.102 6 | libavformat 57. 25.100 / 57. 25.100 7 | libavdevice 57. 0.101 / 57. 0.101 8 | libavfilter 6. 31.100 / 6. 31.100 9 | libavresample 3. 0. 0 / 3. 0. 0 10 | libswscale 4. 0.100 / 4. 0.100 11 | libswresample 2. 0.101 / 2. 0.101 12 | libpostproc 54. 0.100 / 54. 0.100 13 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/samples/README.md: -------------------------------------------------------------------------------- 1 | Sample media: 2 | 3 | big_buck_bunny_720p_1mb.mp4 - Big Buck Bunny - Creative Commons Attribution 3.0 4 | https://archive.org/details/testmp3testfile - https://archive.org/details/testmp3testfile 5 | -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/samples/big_buck_bunny_720p_1mb.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bramp/ffmpeg-cli-wrapper/a6655138d7cc1b2c62a57a5e95798695e060de6e/src/test/resources/net/bramp/ffmpeg/samples/big_buck_bunny_720p_1mb.mp4 -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/samples/test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bramp/ffmpeg-cli-wrapper/a6655138d7cc1b2c62a57a5e95798695e060de6e/src/test/resources/net/bramp/ffmpeg/samples/test.mp3 -------------------------------------------------------------------------------- /src/test/resources/net/bramp/ffmpeg/samples/testscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bramp/ffmpeg-cli-wrapper/a6655138d7cc1b2c62a57a5e95798695e060de6e/src/test/resources/net/bramp/ffmpeg/samples/testscreen.jpg -------------------------------------------------------------------------------- /tools/codec_enum_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | First of all, FFmpeg should be installed on your local PC. 3 | Running "python codec_enum_generator.py" in the shell creates Audioodec and VideooCodec. 4 | You can update the codec file by putting these two files within the net.bramp.ffmpeg.builder package. 5 | """ 6 | import subprocess 7 | import re 8 | 9 | CODECS_REGEX = re.compile("^ ([.D][.E][VASD][.I][.L][.S]) (\S{2,})\s+(.*)$") 10 | 11 | def removeLeadingNumbers(text): 12 | index = 0 13 | while index < len(text) and text[index].isdigit(): 14 | index += 1 15 | return text[index:] 16 | 17 | def writeCodec(m,codec): 18 | document = "\t"+"/**"+ m.group(3).replace("&","&").rstrip() +"*/\n" 19 | enumCode = "\t" +"public static final String " +removeLeadingNumbers(m.group(2).replace(".","_")).upper() +' = "'+ m.group(2) +'";' +'\n' 20 | codec.write(document) 21 | codec.write(enumCode) 22 | 23 | 24 | output = subprocess.check_output("ffmpeg -codecs", shell=True).decode('utf-8') 25 | 26 | print(output) 27 | 28 | output = output.split("\n") 29 | 30 | videoCodec = open("VideoCodec.java", 'w') 31 | audioCodec = open("AudioCodec.java", 'w') 32 | 33 | # write header 34 | videoCodec.write( 35 | """package net.bramp.ffmpeg.builder; 36 | /**The available codecs may vary depending on the version of FFmpeg. 37 | *
38 | * you can get a list of available codecs through use {@link net.bramp.ffmpeg.FFmpeg#codecs()}. 39 | * 40 | * @see net.bramp.ffmpeg.FFmpeg#codecs() 41 | * @author van1164 42 | * */ 43 | public class VideoCodec { 44 | 45 | """) 46 | audioCodec.write( 47 | """package net.bramp.ffmpeg.builder; 48 | /**The available codecs may vary depending on the version of FFmpeg. 49 | *
50 | * you can get a list of available codecs through use {@link net.bramp.ffmpeg.FFmpeg#codecs()}. 51 | * 52 | * @see net.bramp.ffmpeg.FFmpeg#codecs() 53 | * @author van1164 54 | * */ 55 | public class AudioCodec { 56 | 57 | """) 58 | for item in output: 59 | m = CODECS_REGEX.match(item) 60 | if not m : continue 61 | 62 | lst = item.split() 63 | if 'A' in m.group(1): 64 | writeCodec(m,audioCodec) 65 | 66 | if 'V' in m.group(1): 67 | writeCodec(m,videoCodec) 68 | 69 | 70 | videoCodec.write("}") 71 | audioCodec.write("}") 72 | 73 | videoCodec.close() 74 | audioCodec.close() 75 | --------------------------------------------------------------------------------