Newer
Older
* 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 <stdint.h>
#include "cmdutils.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/avutil.h"
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/fifo.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixfmt.h"
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\
int i, ret;\
for (i = 0; i < o->nb_ ## name; i++) {\
char *spec = o->name[i].specifier;\
if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\
outvar = o->name[i].u.type;\
else if (ret < 0)\
#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
{\
for (i = 0; i < o->nb_ ## name; i++) {\
char *spec = o->name[i].specifier;\
if (!strcmp(spec, mediatype))\
outvar = o->name[i].u.type;\
}\
}
const HWAccel hwaccels[] = {
#if HAVE_VDPAU_X11
{ "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
{ "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD },
#endif
#if CONFIG_VDA
{ "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA },
char *vstats_filename;
float audio_drift_threshold = 0.1;
float dts_delta_threshold = 10;
float dts_error_threshold = 3600*30;
int audio_volume = 256;
int audio_sync_method = 0;
int video_sync_method = VSYNC_AUTO;
int do_deinterlace = 0;
int do_benchmark = 0;
int do_benchmark_all = 0;
int do_hex_dump = 0;
int do_pkt_dump = 0;
int copy_ts = 0;
int copy_tb = -1;
int debug_ts = 0;
int exit_on_error = 0;
int stdin_interaction = 1;
int frame_bits_per_raw_sample = 0;
Michael Niedermayer
committed
float max_error_rate = 2.0/3;
static int intra_only = 0;
static int file_overwrite = 0;
static int no_file_overwrite = 0;
static int do_psnr = 0;
static int input_sync;
static int override_ffserver = 0;
Michael Niedermayer
committed
static void uninit_options(OptionsContext *o)
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{
const OptionDef *po = options;
int i;
/* 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++;
}
for (i = 0; i < o->nb_stream_maps; i++)
av_freep(&o->stream_maps[i].linklabel);
av_freep(&o->stream_maps);
av_freep(&o->audio_channel_maps);
av_freep(&o->streamid_map);
Michael Niedermayer
committed
static void init_options(OptionsContext *o)
memset(o, 0, sizeof(*o));
o->mux_max_delay = 0.7;
o->start_time = AV_NOPTS_VALUE;
Michael Niedermayer
committed
o->recording_time = INT64_MAX;
o->limit_filesize = UINT64_MAX;
o->chapters_input_file = INT_MAX;
/* return a copy of the input with the stream specifiers removed from the keys */
static AVDictionary *strip_specifiers(AVDictionary *dict)
{
AVDictionaryEntry *e = NULL;
AVDictionary *ret = NULL;
while ((e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
char *p = strchr(e->key, ':');
if (p)
*p = 0;
av_dict_set(&ret, e->key, e->value, 0);
if (p)
*p = ':';
}
return ret;
}
static int opt_sameq(void *optctx, const char *opt, const char *arg)
{
Stefano Sabatini
committed
av_log(NULL, AV_LOG_ERROR, "Option '%s' was removed. "
"If you are looking for an option to preserve the quality (which is not "
"what -%s was for), use -qscale 0 or an equivalent quality factor option.\n",
opt, opt);
return AVERROR(EINVAL);
static int opt_video_channel(void *optctx, const char *opt, const char *arg)
{
av_log(NULL, AV_LOG_WARNING, "This option is deprecated, use -channel.\n");
return opt_default(optctx, "channel", arg);
static int opt_video_standard(void *optctx, const char *opt, const char *arg)
{
av_log(NULL, AV_LOG_WARNING, "This option is deprecated, use -standard.\n");
return opt_default(optctx, "standard", arg);
static int opt_audio_codec(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
return parse_option(o, "codec:a", arg, options);
}
static int opt_video_codec(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
return parse_option(o, "codec:v", arg, options);
}
static int opt_subtitle_codec(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
return parse_option(o, "codec:s", arg, options);
}
static int opt_data_codec(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
return parse_option(o, "codec:d", arg, options);
}
static int opt_map(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
StreamMap *m = NULL;
int i, negative = 0, file_idx;
int sync_file_idx = -1, sync_stream_idx = 0;
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_FATAL, "Invalid sync file index: %d.\n", sync_file_idx);
}
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_FATAL, "Sync stream specification in map %s does not "
"match any streams.\n", arg);
}
}
if (map[0] == '[') {
/* this mapping refers to lavfi output */
const char *c = map + 1;
GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
m = &o->stream_maps[o->nb_stream_maps - 1];
m->linklabel = av_get_token(&c, "]");
if (!m->linklabel) {
av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", map);
}
} else {
file_idx = strtol(map, &p, 0);
if (file_idx >= nb_input_files || file_idx < 0) {
av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", file_idx);
}
if (negative)
/* disable some already defined maps */
for (i = 0; i < o->nb_stream_maps; i++) {
m = &o->stream_maps[i];
if (file_idx == m->file_index &&
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;
GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
m = &o->stream_maps[o->nb_stream_maps - 1];
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_FATAL, "Stream map '%s' matches no streams.\n", arg);
}
av_freep(&map);
return 0;
}
static int opt_attach(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
GROW_ARRAY(o->attachments, o->nb_attachments);
o->attachments[o->nb_attachments - 1] = arg;
return 0;
}
static int opt_map_channel(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
int n;
AVStream *st;
AudioChannelMap *m;
GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps);
m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1];
/* muted channel syntax */
n = sscanf(arg, "%d:%d.%d", &m->channel_idx, &m->ofile_idx, &m->ostream_idx);
if ((n == 1 || n == 3) && m->channel_idx == -1) {
m->file_idx = m->stream_idx = -1;
if (n == 1)
m->ofile_idx = m->ostream_idx = -1;
return 0;
}
/* normal syntax */
n = sscanf(arg, "%d.%d.%d:%d.%d",
&m->file_idx, &m->stream_idx, &m->channel_idx,
&m->ofile_idx, &m->ostream_idx);
if (n != 3 && n != 5) {
av_log(NULL, AV_LOG_FATAL, "Syntax error, mapchan usage: "
"[file.stream.channel|-1][:syncfile:syncstream]\n");
}
if (n != 5) // only file.stream.channel specified
m->ofile_idx = m->ostream_idx = -1;
/* check input */
if (m->file_idx < 0 || m->file_idx >= nb_input_files) {
av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file index: %d\n",
m->file_idx);
}
if (m->stream_idx < 0 ||
m->stream_idx >= input_files[m->file_idx]->nb_streams) {
av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file stream index #%d.%d\n",
m->file_idx, m->stream_idx);
}
st = input_files[m->file_idx]->ctx->streams[m->stream_idx];
if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
av_log(NULL, AV_LOG_FATAL, "mapchan: stream #%d.%d is not an audio stream.\n",
m->file_idx, m->stream_idx);
}
if (m->channel_idx < 0 || m->channel_idx >= st->codec->channels) {
av_log(NULL, AV_LOG_FATAL, "mapchan: invalid audio channel #%d.%d.%d\n",
m->file_idx, m->stream_idx, m->channel_idx);
}
return 0;
}
* Parse a metadata specifier passed as 'arg' parameter.
* @param arg metadata string to parse
* @param type metadata type is written here -- g(lobal)/s(tream)/c(hapter)/p(rogram)
* @param index for type c/p, chapter/program index is written here
* @param stream_spec for type s, the stream specifier is written here
*/
static void parse_meta_type(char *arg, char *type, int *index, const char **stream_spec)
{
if (*arg) {
*type = *arg;
switch (*arg) {
case 'g':
break;
case 's':
if (*(++arg) && *arg != ':') {
av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg);
}
*stream_spec = *arg == ':' ? arg + 1 : "";
break;
case 'c':
case 'p':
if (*(++arg) == ':')
*index = strtol(++arg, NULL, 0);
break;
default:
av_log(NULL, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg);
}
} else
*type = 'g';
}
static int copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o)
{
AVDictionary **meta_in = NULL;
AVDictionary **meta_out = NULL;
int i, ret = 0;
char type_in, type_out;
const char *istream_spec = NULL, *ostream_spec = NULL;
int idx_in = 0, idx_out = 0;
parse_meta_type(inspec, &type_in, &idx_in, &istream_spec);
parse_meta_type(outspec, &type_out, &idx_out, &ostream_spec);
if (!ic) {
if (type_out == 'g' || !*outspec)
o->metadata_global_manual = 1;
if (type_out == 's' || !*outspec)
o->metadata_streams_manual = 1;
if (type_out == 'c' || !*outspec)
o->metadata_chapters_manual = 1;
return 0;
}
if (type_in == 'g' || type_out == 'g')
o->metadata_global_manual = 1;
if (type_in == 's' || type_out == 's')
o->metadata_streams_manual = 1;
if (type_in == 'c' || type_out == 'c')
o->metadata_chapters_manual = 1;
/* ic is NULL when just disabling automatic mappings */
if (!ic)
return 0;
#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
if ((index) < 0 || (index) >= (nb_elems)) {\
av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\
(desc), (index));\
}
#define SET_DICT(type, meta, context, index)\
switch (type) {\
case 'g':\
meta = &context->metadata;\
break;\
case 'c':\
METADATA_CHECK_INDEX(index, context->nb_chapters, "chapter")\
meta = &context->chapters[index]->metadata;\
break;\
case 'p':\
METADATA_CHECK_INDEX(index, context->nb_programs, "program")\
meta = &context->programs[index]->metadata;\
break;\
break; /* handled separately below */ \
default: av_assert0(0);\
}\
SET_DICT(type_in, meta_in, ic, idx_in);
SET_DICT(type_out, meta_out, oc, idx_out);
/* for input streams choose first matching stream */
if (type_in == 's') {
for (i = 0; i < ic->nb_streams; i++) {
if ((ret = check_stream_specifier(ic, ic->streams[i], istream_spec)) > 0) {
meta_in = &ic->streams[i]->metadata;
break;
} else if (ret < 0)
}
if (!meta_in) {
av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec);
}
}
if (type_out == 's') {
for (i = 0; i < oc->nb_streams; i++) {
if ((ret = check_stream_specifier(oc, oc->streams[i], ostream_spec)) > 0) {
meta_out = &oc->streams[i]->metadata;
av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
} else if (ret < 0)
}
} else
av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
return 0;
}
static int opt_recording_timestamp(void *optctx, const char *opt, const char *arg)
OptionsContext *o = optctx;
char buf[128];
int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6;
struct tm time = *gmtime((time_t*)&recording_timestamp);
strftime(buf, sizeof(buf), "creation_time=%FT%T%z", &time);
parse_option(o, "metadata", buf, options);
av_log(NULL, AV_LOG_WARNING, "%s is deprecated, set the 'creation_time' metadata "
"tag instead.\n", opt);
return 0;
}
static AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder)
{
const AVCodecDescriptor *desc;
const char *codec_string = encoder ? "encoder" : "decoder";
AVCodec *codec;
codec = encoder ?
avcodec_find_encoder_by_name(name) :
avcodec_find_decoder_by_name(name);
if (!codec && (desc = avcodec_descriptor_get_by_name(name))) {
codec = encoder ? avcodec_find_encoder(desc->id) :
avcodec_find_decoder(desc->id);
if (codec)
av_log(NULL, AV_LOG_VERBOSE, "Matched %s '%s' for codec '%s'.\n",
codec_string, codec->name, desc->name);
}
if (!codec) {
av_log(NULL, AV_LOG_FATAL, "Unknown %s '%s'\n", codec_string, name);
}
if (codec->type != type) {
av_log(NULL, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name);
}
return codec;
}
static AVCodec *choose_decoder(OptionsContext *o, AVFormatContext *s, AVStream *st)
{
char *codec_name = NULL;
MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st);
if (codec_name) {
AVCodec *codec = find_codec_or_die(codec_name, st->codec->codec_type, 0);
st->codec->codec_id = codec->id;
return codec;
} else
return avcodec_find_decoder(st->codec->codec_id);
}
/* Add all the streams from the given input file to the global
* list of input streams. */
static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
{
for (i = 0; i < ic->nb_streams; i++) {
AVStream *st = ic->streams[i];
AVCodecContext *dec = st->codec;
InputStream *ist = av_mallocz(sizeof(*ist));
char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL;
char *codec_tag = NULL;
char *next;
char *discard_str = NULL;
const AVOption *discard_opt = av_opt_find(dec, "skip_frame", NULL, 0, 0);
GROW_ARRAY(input_streams, nb_input_streams);
input_streams[nb_input_streams - 1] = ist;
ist->st = st;
ist->file_index = nb_input_files;
ist->discard = 1;
st->discard = AVDISCARD_ALL;
ist->ts_scale = 1.0;
MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st);
if (codec_tag) {
uint32_t tag = strtol(codec_tag, &next, 0);
if (*next)
tag = AV_RL32(codec_tag);
st->codec->codec_tag = tag;
}
ist->dec = choose_decoder(o, ic, st);
ist->decoder_opts = filter_codec_opts(o->g->codec_opts, ist->st->codec->codec_id, ic, st, ist->dec);
ist->reinit_filters = -1;
MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st);
MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st);
ist->user_set_discard = AVDISCARD_NONE;
if (discard_str && av_opt_eval_int(dec, discard_opt, discard_str, &ist->user_set_discard) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error parsing discard %s.\n",
discard_str);
exit_program(1);
}
ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE;
ist->dec_ctx = avcodec_alloc_context3(ist->dec);
if (!ist->dec_ctx) {
av_log(NULL, AV_LOG_ERROR, "Error allocating the decoder context.\n");
exit_program(1);
}
ret = avcodec_copy_context(ist->dec_ctx, dec);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n");
exit_program(1);
}
switch (dec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
if(!ist->dec)
ist->dec = avcodec_find_decoder(dec->codec_id);
if (av_codec_get_lowres(dec)) {
dec->flags |= CODEC_FLAG_EMU_EDGE;
}
ist->resample_height = ist->dec_ctx->height;
ist->resample_width = ist->dec_ctx->width;
ist->resample_pix_fmt = ist->dec_ctx->pix_fmt;
MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st);
if (framerate && av_parse_video_rate(&ist->framerate,
framerate) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error parsing framerate %s.\n",
framerate);
ist->top_field_first = -1;
MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st);
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
if (hwaccel) {
if (!strcmp(hwaccel, "none"))
ist->hwaccel_id = HWACCEL_NONE;
else if (!strcmp(hwaccel, "auto"))
ist->hwaccel_id = HWACCEL_AUTO;
else {
int i;
for (i = 0; hwaccels[i].name; i++) {
if (!strcmp(hwaccels[i].name, hwaccel)) {
ist->hwaccel_id = hwaccels[i].id;
break;
}
}
if (!ist->hwaccel_id) {
av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n",
hwaccel);
av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: ");
for (i = 0; hwaccels[i].name; i++)
av_log(NULL, AV_LOG_FATAL, "%s ", hwaccels[i].name);
av_log(NULL, AV_LOG_FATAL, "\n");
exit_program(1);
}
}
}
MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st);
if (hwaccel_device) {
ist->hwaccel_device = av_strdup(hwaccel_device);
if (!ist->hwaccel_device)
exit_program(1);
}
Loading
Loading full blame...