int index; ///< stream/chapter/program number
} MetadataMap;
-typedef struct ChapterMap {
- int in_file;
- int out_file;
-} ChapterMap;
-
static const OptionDef options[];
-#define MAX_FILES 100
+#define MAX_STREAMS 1024 /* arbitrary sanity check value */
- static const char *last_asked_format = NULL;
+static AVDictionary *ts_scale;
-static const char *last_asked_format = NULL;
-static double *ts_scale;
-static int nb_ts_scale;
-
-static AVFormatContext *output_files[MAX_FILES];
-static AVDictionary *output_opts[MAX_FILES];
-static int nb_output_files = 0;
-
--static StreamMap *stream_maps = NULL;
--static int nb_stream_maps;
-
+static AVDictionary *codec_names;
/* first item specifies output metadata, second is input */
static MetadataMap (*meta_data_maps)[2] = NULL;
static float mux_preload= 0.5;
static float mux_max_delay= 0.7;
--static int64_t recording_time = INT64_MAX;
--static int64_t start_time = 0;
--static int64_t input_ts_offset = 0;
static int file_overwrite = 0;
static AVDictionary *metadata;
static int do_benchmark = 0;
static int nb_frames_dup = 0;
static int nb_frames_drop = 0;
static int input_sync;
- static uint64_t limit_filesize = UINT64_MAX;
-static uint64_t limit_filesize = 0;
static int force_fps = 0;
static char *forced_key_frames = NULL;
static InputFile *input_files = NULL;
static int nb_input_files = 0;
+static OutputStream *output_streams = NULL;
+static int nb_output_streams = 0;
+static OutputFile *output_files = NULL;
+static int nb_output_files = 0;
+
++typedef struct OptionsContext {
++ /* input/output options */
++ int64_t start_time;
++ const char *format;
++
++ /* input options */
++ int64_t input_ts_offset;
++
++ /* output options */
++ StreamMap *stream_maps;
++ int nb_stream_maps;
++
++ int64_t recording_time;
++ uint64_t limit_filesize;
++} OptionsContext;
++
++static void reset_options(OptionsContext *o)
++{
++ const OptionDef *po = options;
++
++ /* all OPT_SPEC and OPT_STRING can be freed in generic way */
++ while (po->name) {
++ void *dst = (uint8_t*)o + po->u.off;
++
++ if (po->flags & OPT_SPEC) {
++ SpecifierOpt **so = dst;
++ int i, *count = (int*)(so + 1);
++ for (i = 0; i < *count; i++) {
++ av_freep(&(*so)[i].specifier);
++ if (po->flags & OPT_STRING)
++ av_freep(&(*so)[i].u.str);
++ }
++ av_freep(so);
++ *count = 0;
++ } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING)
++ av_freep(dst);
++ po++;
++ }
++
++ av_freep(&o->stream_maps);
++
++ memset(o, 0, sizeof(*o));
++
++ o->recording_time = INT64_MAX;
++ o->limit_filesize = UINT64_MAX;
++
++ uninit_opts();
++ init_opts();
++}
++
#if CONFIG_AVFILTER
static int configure_video_filters(InputStream *ist, OutputStream *ost)
#endif
}
+/* read a key without blocking */
+static int read_key(void)
+{
+ unsigned char ch;
+#if HAVE_TERMIOS_H
+ int n = 1;
+ struct timeval tv;
+ fd_set rfds;
+
+ if(run_as_daemon)
+ return -1;
+
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ n = select(1, &rfds, NULL, NULL, &tv);
+ if (n > 0) {
+ n = read(0, &ch, 1);
+ if (n == 1)
+ return ch;
+
+ return n;
+ }
+#elif HAVE_KBHIT
+# if HAVE_PEEKNAMEDPIPE
+ static int is_pipe;
+ static HANDLE input_handle;
+ DWORD dw, nchars;
+ if(!input_handle){
+ input_handle = GetStdHandle(STD_INPUT_HANDLE);
+ is_pipe = !GetConsoleMode(input_handle, &dw);
+ }
+
+ if (stdin->_cnt > 0) {
+ read(0, &ch, 1);
+ return ch;
+ }
+ if (is_pipe) {
+ /* When running under a GUI, you will end here. */
+ if (!PeekNamedPipe(input_handle, NULL, 0, NULL, &nchars, NULL))
+ return -1;
+ //Read it
+ if(nchars != 0) {
+ read(0, &ch, 1);
+ return ch;
+ }else{
+ return -1;
+ }
+ }
+# endif
+ if(kbhit())
+ return(getch());
+#endif
+ return -1;
+}
+
static int decode_interrupt_cb(void)
{
- return received_nb_signals > 1;
+ q_pressed += read_key() == 'q';
+ return q_pressed > 1;
}
- static int exit_program(int ret)
+ void exit_program(int ret)
{
int i;
}
#if CONFIG_AVFILTER
- if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- for (i = 0; i < nb_ostreams; i++) {
- ost = ost_table[i];
+ if(ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- if (start_time == 0 || ist->pts >= start_time) {
- for(i=0;i<nb_ostreams;i++) {
++ for(i=0;i<nb_ostreams;i++) {
++ OutputFile *of = &output_files[ost_table[i].file_index];
++ if (of->start_time == 0 || ist->pts >= of->start_time) {
+ ost = &ost_table[i];
if (ost->input_video_filter && ost->source_index == ist_index) {
- AVRational sar;
- if (ist->st->sample_aspect_ratio.num)
- sar = ist->st->sample_aspect_ratio;
- else
- sar = ist->st->codec->sample_aspect_ratio;
- // add it to be filtered
- av_vsrc_buffer_add_frame(ost->input_video_filter, &picture,
- ist->pts,
- sar);
+ if (!picture.sample_aspect_ratio.num)
+ picture.sample_aspect_ratio = ist->st->sample_aspect_ratio;
+ picture.pts = ist->pts;
+
+ av_vsrc_buffer_add_frame(ost->input_video_filter, &picture, AV_VSRC_BUF_FLAG_OVERWRITE);
}
}
}
return ret;
}
--static int opt_format(const char *opt, const char *arg)
--{
-- last_asked_format = arg;
-- return 0;
--}
--
static int opt_video_rc_override_string(const char *opt, const char *arg)
{
video_rc_override_string = arg;
return 0;
}
--static int opt_map(const char *opt, const char *arg)
++static int opt_map(OptionsContext *o, const char *opt, const char *arg)
{
- StreamMap *m;
- char *p;
+ StreamMap *m = NULL;
+ int i, negative = 0, file_idx;
+ int sync_file_idx = -1, sync_stream_idx;
+ char *p, *sync;
+ char *map;
+
+ if (*arg == '-') {
+ negative = 1;
+ arg++;
+ }
+ map = av_strdup(arg);
+
+ /* parse sync stream first, just pick first matching stream */
+ if (sync = strchr(map, ',')) {
+ *sync = 0;
+ sync_file_idx = strtol(sync + 1, &sync, 0);
+ if (sync_file_idx >= nb_input_files || sync_file_idx < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid sync file index: %d.\n", sync_file_idx);
+ exit_program(1);
+ }
+ if (*sync)
+ sync++;
+ for (i = 0; i < input_files[sync_file_idx].nb_streams; i++)
+ if (check_stream_specifier(input_files[sync_file_idx].ctx,
+ input_files[sync_file_idx].ctx->streams[i], sync) == 1) {
+ sync_stream_idx = i;
+ break;
+ }
+ if (i == input_files[sync_file_idx].nb_streams) {
+ av_log(NULL, AV_LOG_ERROR, "Sync stream specification in map %s does not "
+ "match any streams.\n", arg);
+ exit_program(1);
+ }
+ }
- stream_maps = grow_array(stream_maps, sizeof(*stream_maps), &nb_stream_maps, nb_stream_maps + 1);
- m = &stream_maps[nb_stream_maps-1];
- m->file_index = strtol(arg, &p, 0);
- if (*p)
- p++;
+ file_idx = strtol(map, &p, 0);
+ if (file_idx >= nb_input_files || file_idx < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid input file index: %d.\n", file_idx);
+ exit_program(1);
+ }
+ if (negative)
+ /* disable some already defined maps */
- for (i = 0; i < nb_stream_maps; i++) {
- m = &stream_maps[i];
++ for (i = 0; i < o->nb_stream_maps; i++) {
++ m = &o->stream_maps[i];
+ if (check_stream_specifier(input_files[m->file_index].ctx,
+ input_files[m->file_index].ctx->streams[m->stream_index],
+ *p == ':' ? p + 1 : p) > 0)
+ m->disabled = 1;
+ }
+ else
+ for (i = 0; i < input_files[file_idx].nb_streams; i++) {
+ if (check_stream_specifier(input_files[file_idx].ctx, input_files[file_idx].ctx->streams[i],
+ *p == ':' ? p + 1 : p) <= 0)
+ continue;
- stream_maps = grow_array(stream_maps, sizeof(*stream_maps), &nb_stream_maps, nb_stream_maps + 1);
- m = &stream_maps[nb_stream_maps - 1];
++ o->stream_maps = grow_array(o->stream_maps, sizeof(*o->stream_maps),
++ &o->nb_stream_maps, o->nb_stream_maps + 1);
++ m = &o->stream_maps[o->nb_stream_maps - 1];
- m->stream_index = strtol(p, &p, 0);
- if (*p) {
- p++;
- m->sync_file_index = strtol(p, &p, 0);
- if (*p)
- p++;
- m->sync_stream_index = strtol(p, &p, 0);
- } else {
- m->sync_file_index = m->file_index;
- m->sync_stream_index = m->stream_index;
+ m->file_index = file_idx;
+ m->stream_index = i;
+
+ if (sync_file_idx >= 0) {
+ m->sync_file_index = sync_file_idx;
+ m->sync_stream_index = sync_stream_idx;
+ } else {
+ m->sync_file_index = file_idx;
+ m->sync_stream_index = i;
+ }
+ }
+
+ if (!m) {
+ av_log(NULL, AV_LOG_ERROR, "Stream map '%s' matches no streams.\n", arg);
+ exit_program(1);
}
+
+ av_freep(&map);
return 0;
}
return opt_map_metadata(opt, arg);
}
-static int opt_map_chapters(const char *opt, const char *arg)
-{
- ChapterMap *c;
- char *p;
-
- chapter_maps = grow_array(chapter_maps, sizeof(*chapter_maps), &nb_chapter_maps,
- nb_chapter_maps + 1);
- c = &chapter_maps[nb_chapter_maps - 1];
- c->out_file = strtol(arg, &p, 0);
- if (*p)
- p++;
-
- c->in_file = strtol(p, &p, 0);
- return 0;
-}
-
static int opt_input_ts_scale(const char *opt, const char *arg)
{
- unsigned int stream;
- double scale;
- char *p;
-
- stream = strtol(arg, &p, 0);
- if (*p)
- p++;
- scale= strtod(p, &p);
-
- ts_scale = grow_array(ts_scale, sizeof(*ts_scale), &nb_ts_scale, stream + 1);
- ts_scale[stream] = scale;
- return 0;
-}
-
-static int opt_recording_time(const char *opt, const char *arg)
-{
- recording_time = parse_time_or_die(opt, arg, 1);
- return 0;
-}
-
-static int opt_start_time(const char *opt, const char *arg)
-{
- start_time = parse_time_or_die(opt, arg, 1);
- return 0;
+ return av_dict_set(&ts_scale, opt, arg, 0);
}
- static int opt_recording_time(const char *opt, const char *arg)
- {
- recording_time = parse_time_or_die(opt, arg, 1);
- return 0;
- }
-
- static int opt_start_time(const char *opt, const char *arg)
- {
- start_time = parse_time_or_die(opt, arg, 1);
- return 0;
- }
-
static int opt_recording_timestamp(const char *opt, const char *arg)
{
char buf[128];
return 0;
}
--static int opt_input_ts_offset(const char *opt, const char *arg)
--{
-- input_ts_offset = parse_time_or_die(opt, arg, 1);
-- return 0;
--}
--
-static enum CodecID find_codec_or_die(const char *name, int type, int encoder)
+static enum CodecID find_codec_or_die(const char *name, enum AVMediaType type, int encoder)
{
const char *codec_string = encoder ? "encoder" : "decoder";
AVCodec *codec;
return codec->id;
}
-static int opt_input_file(const char *opt, const char *filename)
+static AVCodec *choose_codec(AVFormatContext *s, AVStream *st, enum AVMediaType type, AVDictionary *codec_names)
+{
+ AVDictionaryEntry *e = NULL;
+ char *codec_name = NULL;
+ int ret;
+
+ while (e = av_dict_get(codec_names, "", e, AV_DICT_IGNORE_SUFFIX)) {
+ char *p = strchr(e->key, ':');
+
+ if ((ret = check_stream_specifier(s, st, p ? p + 1 : "")) > 0)
+ codec_name = e->value;
+ else if (ret < 0)
+ exit_program(1);
+ }
+
+ if (!codec_name) {
+ if (s->oformat) {
+ st->codec->codec_id = av_guess_codec(s->oformat, NULL, s->filename, NULL, type);
+ return avcodec_find_encoder(st->codec->codec_id);
+ }
+ } else if (!strcmp(codec_name, "copy"))
+ st->stream_copy = 1;
+ else {
+ st->codec->codec_id = find_codec_or_die(codec_name, type, s->iformat == NULL);
+ return s->oformat ? avcodec_find_encoder_by_name(codec_name) :
+ avcodec_find_decoder_by_name(codec_name);
+ }
+
+ return NULL;
+}
+
+/**
+ * Add all the streams from the given input file to the global
+ * list of input streams.
+ */
+static void add_input_streams(AVFormatContext *ic)
+{
+ int i, rfps, rfps_base, ret;
+
+ for (i = 0; i < ic->nb_streams; i++) {
+ AVStream *st = ic->streams[i];
+ AVCodecContext *dec = st->codec;
+ AVDictionaryEntry *e = NULL;
+ InputStream *ist;
+ char *scale = NULL;
+
+ dec->thread_count = thread_count;
+
+ input_streams = grow_array(input_streams, sizeof(*input_streams), &nb_input_streams, nb_input_streams + 1);
+ ist = &input_streams[nb_input_streams - 1];
+ ist->st = st;
+ ist->file_index = nb_input_files;
+ ist->discard = 1;
+ ist->opts = filter_codec_opts(codec_opts, ist->st->codec->codec_id, ic, st);
+
+ while (e = av_dict_get(ts_scale, "", e, AV_DICT_IGNORE_SUFFIX)) {
+ char *p = strchr(e->key, ':');
+
+ if ((ret = check_stream_specifier(ic, st, p ? p + 1 : "")) > 0)
+ scale = e->value;
+ else if (ret < 0)
+ exit_program(1);
+ }
+ if (scale)
+ ist->ts_scale = strtod(scale, NULL);
+
+ ist->dec = choose_codec(ic, st, dec->codec_type, codec_names);
+ if (!ist->dec)
+ ist->dec = avcodec_find_decoder(dec->codec_id);
+
+ switch (dec->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ if(!ist->dec)
+ ist->dec = avcodec_find_decoder(dec->codec_id);
+ if(audio_disable)
+ st->discard= AVDISCARD_ALL;
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ if(!ist->dec)
+ ist->dec = avcodec_find_decoder(dec->codec_id);
+ rfps = ic->streams[i]->r_frame_rate.num;
+ rfps_base = ic->streams[i]->r_frame_rate.den;
+ if (dec->lowres) {
+ dec->flags |= CODEC_FLAG_EMU_EDGE;
+ }
+ if(me_threshold)
+ dec->debug |= FF_DEBUG_MV;
+
+ if (dec->time_base.den != rfps*dec->ticks_per_frame || dec->time_base.num != rfps_base) {
+
+ if (verbose >= 0)
+ fprintf(stderr,"\nSeems stream %d codec frame rate differs from container frame rate: %2.2f (%d/%d) -> %2.2f (%d/%d)\n",
+ i, (float)dec->time_base.den / dec->time_base.num, dec->time_base.den, dec->time_base.num,
+
+ (float)rfps / rfps_base, rfps, rfps_base);
+ }
+
+ if(video_disable)
+ st->discard= AVDISCARD_ALL;
+ else if(video_discard)
+ st->discard= video_discard;
+ break;
+ case AVMEDIA_TYPE_DATA:
+ break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if(!ist->dec)
+ ist->dec = avcodec_find_decoder(dec->codec_id);
+ if(subtitle_disable)
+ st->discard = AVDISCARD_ALL;
+ break;
+ case AVMEDIA_TYPE_ATTACHMENT:
+ case AVMEDIA_TYPE_UNKNOWN:
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
- static int opt_input_file(const char *opt, const char *filename)
++static int opt_input_file(OptionsContext *o, const char *opt, const char *filename)
{
AVFormatContext *ic;
AVInputFormat *file_iformat = NULL;
AVDictionary **opts;
int orig_nb_streams; // number of streams before avformat_find_stream_info
-- if (last_asked_format) {
-- if (!(file_iformat = av_find_input_format(last_asked_format))) {
-- fprintf(stderr, "Unknown input format: '%s'\n", last_asked_format);
++ if (o->format) {
++ if (!(file_iformat = av_find_input_format(o->format))) {
++ fprintf(stderr, "Unknown input format: '%s'\n", o->format);
exit_program(1);
}
-- last_asked_format = NULL;
}
if (!strcmp(filename, "-"))
exit_program(1);
}
-- timestamp = start_time;
++ timestamp = o->start_time;
/* add the stream start time */
if (ic->start_time != AV_NOPTS_VALUE)
timestamp += ic->start_time;
/* if seeking requested, we execute it */
-- if (start_time != 0) {
++ if (o->start_time != 0) {
ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
if (ret < 0) {
fprintf(stderr, "%s: could not seek to position %0.3f\n",
filename, (double)timestamp / AV_TIME_BASE);
}
-- /* reset seek info */
-- start_time = 0;
}
/* update the current parameters so that they match the one of the input stream */
input_files = grow_array(input_files, sizeof(*input_files), &nb_input_files, nb_input_files + 1);
input_files[nb_input_files - 1].ctx = ic;
input_files[nb_input_files - 1].ist_index = nb_input_streams - ic->nb_streams;
-- input_files[nb_input_files - 1].ts_offset = input_ts_offset - (copy_ts ? 0 : timestamp);
++ input_files[nb_input_files - 1].ts_offset = o->input_ts_offset - (copy_ts ? 0 : timestamp);
+ input_files[nb_input_files - 1].nb_streams = ic->nb_streams;
+ top_field_first = -1;
frame_rate = (AVRational){0, 0};
frame_pix_fmt = PIX_FMT_NONE;
frame_height = 0;
audio_sample_rate = 0;
audio_channels = 0;
audio_sample_fmt = AV_SAMPLE_FMT_NONE;
- av_freep(&ts_scale);
- nb_ts_scale = 0;
+ av_dict_free(&ts_scale);
- input_ts_offset = 0;
for (i = 0; i < orig_nb_streams; i++)
av_dict_free(&opts[i]);
- av_freep(&opts);
- av_freep(&video_codec_name);
- av_freep(&audio_codec_name);
- av_freep(&subtitle_codec_name);
- uninit_opts();
- init_opts();
+ av_freep(&opts);
+ av_dict_free(&codec_names);
- uninit_opts();
- init_opts();
++
++ reset_options(o);
return 0;
}
streamid_map[idx] = parse_number_or_die(opt, p, OPT_INT, 0, INT_MAX);
return 0;
}
- static int copy_chapters(int infile, int outfile)
+static int read_ffserver_streams(AVFormatContext *s, const char *filename)
+{
+ int i, err;
+ AVFormatContext *ic = NULL;
+
+ err = avformat_open_input(&ic, filename, NULL, NULL);
+ if (err < 0)
+ return err;
+ /* copy stream format */
+ for(i=0;i<ic->nb_streams;i++) {
+ AVStream *st;
+ OutputStream *ost;
+ AVCodec *codec;
+
+ codec = avcodec_find_encoder(ic->streams[i]->codec->codec_id);
+ ost = new_output_stream(s, codec->type);
+ st = ost->st;
+
+ // FIXME: a more elegant solution is needed
+ memcpy(st, ic->streams[i], sizeof(AVStream));
+ st->info = av_malloc(sizeof(*st->info));
+ memcpy(st->info, ic->streams[i]->info, sizeof(*st->info));
+ avcodec_copy_context(st->codec, ic->streams[i]->codec);
+
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && !st->stream_copy)
+ choose_sample_fmt(st, codec);
+ else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && !st->stream_copy)
+ choose_pixel_fmt(st, codec);
+ }
+
+ av_close_input_file(ic);
+ return 0;
+}
+
+
- AVFormatContext *is = input_files[infile].ctx;
- AVFormatContext *os = output_files[outfile].ctx;
++static int copy_chapters(InputFile *ifile, OutputFile *ofile)
+{
- int64_t ts_off = av_rescale_q(start_time - input_files[infile].ts_offset,
++ AVFormatContext *is = ifile->ctx;
++ AVFormatContext *os = ofile->ctx;
+ int i;
+
+ for (i = 0; i < is->nb_chapters; i++) {
+ AVChapter *in_ch = is->chapters[i], *out_ch;
- int64_t rt = (recording_time == INT64_MAX) ? INT64_MAX :
- av_rescale_q(recording_time, AV_TIME_BASE_Q, in_ch->time_base);
++ int64_t ts_off = av_rescale_q(ofile->start_time - ifile->ts_offset,
+ AV_TIME_BASE_Q, in_ch->time_base);
++ int64_t rt = (ofile->recording_time == INT64_MAX) ? INT64_MAX :
++ av_rescale_q(ofile->recording_time, AV_TIME_BASE_Q, in_ch->time_base);
+
+
+ if (in_ch->end < ts_off)
+ continue;
+ if (rt != INT64_MAX && in_ch->start > rt + ts_off)
+ break;
+
+ out_ch = av_mallocz(sizeof(AVChapter));
+ if (!out_ch)
+ return AVERROR(ENOMEM);
+
+ out_ch->id = in_ch->id;
+ out_ch->time_base = in_ch->time_base;
+ out_ch->start = FFMAX(0, in_ch->start - ts_off);
+ out_ch->end = FFMIN(rt, in_ch->end - ts_off);
+
+ if (metadata_chapters_autocopy)
+ av_dict_copy(&out_ch->metadata, in_ch->metadata, 0);
+
+ os->nb_chapters++;
+ os->chapters = av_realloc(os->chapters, sizeof(AVChapter)*os->nb_chapters);
+ if (!os->chapters)
+ return AVERROR(ENOMEM);
+ os->chapters[os->nb_chapters - 1] = out_ch;
+ }
+ return 0;
+}
- static int opt_output_file(const char *opt, const char *filename)
+ static void opt_output_file(void *optctx, const char *filename)
{
++ OptionsContext *o = optctx;
AVFormatContext *oc;
- int err, use_video, use_audio, use_subtitle, use_data;
- int input_has_video, input_has_audio, input_has_subtitle, input_has_data;
+ int i, err;
AVOutputFormat *file_oformat;
+ OutputStream *ost;
+ InputStream *ist;
if (!strcmp(filename, "-"))
filename = "pipe:";
- err = avformat_alloc_output_context2(&oc, NULL, last_asked_format, filename);
- last_asked_format = NULL;
- oc = avformat_alloc_context();
++ err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
if (!oc) {
- print_error(filename, AVERROR(ENOMEM));
+ print_error(filename, err);
exit_program(1);
}
-
- if (last_asked_format) {
- file_oformat = av_guess_format(last_asked_format, NULL, NULL);
- if (!file_oformat) {
- fprintf(stderr, "Requested output format '%s' is not a suitable output format\n", last_asked_format);
- exit_program(1);
- }
- last_asked_format = NULL;
- } else {
- file_oformat = av_guess_format(NULL, filename, NULL);
- if (!file_oformat) {
- fprintf(stderr, "Unable to find a suitable output format for '%s'\n",
- filename);
- exit_program(1);
- }
- }
-
- oc->oformat = file_oformat;
- av_strlcpy(oc->filename, filename, sizeof(oc->filename));
+ file_oformat= oc->oformat;
if (!strcmp(file_oformat->name, "ffm") &&
av_strstart(filename, "http:", NULL)) {
print_error(filename, err);
exit_program(1);
}
- } else if (!nb_stream_maps) {
++ } else if (!o->nb_stream_maps) {
+ /* pick the "best" stream of each type */
+#define NEW_STREAM(type, index)\
+ if (index >= 0) {\
+ ost = new_ ## type ## _stream(oc);\
+ ost->source_index = index;\
+ ost->sync_ist = &input_streams[index];\
+ input_streams[index].discard = 0;\
+ }
+
+ /* video: highest resolution */
+ if (!video_disable && oc->oformat->video_codec != CODEC_ID_NONE) {
+ int area = 0, idx = -1;
+ for (i = 0; i < nb_input_streams; i++) {
+ ist = &input_streams[i];
+ if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ ist->st->codec->width * ist->st->codec->height > area) {
+ area = ist->st->codec->width * ist->st->codec->height;
+ idx = i;
+ }
+ }
+ NEW_STREAM(video, idx);
+ }
+
+ /* audio: most channels */
+ if (!audio_disable && oc->oformat->audio_codec != CODEC_ID_NONE) {
+ int channels = 0, idx = -1;
+ for (i = 0; i < nb_input_streams; i++) {
+ ist = &input_streams[i];
+ if (ist->st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+ ist->st->codec->channels > channels) {
+ channels = ist->st->codec->channels;
+ idx = i;
+ }
+ }
+ NEW_STREAM(audio, idx);
+ }
+
+ /* subtitles: pick first */
+ if (!subtitle_disable && oc->oformat->subtitle_codec != CODEC_ID_NONE) {
+ for (i = 0; i < nb_input_streams; i++)
+ if (input_streams[i].st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ NEW_STREAM(subtitle, i);
+ break;
+ }
+ }
+ /* do something with data? */
} else {
- for (i = 0; i < nb_stream_maps; i++) {
- StreamMap *map = &stream_maps[i];
- use_video = file_oformat->video_codec != CODEC_ID_NONE || video_stream_copy || video_codec_name;
- use_audio = file_oformat->audio_codec != CODEC_ID_NONE || audio_stream_copy || audio_codec_name;
- use_subtitle = file_oformat->subtitle_codec != CODEC_ID_NONE || subtitle_stream_copy || subtitle_codec_name;
- use_data = data_stream_copy || data_codec_name; /* XXX once generic data codec will be available add a ->data_codec reference and use it here */
-
- /* disable if no corresponding type found */
- check_inputs(&input_has_video,
- &input_has_audio,
- &input_has_subtitle,
- &input_has_data);
-
- if (!input_has_video)
- use_video = 0;
- if (!input_has_audio)
- use_audio = 0;
- if (!input_has_subtitle)
- use_subtitle = 0;
- if (!input_has_data)
- use_data = 0;
-
- /* manual disable */
- if (audio_disable) use_audio = 0;
- if (video_disable) use_video = 0;
- if (subtitle_disable) use_subtitle = 0;
- if (data_disable) use_data = 0;
-
- if (use_video) new_video_stream(oc, nb_output_files);
- if (use_audio) new_audio_stream(oc, nb_output_files);
- if (use_subtitle) new_subtitle_stream(oc, nb_output_files);
- if (use_data) new_data_stream(oc, nb_output_files);
-
- av_dict_copy(&oc->metadata, metadata, 0);
- av_dict_free(&metadata);
++ for (i = 0; i < o->nb_stream_maps; i++) {
++ StreamMap *map = &o->stream_maps[i];
+
+ if (map->disabled)
+ continue;
+
+ ist = &input_streams[input_files[map->file_index].ist_index + map->stream_index];
+ switch (ist->st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(oc); break;
+ case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(oc); break;
+ case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(oc); break;
+ case AVMEDIA_TYPE_DATA: ost = new_data_stream(oc); break;
+ default:
+ av_log(NULL, AV_LOG_ERROR, "Cannot map stream #%d.%d - unsupported type.\n",
+ map->file_index, map->stream_index);
+ exit_program(1);
+ }
+
+ ost->source_index = input_files[map->file_index].ist_index + map->stream_index;
+ ost->sync_ist = &input_streams[input_files[map->sync_file_index].ist_index +
+ map->sync_stream_index];
+ ist->discard = 0;
+ }
}
- av_dict_copy(&output_opts[nb_output_files], format_opts, 0);
- output_files[nb_output_files++] = oc;
+ av_dict_copy(&oc->metadata, metadata, 0);
+ av_dict_free(&metadata);
+
+
+ output_files = grow_array(output_files, sizeof(*output_files), &nb_output_files, nb_output_files + 1);
+ output_files[nb_output_files - 1].ctx = oc;
+ output_files[nb_output_files - 1].ost_index = nb_output_streams - oc->nb_streams;
- output_files[nb_output_files - 1].recording_time = recording_time;
- output_files[nb_output_files - 1].start_time = start_time;
- output_files[nb_output_files - 1].limit_filesize = limit_filesize;
++ output_files[nb_output_files - 1].recording_time = o->recording_time;
++ output_files[nb_output_files - 1].start_time = o->start_time;
++ output_files[nb_output_files - 1].limit_filesize = o->limit_filesize;
+ av_dict_copy(&output_files[nb_output_files - 1].opts, format_opts, 0);
/* check filename in case of an image number is expected */
if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
av_log(NULL, AV_LOG_WARNING, "-loop_output is deprecated, use -loop\n");
oc->loop_output = loop_output;
}
- oc->flags |= AVFMT_FLAG_NONBLOCK;
+
+ /* copy chapters */
+ if (chapters_input_file >= nb_input_files) {
+ if (chapters_input_file == INT_MAX) {
+ /* copy chapters from the first input file that has them*/
+ chapters_input_file = -1;
+ for (i = 0; i < nb_input_files; i++)
+ if (input_files[i].ctx->nb_chapters) {
+ chapters_input_file = i;
+ break;
+ }
+ } else {
+ av_log(NULL, AV_LOG_ERROR, "Invalid input file index %d in chapter mapping.\n",
+ chapters_input_file);
+ exit_program(1);
+ }
+ }
+ if (chapters_input_file >= 0)
- copy_chapters(chapters_input_file, nb_output_files - 1);
++ copy_chapters(&input_files[chapters_input_file], &output_files[nb_output_files - 1]);
+
+ /* copy metadata */
+ for (i = 0; i < nb_meta_data_maps; i++) {
+ AVFormatContext *files[2];
+ AVDictionary **meta[2];
+ int j;
+
+#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
+ if ((index) < 0 || (index) >= (nb_elems)) {\
+ av_log(NULL, AV_LOG_ERROR, "Invalid %s index %d while processing metadata maps\n",\
+ (desc), (index));\
+ exit_program(1);\
+ }
+
+ int in_file_index = meta_data_maps[i][1].file;
+ if (in_file_index < 0)
+ continue;
+ METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file")
+
+ files[0] = oc;
+ files[1] = input_files[in_file_index].ctx;
+
+ for (j = 0; j < 2; j++) {
+ MetadataMap *map = &meta_data_maps[i][j];
+
+ switch (map->type) {
+ case 'g':
+ meta[j] = &files[j]->metadata;
+ break;
+ case 's':
+ METADATA_CHECK_INDEX(map->index, files[j]->nb_streams, "stream")
+ meta[j] = &files[j]->streams[map->index]->metadata;
+ break;
+ case 'c':
+ METADATA_CHECK_INDEX(map->index, files[j]->nb_chapters, "chapter")
+ meta[j] = &files[j]->chapters[map->index]->metadata;
+ break;
+ case 'p':
+ METADATA_CHECK_INDEX(map->index, files[j]->nb_programs, "program")
+ meta[j] = &files[j]->programs[map->index]->metadata;
+ break;
+ }
+ }
+
+ av_dict_copy(meta[0], *meta[1], AV_DICT_DONT_OVERWRITE);
+ }
+
+ /* copy global metadata by default */
+ if (metadata_global_autocopy && nb_input_files)
+ av_dict_copy(&oc->metadata, input_files[0].ctx->metadata,
+ AV_DICT_DONT_OVERWRITE);
+ if (metadata_streams_autocopy)
+ for (i = output_files[nb_output_files - 1].ost_index; i < nb_output_streams; i++) {
+ InputStream *ist = &input_streams[output_streams[i].source_index];
+ av_dict_copy(&output_streams[i].st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
+ }
frame_rate = (AVRational){0, 0};
frame_width = 0;
audio_sample_rate = 0;
audio_channels = 0;
audio_sample_fmt = AV_SAMPLE_FMT_NONE;
- recording_time = INT64_MAX;
- start_time = 0;
- limit_filesize = UINT64_MAX;
+ chapters_input_file = INT_MAX;
- av_freep(&stream_maps);
- nb_stream_maps = 0;
+
+ av_freep(&meta_data_maps);
+ nb_meta_data_maps = 0;
+ metadata_global_autocopy = 1;
+ metadata_streams_autocopy = 1;
+ metadata_chapters_autocopy = 1;
+ av_freep(&streamid_map);
+ nb_streamid_map = 0;
+
+ av_dict_free(&codec_names);
av_freep(&forced_key_frames);
-- uninit_opts();
-- init_opts();
- return 0;
++ reset_options(o);
}
/* same option as mencoder */
class = sws_get_class();
av_opt_show2(&class, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0);
+ return 0;
}
--static int opt_target(const char *opt, const char *arg)
++static int opt_target(OptionsContext *o, const char *opt, const char *arg)
{
enum { PAL, NTSC, FILM, UNKNOWN } norm = UNKNOWN;
static const char *const frame_rates[] = {"25", "30000/1001", "24000/1001"};
}
if(!strcmp(arg, "vcd")) {
- opt_video_codec("vcodec", "mpeg1video");
- opt_audio_codec("vcodec", "mp2");
- opt_format("f", "vcd");
+ opt_codec("c:v", "mpeg1video");
+ opt_codec("c:a", "mp2");
- opt_format("f", "vcd");
++ parse_option(o, "f", "vcd", options);
opt_frame_size("s", norm == PAL ? "352x288" : "352x240");
opt_frame_rate("r", frame_rates[norm]);
mux_preload= (36000+3*1200) / 90000.0; //0.44
} else if(!strcmp(arg, "svcd")) {
- opt_video_codec("vcodec", "mpeg2video");
- opt_audio_codec("acodec", "mp2");
- opt_format("f", "svcd");
+ opt_codec("c:v", "mpeg2video");
+ opt_codec("c:a", "mp2");
- opt_format("f", "svcd");
++ parse_option(o, "f", "svcd", options);
opt_frame_size("s", norm == PAL ? "480x576" : "480x480");
opt_frame_rate("r", frame_rates[norm]);
} else if(!strcmp(arg, "dvd")) {
- opt_video_codec("vcodec", "mpeg2video");
- opt_audio_codec("vcodec", "ac3");
- opt_format("f", "dvd");
+ opt_codec("c:v", "mpeg2video");
+ opt_codec("c:a", "ac3");
- opt_format("f", "dvd");
++ parse_option(o, "f", "dvd", options);
opt_frame_size("vcodec", norm == PAL ? "720x576" : "720x480");
opt_frame_rate("r", frame_rates[norm]);
} else if(!strncmp(arg, "dv", 2)) {
-- opt_format("f", "dv");
++ parse_option(o, "f", "dv", options);
opt_frame_size("s", norm == PAL ? "720x576" : "720x480");
opt_frame_pix_fmt("pix_fmt", !strncmp(arg, "dv50", 4) ? "yuv422p" :
return 0;
}
+static void log_callback_null(void* ptr, int level, const char* fmt, va_list vl)
+{
+}
+
+static int opt_passlogfile(const char *opt, const char *arg)
+{
+ pass_logfilename_prefix = arg;
+#if CONFIG_LIBX264_ENCODER
+ return opt_default("passlogfile", arg);
+#else
+ return 0;
+#endif
+}
+
++#define OFFSET(x) offsetof(OptionsContext, x)
static const OptionDef options[] = {
/* main options */
#include "cmdutils_common_opts.h"
-- { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" },
-- { "i", HAS_ARG, {(void*)opt_input_file}, "input file name", "filename" },
++ { "f", HAS_ARG | OPT_STRING | OPT_OFFSET, {.off = OFFSET(format)}, "force format", "fmt" },
++ { "i", HAS_ARG | OPT_FUNC2, {(void*)opt_input_file}, "input file name", "filename" },
{ "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" },
- { "map", HAS_ARG | OPT_EXPERT, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" },
+ { "c", HAS_ARG, {(void*)opt_codec}, "codec name", "codec" },
+ { "codec", HAS_ARG, {(void*)opt_codec}, "codec name", "codec" },
- { "map", HAS_ARG | OPT_EXPERT, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" },
++ { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" },
{ "map_meta_data", HAS_ARG | OPT_EXPERT, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile",
"outfile[,metadata]:infile[,metadata]" },
{ "map_metadata", HAS_ARG | OPT_EXPERT, {(void*)opt_map_metadata}, "set metadata information of outfile from infile",
"outfile[,metadata]:infile[,metadata]" },
- { "map_chapters", HAS_ARG | OPT_EXPERT, {(void*)opt_map_chapters}, "set chapters mapping", "outfile:infile" },
- { "t", HAS_ARG, {(void*)opt_recording_time}, "record or transcode \"duration\" seconds of audio/video", "duration" },
- { "fs", HAS_ARG | OPT_INT64, {(void*)&limit_filesize}, "set the limit file size in bytes", "limit_size" }, //
- { "ss", HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" },
- { "itsoffset", HAS_ARG, {(void*)opt_input_ts_offset}, "set the input ts offset", "time_off" },
- { "itsscale", HAS_ARG, {(void*)opt_input_ts_scale}, "set the input ts scale", "stream:scale" },
+ { "map_chapters", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&chapters_input_file}, "set chapters mapping", "input_file_index" },
- { "t", HAS_ARG, {(void*)opt_recording_time}, "record or transcode \"duration\" seconds of audio/video", "duration" },
- { "fs", HAS_ARG | OPT_INT64, {(void*)&limit_filesize}, "set the limit file size in bytes", "limit_size" }, //
- { "ss", HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" },
- { "itsoffset", HAS_ARG, {(void*)opt_input_ts_offset}, "set the input ts offset", "time_off" },
++ { "t", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(recording_time)}, "record or transcode \"duration\" seconds of audio/video", "duration" },
++ { "fs", HAS_ARG | OPT_INT64 | OPT_OFFSET, {.off = OFFSET(limit_filesize)}, "set the limit file size in bytes", "limit_size" }, //
++ { "ss", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(start_time)}, "set the start time offset", "time_off" },
++ { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(input_ts_offset)}, "set the input ts offset", "time_off" },
+ { "itsscale", HAS_ARG, {(void*)opt_input_ts_scale}, "set the input ts scale", "scale" },
{ "timestamp", HAS_ARG, {(void*)opt_recording_timestamp}, "set the recording timestamp ('now' to set the current time)", "time" },
{ "metadata", HAS_ARG, {(void*)opt_metadata}, "add metadata", "string=string" },
{ "dframes", OPT_INT | HAS_ARG, {(void*)&max_frames[AVMEDIA_TYPE_DATA]}, "set the number of data frames to record", "number" },
{ "re", OPT_BOOL | OPT_EXPERT, {(void*)&rate_emu}, "read input at native frame rate", "" },
{ "loop_input", OPT_BOOL | OPT_EXPERT, {(void*)&loop_input}, "deprecated, use -loop" },
{ "loop_output", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&loop_output}, "deprecated, use -loop", "" },
- { "v", HAS_ARG, {(void*)opt_verbose}, "set ffmpeg verbosity level", "number" },
- { "target", HAS_ARG, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"dv50\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" },
+ { "v", HAS_ARG, {(void*)opt_verbose}, "set the verbosity level", "number" },
- { "target", HAS_ARG, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"dv50\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" },
++ { "target", HAS_ARG | OPT_FUNC2, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"dv50\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" },
{ "threads", HAS_ARG | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" },
{ "vsync", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&video_sync_method}, "video sync method", "" },
{ "async", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&audio_sync_method}, "audio sync method", "" },
int main(int argc, char **argv)
{
++ OptionsContext o = { 0 };
int64_t ti;
++ reset_options(&o);
++
av_log_set_flags(AV_LOG_SKIP_REPEATED);
+ if(argc>1 && !strcmp(argv[1], "-d")){
+ run_as_daemon=1;
+ verbose=-1;
+ av_log_set_callback(log_callback_null);
+ argc--;
+ argv++;
+ }
+
avcodec_register_all();
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
av_register_all();
- avio_set_interrupt_cb(decode_interrupt_cb);
-
- init_opts();
-
- show_banner();
+#if HAVE_ISATTY
+ if(isatty(STDIN_FILENO))
+ avio_set_interrupt_cb(decode_interrupt_cb);
+#endif
- init_opts();
-
- av_log(NULL, AV_LOG_WARNING, "This program is not developed anymore and is only "
- "provided for compatibility. Use avconv instead "
- "(see Changelog for the list of incompatible changes).\n");
+ if(verbose>=0)
+ show_banner();
/* parse options */
- parse_options(argc, argv, options, opt_output_file);
- parse_options(NULL, argc, argv, options, opt_output_file);
++ parse_options(&o, argc, argv, options, opt_output_file);
if(nb_output_files <= 0 && nb_input_files == 0) {
show_usage();
--- /dev/null
- static int opt_input_file(const char *opt, const char *arg)
+/*
+ * ffprobe : Simple Media Prober based on the FFmpeg libraries
+ * Copyright (c) 2007-2010 Stefano Sabatini
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "libavformat/avformat.h"
+#include "libavcodec/avcodec.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/dict.h"
+#include "libavdevice/avdevice.h"
+#include "cmdutils.h"
+
+const char program_name[] = "ffprobe";
+const int program_birth_year = 2007;
+
+static int do_show_format = 0;
+static int do_show_packets = 0;
+static int do_show_streams = 0;
+
+static int show_value_unit = 0;
+static int use_value_prefix = 0;
+static int use_byte_value_binary_prefix = 0;
+static int use_value_sexagesimal_format = 0;
+
+static char *print_format;
+
+/* globals */
+static const OptionDef options[];
+
+/* FFprobe context */
+static const char *input_filename;
+static AVInputFormat *iformat = NULL;
+
+static const char *binary_unit_prefixes [] = { "", "Ki", "Mi", "Gi", "Ti", "Pi" };
+static const char *decimal_unit_prefixes[] = { "", "K" , "M" , "G" , "T" , "P" };
+
+static const char *unit_second_str = "s" ;
+static const char *unit_hertz_str = "Hz" ;
+static const char *unit_byte_str = "byte" ;
+static const char *unit_bit_per_second_str = "bit/s";
+
++void exit_program(int ret)
++{
++ exit(ret);
++}
++
+static char *value_string(char *buf, int buf_size, double val, const char *unit)
+{
+ if (unit == unit_second_str && use_value_sexagesimal_format) {
+ double secs;
+ int hours, mins;
+ secs = val;
+ mins = (int)secs / 60;
+ secs = secs - mins * 60;
+ hours = mins / 60;
+ mins %= 60;
+ snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
+ } else if (use_value_prefix) {
+ const char *prefix_string;
+ int index;
+
+ if (unit == unit_byte_str && use_byte_value_binary_prefix) {
+ index = (int) (log(val)/log(2)) / 10;
+ index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) -1);
+ val /= pow(2, index*10);
+ prefix_string = binary_unit_prefixes[index];
+ } else {
+ index = (int) (log10(val)) / 3;
+ index = av_clip(index, 0, FF_ARRAY_ELEMS(decimal_unit_prefixes) -1);
+ val /= pow(10, index*3);
+ prefix_string = decimal_unit_prefixes[index];
+ }
+
+ snprintf(buf, buf_size, "%.3f%s%s%s", val, prefix_string || show_value_unit ? " " : "",
+ prefix_string, show_value_unit ? unit : "");
+ } else {
+ snprintf(buf, buf_size, "%f%s%s", val, show_value_unit ? " " : "",
+ show_value_unit ? unit : "");
+ }
+
+ return buf;
+}
+
+static char *time_value_string(char *buf, int buf_size, int64_t val, const AVRational *time_base)
+{
+ if (val == AV_NOPTS_VALUE) {
+ snprintf(buf, buf_size, "N/A");
+ } else {
+ value_string(buf, buf_size, val * av_q2d(*time_base), unit_second_str);
+ }
+
+ return buf;
+}
+
+static char *ts_value_string (char *buf, int buf_size, int64_t ts)
+{
+ if (ts == AV_NOPTS_VALUE) {
+ snprintf(buf, buf_size, "N/A");
+ } else {
+ snprintf(buf, buf_size, "%"PRId64, ts);
+ }
+
+ return buf;
+}
+
+static const char *media_type_string(enum AVMediaType media_type)
+{
+ const char *s = av_get_media_type_string(media_type);
+ return s ? s : "unknown";
+}
+
+
+struct writer {
+ const char *name;
+ const char *item_sep; ///< separator between key/value couples
+ const char *items_sep; ///< separator between sets of key/value couples
+ const char *section_sep; ///< separator between sections (streams, packets, ...)
+ const char *header, *footer;
+ void (*print_header)(const char *);
+ void (*print_footer)(const char *);
+ void (*print_fmt_f)(const char *, const char *, ...);
+ void (*print_int_f)(const char *, int);
+ void (*show_tags)(struct writer *w, AVDictionary *dict);
+};
+
+
+/* Default output */
+
+static void default_print_header(const char *section)
+{
+ printf("[%s]\n", section);
+}
+
+static void default_print_fmt(const char *key, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ printf("%s=", key);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static void default_print_int(const char *key, int value)
+{
+ printf("%s=%d", key, value);
+}
+
+static void default_print_footer(const char *section)
+{
+ printf("\n[/%s]", section);
+}
+
+
+/* Print helpers */
+
+#define print_fmt0(k, f, a...) w->print_fmt_f(k, f, ##a)
+#define print_fmt( k, f, a...) do { \
+ if (w->item_sep) \
+ printf("%s", w->item_sep); \
+ w->print_fmt_f(k, f, ##a); \
+} while (0)
+
+#define print_int0(k, v) w->print_int_f(k, v)
+#define print_int( k, v) do { \
+ if (w->item_sep) \
+ printf("%s", w->item_sep); \
+ print_int0(k, v); \
+} while (0)
+
+#define print_str0(k, v) print_fmt0(k, "%s", v)
+#define print_str( k, v) print_fmt (k, "%s", v)
+
+
+static void show_packet(struct writer *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
+{
+ char val_str[128];
+ AVStream *st = fmt_ctx->streams[pkt->stream_index];
+
+ if (packet_idx)
+ printf("%s", w->items_sep);
+ w->print_header("PACKET");
+ print_str0("codec_type", media_type_string(st->codec->codec_type));
+ print_int("stream_index", pkt->stream_index);
+ print_str("pts", ts_value_string (val_str, sizeof(val_str), pkt->pts));
+ print_str("pts_time", time_value_string(val_str, sizeof(val_str), pkt->pts, &st->time_base));
+ print_str("dts", ts_value_string (val_str, sizeof(val_str), pkt->dts));
+ print_str("dts_time", time_value_string(val_str, sizeof(val_str), pkt->dts, &st->time_base));
+ print_str("duration", ts_value_string (val_str, sizeof(val_str), pkt->duration));
+ print_str("duration_time", time_value_string(val_str, sizeof(val_str), pkt->duration, &st->time_base));
+ print_str("size", value_string (val_str, sizeof(val_str), pkt->size, unit_byte_str));
+ print_fmt("pos", "%"PRId64, pkt->pos);
+ print_fmt("flags", "%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
+ w->print_footer("PACKET");
+ fflush(stdout);
+}
+
+static void show_packets(struct writer *w, AVFormatContext *fmt_ctx)
+{
+ AVPacket pkt;
+ int i = 0;
+
+ av_init_packet(&pkt);
+
+ while (!av_read_frame(fmt_ctx, &pkt))
+ show_packet(w, fmt_ctx, &pkt, i++);
+}
+
+static void default_show_tags(struct writer *w, AVDictionary *dict)
+{
+ AVDictionaryEntry *tag = NULL;
+ while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+ printf("\nTAG:");
+ print_str0(tag->key, tag->value);
+ }
+}
+
+static void show_stream(struct writer *w, AVFormatContext *fmt_ctx, int stream_idx)
+{
+ AVStream *stream = fmt_ctx->streams[stream_idx];
+ AVCodecContext *dec_ctx;
+ AVCodec *dec;
+ char val_str[128];
+ AVRational display_aspect_ratio;
+
+ if (stream_idx)
+ printf("%s", w->items_sep);
+ w->print_header("STREAM");
+
+ print_int0("index", stream->index);
+
+ if ((dec_ctx = stream->codec)) {
+ if ((dec = dec_ctx->codec)) {
+ print_str("codec_name", dec->name);
+ print_str("codec_long_name", dec->long_name);
+ } else {
+ print_str("codec_name", "unknown");
+ }
+
+ print_str("codec_type", media_type_string(dec_ctx->codec_type));
+ print_fmt("codec_time_base", "%d/%d", dec_ctx->time_base.num, dec_ctx->time_base.den);
+
+ /* print AVI/FourCC tag */
+ av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
+ print_str("codec_tag_string", val_str);
+ print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);
+
+ switch (dec_ctx->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ print_int("width", dec_ctx->width);
+ print_int("height", dec_ctx->height);
+ print_int("has_b_frames", dec_ctx->has_b_frames);
+ if (dec_ctx->sample_aspect_ratio.num) {
+ print_fmt("sample_aspect_ratio", "%d:%d",
+ dec_ctx->sample_aspect_ratio.num,
+ dec_ctx->sample_aspect_ratio.den);
+ av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
+ dec_ctx->width * dec_ctx->sample_aspect_ratio.num,
+ dec_ctx->height * dec_ctx->sample_aspect_ratio.den,
+ 1024*1024);
+ print_fmt("display_aspect_ratio", "%d:%d",
+ display_aspect_ratio.num,
+ display_aspect_ratio.den);
+ }
+ print_str("pix_fmt", dec_ctx->pix_fmt != PIX_FMT_NONE ? av_pix_fmt_descriptors[dec_ctx->pix_fmt].name : "unknown");
+ print_int("level", dec_ctx->level);
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ print_str("sample_rate", value_string(val_str, sizeof(val_str), dec_ctx->sample_rate, unit_hertz_str));
+ print_int("channels", dec_ctx->channels);
+ print_int("bits_per_sample", av_get_bits_per_sample(dec_ctx->codec_id));
+ break;
+ }
+ } else {
+ print_fmt("codec_type", "unknown");
+ }
+
+ if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS)
+ print_fmt("id=", "0x%x", stream->id);
+ print_fmt("r_frame_rate", "%d/%d", stream->r_frame_rate.num, stream->r_frame_rate.den);
+ print_fmt("avg_frame_rate", "%d/%d", stream->avg_frame_rate.num, stream->avg_frame_rate.den);
+ print_fmt("time_base", "%d/%d", stream->time_base.num, stream->time_base.den);
+ print_str("start_time", time_value_string(val_str, sizeof(val_str), stream->start_time, &stream->time_base));
+ print_str("duration", time_value_string(val_str, sizeof(val_str), stream->duration, &stream->time_base));
+ if (stream->nb_frames)
+ print_fmt("nb_frames", "%"PRId64, stream->nb_frames);
+
+ w->show_tags(w, stream->metadata);
+
+ w->print_footer("STREAM");
+ fflush(stdout);
+}
+
+static void show_streams(struct writer *w, AVFormatContext *fmt_ctx)
+{
+ int i;
+ for (i = 0; i < fmt_ctx->nb_streams; i++)
+ show_stream(w, fmt_ctx, i);
+}
+
+static void show_format(struct writer *w, AVFormatContext *fmt_ctx)
+{
+ char val_str[128];
+
+ w->print_header("FORMAT");
+ print_str0("filename", fmt_ctx->filename);
+ print_int("nb_streams", fmt_ctx->nb_streams);
+ print_str("format_name", fmt_ctx->iformat->name);
+ print_str("format_long_name", fmt_ctx->iformat->long_name);
+ print_str("start_time", time_value_string(val_str, sizeof(val_str), fmt_ctx->start_time, &AV_TIME_BASE_Q));
+ print_str("duration", time_value_string(val_str, sizeof(val_str), fmt_ctx->duration, &AV_TIME_BASE_Q));
+ print_str("size", value_string(val_str, sizeof(val_str), fmt_ctx->file_size, unit_byte_str));
+ print_str("bit_rate", value_string(val_str, sizeof(val_str), fmt_ctx->bit_rate, unit_bit_per_second_str));
+ w->show_tags(w, fmt_ctx->metadata);
+ w->print_footer("FORMAT");
+ fflush(stdout);
+}
+
+static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
+{
+ int err, i;
+ AVFormatContext *fmt_ctx = NULL;
+ AVDictionaryEntry *t;
+
+ if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0) {
+ print_error(filename, err);
+ return err;
+ }
+ if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
+ av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
+ return AVERROR_OPTION_NOT_FOUND;
+ }
+
+
+ /* fill the streams in the format context */
+ if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
+ print_error(filename, err);
+ return err;
+ }
+
+ av_dump_format(fmt_ctx, 0, filename, 0);
+
+ /* bind a decoder to each input stream */
+ for (i = 0; i < fmt_ctx->nb_streams; i++) {
+ AVStream *stream = fmt_ctx->streams[i];
+ AVCodec *codec;
+
+ if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
+ fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
+ stream->codec->codec_id, stream->index);
+ } else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
+ fprintf(stderr, "Error while opening codec for input stream %d\n",
+ stream->index);
+ }
+ }
+
+ *fmt_ctx_ptr = fmt_ctx;
+ return 0;
+}
+
+#define WRITER_FUNC(func) \
+ .print_header = func ## _print_header, \
+ .print_footer = func ## _print_footer, \
+ .print_fmt_f = func ## _print_fmt, \
+ .print_int_f = func ## _print_int, \
+ .show_tags = func ## _show_tags
+
+static struct writer writers[] = {{
+ .name = "default",
+ .item_sep = "\n",
+ .items_sep = "\n",
+ .section_sep = "\n",
+ .footer = "\n",
+ WRITER_FUNC(default),
+ }
+};
+
+static int get_writer(const char *name)
+{
+ int i;
+ if (!name)
+ return 0;
+ for (i = 0; i < FF_ARRAY_ELEMS(writers); i++)
+ if (!strcmp(writers[i].name, name))
+ return i;
+ return -1;
+}
+
+#define SECTION_PRINT(name, left) do { \
+ if (do_show_ ## name) { \
+ show_ ## name (w, fmt_ctx); \
+ if (left) \
+ printf("%s", w->section_sep); \
+ } \
+} while (0)
+
+static int probe_file(const char *filename)
+{
+ AVFormatContext *fmt_ctx;
+ int ret, writer_id;
+ struct writer *w;
+
+ writer_id = get_writer(print_format);
+ if (writer_id < 0) {
+ fprintf(stderr, "Invalid output format '%s'\n", print_format);
+ return AVERROR(EINVAL);
+ }
+ w = &writers[writer_id];
+
+ if ((ret = open_input_file(&fmt_ctx, filename)))
+ return ret;
+
+ if (w->header)
+ printf("%s", w->header);
+
+ SECTION_PRINT(packets, do_show_streams || do_show_format);
+ SECTION_PRINT(streams, do_show_format);
+ SECTION_PRINT(format, 0);
+
+ if (w->footer)
+ printf("%s", w->footer);
+
+ av_close_input_file(fmt_ctx);
+ return 0;
+}
+
+static void show_usage(void)
+{
+ printf("Simple multimedia streams analyzer\n");
+ printf("usage: %s [OPTIONS] [INPUT_FILE]\n", program_name);
+ printf("\n");
+}
+
+static int opt_format(const char *opt, const char *arg)
+{
+ iformat = av_find_input_format(arg);
+ if (!iformat) {
+ fprintf(stderr, "Unknown input format: %s\n", arg);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
- return 0;
++static void opt_input_file(void *optctx, const char *arg)
+{
+ if (input_filename) {
+ fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n",
+ arg, input_filename);
+ exit(1);
+ }
+ if (!strcmp(arg, "-"))
+ arg = "pipe:";
+ input_filename = arg;
- parse_options(argc, argv, options, opt_input_file);
+}
+
+static int opt_help(const char *opt, const char *arg)
+{
+ const AVClass *class = avformat_get_class();
+ av_log_set_callback(log_callback_help);
+ show_usage();
+ show_help_options(options, "Main options:\n", 0, 0);
+ printf("\n");
+ av_opt_show2(&class, NULL,
+ AV_OPT_FLAG_DECODING_PARAM, 0);
+ return 0;
+}
+
+static int opt_pretty(const char *opt, const char *arg)
+{
+ show_value_unit = 1;
+ use_value_prefix = 1;
+ use_byte_value_binary_prefix = 1;
+ use_value_sexagesimal_format = 1;
+ return 0;
+}
+
+static const OptionDef options[] = {
+#include "cmdutils_common_opts.h"
+ { "f", HAS_ARG, {(void*)opt_format}, "force format", "format" },
+ { "unit", OPT_BOOL, {(void*)&show_value_unit}, "show unit of the displayed values" },
+ { "prefix", OPT_BOOL, {(void*)&use_value_prefix}, "use SI prefixes for the displayed values" },
+ { "byte_binary_prefix", OPT_BOOL, {(void*)&use_byte_value_binary_prefix},
+ "use binary prefixes for byte units" },
+ { "sexagesimal", OPT_BOOL, {(void*)&use_value_sexagesimal_format},
+ "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
+ { "pretty", 0, {(void*)&opt_pretty},
+ "prettify the format of displayed values, make it more human readable" },
+ { "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
+ { "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" },
+ { "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" },
+ { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
+ { "i", HAS_ARG, {(void *)opt_input_file}, "read specified file", "input_file"},
+ { NULL, },
+};
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ av_register_all();
+ init_opts();
+#if CONFIG_AVDEVICE
+ avdevice_register_all();
+#endif
+
+ show_banner();
++ parse_options(NULL, argc, argv, options, opt_input_file);
+
+ if (!input_filename) {
+ show_usage();
+ fprintf(stderr, "You have to specify one input file.\n");
+ fprintf(stderr, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
+ exit(1);
+ }
+
+ ret = probe_file(input_filename);
+
+ return ret;
+}