001package net.bramp.ffmpeg; 002 003import com.google.common.base.MoreObjects; 004import com.google.common.collect.ImmutableList; 005import com.google.gson.Gson; 006import java.io.IOException; 007import java.io.Reader; 008import java.util.List; 009import javax.annotation.Nonnull; 010import javax.annotation.Nullable; 011import net.bramp.ffmpeg.io.LoggingFilterReader; 012import net.bramp.ffmpeg.probe.FFmpegProbeResult; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Wrapper around FFprobe 018 * 019 * @author bramp 020 */ 021public class FFprobe extends FFcommon { 022 023 static final Logger LOG = LoggerFactory.getLogger(FFprobe.class); 024 025 static final String FFPROBE = "ffprobe"; 026 static final String DEFAULT_PATH = MoreObjects.firstNonNull(System.getenv("FFPROBE"), FFPROBE); 027 028 static final Gson gson = FFmpegUtils.getGson(); 029 030 public FFprobe() throws IOException { 031 this(DEFAULT_PATH, new RunProcessFunction()); 032 } 033 034 public FFprobe(@Nonnull ProcessFunction runFunction) throws IOException { 035 this(DEFAULT_PATH, runFunction); 036 } 037 038 public FFprobe(@Nonnull String path) throws IOException { 039 this(path, new RunProcessFunction()); 040 } 041 042 public FFprobe(@Nonnull String path, @Nonnull ProcessFunction runFunction) { 043 super(path, runFunction); 044 } 045 046 public FFmpegProbeResult probe(String mediaPath) throws IOException { 047 return probe(mediaPath, null); 048 } 049 050 /** 051 * Returns true if the binary we are using is the true ffprobe. This is to avoid conflict with 052 * avprobe (from the libav project), that some symlink to ffprobe. 053 * 054 * @return true iff this is the official ffprobe binary. 055 * @throws IOException If a I/O error occurs while executing ffprobe. 056 */ 057 public boolean isFFprobe() throws IOException { 058 return version().startsWith("ffprobe"); 059 } 060 061 /** 062 * Throws an exception if this is an unsupported version of ffprobe. 063 * 064 * @throws IllegalArgumentException if this is not the official ffprobe binary. 065 * @throws IOException If a I/O error occurs while executing ffprobe. 066 */ 067 private void checkIfFFprobe() throws IllegalArgumentException, IOException { 068 if (!isFFprobe()) { 069 throw new IllegalArgumentException( 070 "This binary '" + path + "' is not a supported version of ffprobe"); 071 } 072 } 073 074 @Override 075 public void run(List<String> args) throws IOException { 076 checkIfFFprobe(); 077 super.run(args); 078 } 079 080 // TODO Add Probe Inputstream 081 public FFmpegProbeResult probe(String mediaPath, @Nullable String userAgent) throws IOException { 082 checkIfFFprobe(); 083 084 ImmutableList.Builder<String> args = new ImmutableList.Builder<String>(); 085 086 // TODO Add: 087 // .add("--show_packets") 088 // .add("--show_frames") 089 090 args.add(path).add("-v", "quiet"); 091 092 if (userAgent != null) { 093 args.add("-user_agent", userAgent); 094 } 095 096 args.add("-print_format", "json") 097 .add("-show_error") 098 .add("-show_format") 099 .add("-show_streams") 100 .add("-show_chapters") 101 .add(mediaPath); 102 103 Process p = runFunc.run(args.build()); 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); 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}