001package net.bramp.ffmpeg;
002
003import static com.google.common.base.Preconditions.checkNotNull;
004
005import com.google.common.base.MoreObjects;
006import com.google.gson.Gson;
007import java.io.IOException;
008import java.io.Reader;
009import java.util.List;
010import javax.annotation.CheckReturnValue;
011import javax.annotation.Nonnull;
012import javax.annotation.Nullable;
013import net.bramp.ffmpeg.builder.FFprobeBuilder;
014import net.bramp.ffmpeg.io.LoggingFilterReader;
015import net.bramp.ffmpeg.probe.FFmpegProbeResult;
016import org.slf4j.Logger;
017import org.slf4j.LoggerFactory;
018
019/**
020 * Wrapper around FFprobe
021 *
022 * @author bramp
023 */
024public class FFprobe extends FFcommon {
025
026  static final Logger LOG = LoggerFactory.getLogger(FFprobe.class);
027
028  static final String FFPROBE = "ffprobe";
029  static final String DEFAULT_PATH = MoreObjects.firstNonNull(System.getenv("FFPROBE"), FFPROBE);
030
031  static final Gson gson = FFmpegUtils.getGson();
032
033  public FFprobe() throws IOException {
034    this(DEFAULT_PATH, new RunProcessFunction());
035  }
036
037  public FFprobe(@Nonnull ProcessFunction runFunction) throws IOException {
038    this(DEFAULT_PATH, runFunction);
039  }
040
041  public FFprobe(@Nonnull String path) throws IOException {
042    this(path, new RunProcessFunction());
043  }
044
045  public FFprobe(@Nonnull String path, @Nonnull ProcessFunction runFunction) {
046    super(path, runFunction);
047  }
048
049  public FFmpegProbeResult probe(String mediaPath) throws IOException {
050    return probe(mediaPath, null);
051  }
052
053  /**
054   * Returns true if the binary we are using is the true ffprobe. This is to avoid conflict with
055   * avprobe (from the libav project), that some symlink to ffprobe.
056   *
057   * @return true iff this is the official ffprobe binary.
058   * @throws IOException If a I/O error occurs while executing ffprobe.
059   */
060  public boolean isFFprobe() throws IOException {
061    return version().startsWith("ffprobe");
062  }
063
064  /**
065   * Throws an exception if this is an unsupported version of ffprobe.
066   *
067   * @throws IllegalArgumentException if this is not the official ffprobe binary.
068   * @throws IOException If a I/O error occurs while executing ffprobe.
069   */
070  private void checkIfFFprobe() throws IllegalArgumentException, IOException {
071    if (!isFFprobe()) {
072      throw new IllegalArgumentException(
073          "This binary '" + path + "' is not a supported version of ffprobe");
074    }
075  }
076
077  @Override
078  public void run(List<String> args) throws IOException {
079    checkIfFFprobe();
080    super.run(args);
081  }
082
083  public FFmpegProbeResult probe(String mediaPath, @Nullable String userAgent) throws IOException {
084    return probe(this.builder().setInput(mediaPath).setUserAgent(userAgent));
085  }
086
087  public FFmpegProbeResult probe(FFprobeBuilder builder) throws IOException {
088    checkNotNull(builder);
089    return probe(builder.build());
090  }
091
092  public FFmpegProbeResult probe(
093      String mediaPath, @Nullable String userAgent, @Nullable String... extraArgs)
094      throws IOException {
095    return probe(
096        this.builder().setInput(mediaPath).setUserAgent(userAgent).addExtraArgs(extraArgs).build());
097  }
098
099  // TODO Add Probe Inputstream
100  public FFmpegProbeResult probe(List<String> args) throws IOException {
101    checkIfFFprobe();
102
103    Process p = runFunc.run(path(args));
104    try {
105      Reader reader = wrapInReader(p);
106      if (LOG.isDebugEnabled()) {
107        reader = new LoggingFilterReader(reader, LOG);
108      }
109
110      FFmpegProbeResult result = gson.fromJson(reader, FFmpegProbeResult.class);
111
112      throwOnError(p, result);
113
114      if (result == null) {
115        throw new IllegalStateException("Gson returned null, which shouldn't happen :(");
116      }
117
118      return result;
119
120    } finally {
121      p.destroy();
122    }
123  }
124
125  @CheckReturnValue
126  public FFprobeBuilder builder() {
127    return new FFprobeBuilder();
128  }
129}