Newer
Older
/*
* avconv option parsing
*
* This file is part of Libav.
*
* Libav 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.
*
* Libav 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 Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include "avconv.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 DEFAULT_PASS_LOGFILENAME_PREFIX "av2pass"
#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)\
exit_program(1);\
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;
int audio_volume = 256;
int audio_sync_method = 0;
int video_sync_method = VSYNC_AUTO;
int do_benchmark = 0;
int do_hex_dump = 0;
int do_pkt_dump = 0;
int copy_ts = 0;
int copy_tb = 1;
int exit_on_error = 0;
int print_stats = 1;
int qp_hist = 0;
static int file_overwrite = 0;
Vittorio Giovara
committed
static int file_skip = 0;
static int video_discard = 0;
static int intra_dc_precision = 8;
static int using_stdin = 0;
static int input_sync;
static void uninit_options(OptionsContext *o)
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{
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->meta_data_maps);
av_freep(&o->streamid_map);
static void init_options(OptionsContext *o)
{
memset(o, 0, sizeof(*o));
o->mux_max_delay = 0.7;
o->start_time = AV_NOPTS_VALUE;
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 double parse_frame_aspect_ratio(const char *arg)
{
int x = 0, y = 0;
double ar = 0;
const char *p;
char *end;
p = strchr(arg, ':');
if (p) {
x = strtol(arg, &end, 10);
if (end == p)
y = strtol(end + 1, &end, 10);
if (x > 0 && y > 0)
ar = (double)x / (double)y;
} else
ar = strtod(arg, NULL);
if (!ar) {
av_log(NULL, AV_LOG_FATAL, "Incorrect aspect ratio specification.\n");
exit_program(1);
}
return ar;
}
static int show_hwaccels(void *optctx, const char *opt, const char *arg)
{
int i;
printf("Supported hardware acceleration:\n");
for (i = 0; i < FF_ARRAY_ELEMS(hwaccels) - 1; i++) {
printf("%s\n", hwaccels[i].name);
}
printf("\n");
return 0;
}
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;
char *p, *sync;
char *map;
if (*arg == '-') {
negative = 1;
arg++;
}
map = av_strdup(arg);
if (!map)
return AVERROR(ENOMEM);
/* 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);
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_FATAL, "Sync stream specification in map %s does not "
"match any streams.\n", arg);
exit_program(1);
}
}
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);
exit_program(1);
}
} 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);
exit_program(1);
}
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);
exit_program(1);
}
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;
}
/**
* 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);
exit_program(1);
}
*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);
exit_program(1);
}
} else
*type = 'g';
}
static int copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o)
{
AVDictionary **meta_in = NULL;
AVDictionary **meta_out;
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 (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));\
exit_program(1);\
}
#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;\
case 's':\
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)
exit_program(1);
}
if (!meta_in) {
av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec);
exit_program(1);
}
}
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)
exit_program(1);
}
} else
av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
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);
exit_program(1);
}
if (codec->type != type) {
av_log(NULL, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name);
exit_program(1);
}
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));
Loading
Loading full blame...