All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itshidu.ffmpeg.FFmpeg Maven / Gradle / Ivy

The newest version!
package com.itshidu.ffmpeg;

import com.google.common.collect.ImmutableList;
import com.itshidu.ffmpeg.builder.FFmpegBuilder;
import com.itshidu.ffmpeg.info.Codec;
import com.itshidu.ffmpeg.info.Format;
import com.itshidu.ffmpeg.progress.ProgressListener;
import com.itshidu.ffmpeg.progress.ProgressParser;
import com.itshidu.ffmpeg.progress.TcpProgressParser;

import org.apache.commons.lang3.math.Fraction;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Wrapper around FFmpeg
 *
 * @author bramp
 */
public class FFmpeg extends FFcommon {

  public static final String FFMPEG = "ffmpeg";
  public static final String DEFAULT_PATH = firstNonNull(System.getenv("FFMPEG"), FFMPEG);

  public static final Fraction FPS_30 = Fraction.getFraction(30, 1);
  public static final Fraction FPS_29_97 = Fraction.getFraction(30000, 1001);
  public static final Fraction FPS_24 = Fraction.getFraction(24, 1);
  public static final Fraction FPS_23_976 = Fraction.getFraction(24000, 1001);

  public static final int AUDIO_MONO = 1;
  public static final int AUDIO_STEREO = 2;

  public static final String AUDIO_FORMAT_U8 = "u8"; // 8
  public static final String AUDIO_FORMAT_S16 = "s16"; // 16
  public static final String AUDIO_FORMAT_S32 = "s32"; // 32
  public static final String AUDIO_FORMAT_FLT = "flt"; // 32
  public static final String AUDIO_FORMAT_DBL = "dbl"; // 64

  @Deprecated public static final String AUDIO_DEPTH_U8 = AUDIO_FORMAT_U8;
  @Deprecated public static final String AUDIO_DEPTH_S16 = AUDIO_FORMAT_S16;
  @Deprecated public static final String AUDIO_DEPTH_S32 = AUDIO_FORMAT_S32;
  @Deprecated public static final String AUDIO_DEPTH_FLT = AUDIO_FORMAT_FLT;
  @Deprecated public static final String AUDIO_DEPTH_DBL = AUDIO_FORMAT_DBL;

  public static final int AUDIO_SAMPLE_8000 = 8000;
  public static final int AUDIO_SAMPLE_11025 = 11025;
  public static final int AUDIO_SAMPLE_12000 = 12000;
  public static final int AUDIO_SAMPLE_16000 = 16000;
  public static final int AUDIO_SAMPLE_22050 = 22050;
  public static final int AUDIO_SAMPLE_32000 = 32000;
  public static final int AUDIO_SAMPLE_44100 = 44100;
  public static final int AUDIO_SAMPLE_48000 = 48000;
  public static final int AUDIO_SAMPLE_96000 = 96000;

  static final Pattern CODECS_REGEX =
      Pattern.compile("^ ([ D][ E][VAS][ S][ D][ T]) (\\S+)\\s+(.*)$");
  static final Pattern FORMATS_REGEX = Pattern.compile("^ ([ D][ E]) (\\S+)\\s+(.*)$");

  /** Supported codecs */
  List codecs = null;

  /** Supported formats */
  List formats = null;

  public FFmpeg() throws IOException {
    this(DEFAULT_PATH, new RunProcessFunction());
  }

  public FFmpeg(@Nonnull ProcessFunction runFunction) throws IOException {
    this(DEFAULT_PATH, runFunction);
  }

  public FFmpeg(@Nonnull String path) throws IOException {
    this(path, new RunProcessFunction());
  }

  public FFmpeg(@Nonnull String path, @Nonnull ProcessFunction runFunction) throws IOException {
    super(path, runFunction);
    version();
  }

  /**
   * Returns true if the binary we are using is the true ffmpeg. This is to avoid conflict with
   * avconv (from the libav project), that some symlink to ffmpeg.
   *
   * @return true iff this is the official ffmpeg binary.
   * @throws IOException If a I/O error occurs while executing ffmpeg.
   */
  public boolean isFFmpeg() throws IOException {
    return version().startsWith("ffmpeg");
  }

  /**
   * Throws an exception if this is an unsupported version of ffmpeg.
   *
   * @throws IllegalArgumentException if this is not the official ffmpeg binary.
   * @throws IOException If a I/O error occurs while executing ffmpeg.
   */
  private void checkIfFFmpeg() throws IllegalArgumentException, IOException {
    if (!isFFmpeg()) {
      throw new IllegalArgumentException(
          "This binary '" + path + "' is not a supported version of ffmpeg");
    }
  }

  public synchronized @Nonnull List codecs() throws IOException {
    checkIfFFmpeg();

    if (this.codecs == null) {
      codecs = new ArrayList<>();

      Process p = runFunc.run(ImmutableList.of(path, "-codecs"));
      try {
        BufferedReader r = wrapInReader(p);
        String line;
        while ((line = r.readLine()) != null) {
          Matcher m = CODECS_REGEX.matcher(line);
          if (!m.matches()) continue;

          codecs.add(new Codec(m.group(2), m.group(3), m.group(1)));
        }

        throwOnError(p);
        this.codecs = ImmutableList.copyOf(codecs);
      } finally {
        p.destroy();
      }
    }

    return codecs;
  }

  public synchronized @Nonnull List formats() throws IOException {
    checkIfFFmpeg();

    if (this.formats == null) {
      formats = new ArrayList<>();

      Process p = runFunc.run(ImmutableList.of(path, "-formats"));
      try {
        BufferedReader r = wrapInReader(p);
        String line;
        while ((line = r.readLine()) != null) {
          Matcher m = FORMATS_REGEX.matcher(line);
          if (!m.matches()) continue;

          formats.add(new Format(m.group(2), m.group(3), m.group(1)));
        }

        throwOnError(p);
        this.formats = ImmutableList.copyOf(formats);
      } finally {
        p.destroy();
      }
    }
    return formats;
  }

  protected ProgressParser createProgressParser(ProgressListener listener) throws IOException {
    // TODO In future create the best kind for this OS, unix socket, named pipe, or TCP.
    try {
      // Default to TCP because it is supported across all OSes, and is better than UDP because it
      // provides good properties such as in-order packets, reliability, error checking, etc.
      return new TcpProgressParser(checkNotNull(listener));
    } catch (URISyntaxException e) {
      throw new IOException(e);
    }
  }

  @Override
  public void run(List args) throws IOException {
    checkIfFFmpeg();
    super.run(args);
  }

  public void run(FFmpegBuilder builder) throws IOException {
    run(builder, null);
  }

  public void run(FFmpegBuilder builder, @Nullable ProgressListener listener) throws IOException {
    checkNotNull(builder);

    if (listener != null) {
      try (ProgressParser progressParser = createProgressParser(listener)) {
        progressParser.start();
        builder = builder.addProgress(progressParser.getUri());

        run(builder.build());
      }
    } else {
      run(builder.build());
    }
  }

  @CheckReturnValue
  public FFmpegBuilder builder() {
    return new FFmpegBuilder();
  }

  @Override
  public String getPath() {
    return path;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy