mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-01-29 10:43:15 +00:00
fftools/ffmpeg: rewrite checking whether codec AVOptions have been used
Share the code between encoding and decoding. Instead of checking every stream's options dictionary (which is also used for other purposes), track all used options in a dedicated dictionary.
This commit is contained in:
parent
fc446eea05
commit
f1664aabb1
@ -986,7 +986,7 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
|
||||
|
||||
int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
|
||||
AVFormatContext *s, AVStream *st, const AVCodec *codec,
|
||||
AVDictionary **dst)
|
||||
AVDictionary **dst, AVDictionary **opts_used)
|
||||
{
|
||||
AVDictionary *ret = NULL;
|
||||
const AVDictionaryEntry *t = NULL;
|
||||
@ -1013,6 +1013,7 @@ int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
|
||||
while (t = av_dict_iterate(opts, t)) {
|
||||
const AVClass *priv_class;
|
||||
char *p = strchr(t->key, ':');
|
||||
int used = 0;
|
||||
|
||||
/* check stream specification in opt name */
|
||||
if (p) {
|
||||
@ -1030,15 +1031,21 @@ int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
|
||||
!codec ||
|
||||
((priv_class = codec->priv_class) &&
|
||||
av_opt_find(&priv_class, t->key, NULL, flags,
|
||||
AV_OPT_SEARCH_FAKE_OBJ)))
|
||||
AV_OPT_SEARCH_FAKE_OBJ))) {
|
||||
av_dict_set(&ret, t->key, t->value, 0);
|
||||
else if (t->key[0] == prefix &&
|
||||
used = 1;
|
||||
} else if (t->key[0] == prefix &&
|
||||
av_opt_find(&cc, t->key + 1, NULL, flags,
|
||||
AV_OPT_SEARCH_FAKE_OBJ))
|
||||
AV_OPT_SEARCH_FAKE_OBJ)) {
|
||||
av_dict_set(&ret, t->key + 1, t->value, 0);
|
||||
used = 1;
|
||||
}
|
||||
|
||||
if (p)
|
||||
*p = ':';
|
||||
|
||||
if (used && opts_used)
|
||||
av_dict_set(opts_used, t->key, "", 0);
|
||||
}
|
||||
|
||||
*dst = ret;
|
||||
@ -1063,7 +1070,7 @@ int setup_find_stream_info_opts(AVFormatContext *s,
|
||||
|
||||
for (int i = 0; i < s->nb_streams; i++) {
|
||||
ret = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
|
||||
s, s->streams[i], NULL, &opts[i]);
|
||||
s, s->streams[i], NULL, &opts[i], NULL);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
@ -371,11 +371,13 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec);
|
||||
* @param codec The particular codec for which the options should be filtered.
|
||||
* If null, the default one is looked up according to the codec id.
|
||||
* @param dst a pointer to the created dictionary
|
||||
* @param opts_used if non-NULL, every option stored in dst is also stored here,
|
||||
* with specifiers preserved
|
||||
* @return a non-negative number on success, a negative error code on failure
|
||||
*/
|
||||
int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
|
||||
AVFormatContext *s, AVStream *st, const AVCodec *codec,
|
||||
AVDictionary **dst);
|
||||
AVDictionary **dst, AVDictionary **opts_used);
|
||||
|
||||
/**
|
||||
* Setup AVCodecContext options for avformat_find_stream_info().
|
||||
|
@ -474,6 +474,56 @@ const FrameData *packet_data_c(AVPacket *pkt)
|
||||
return ret < 0 ? NULL : (const FrameData*)pkt->opaque_ref->data;
|
||||
}
|
||||
|
||||
int check_avoptions_used(const AVDictionary *opts, const AVDictionary *opts_used,
|
||||
void *logctx, int decode)
|
||||
{
|
||||
const AVClass *class = avcodec_get_class();
|
||||
const AVClass *fclass = avformat_get_class();
|
||||
|
||||
const int flag = decode ? AV_OPT_FLAG_DECODING_PARAM :
|
||||
AV_OPT_FLAG_ENCODING_PARAM;
|
||||
const AVDictionaryEntry *e = NULL;
|
||||
|
||||
while ((e = av_dict_iterate(opts, e))) {
|
||||
const AVOption *option, *foption;
|
||||
char *optname, *p;
|
||||
|
||||
if (av_dict_get(opts_used, e->key, NULL, 0))
|
||||
continue;
|
||||
|
||||
optname = av_strdup(e->key);
|
||||
if (!optname)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
p = strchr(optname, ':');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
option = av_opt_find(&class, optname, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
foption = av_opt_find(&fclass, optname, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
av_freep(&optname);
|
||||
if (!option || foption)
|
||||
continue;
|
||||
|
||||
if (!(option->flags & flag)) {
|
||||
av_log(logctx, AV_LOG_ERROR, "Codec AVOption %s (%s) is not a %s "
|
||||
"option.\n", e->key, option->help ? option->help : "",
|
||||
decode ? "decoding" : "encoding");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
av_log(logctx, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used "
|
||||
"for any stream. The most likely reason is either wrong type "
|
||||
"(e.g. a video option with no video streams) or that it is a "
|
||||
"private option of some decoder which was not actually used "
|
||||
"for any stream.\n", e->key, option->help ? option->help : "");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_benchmark(const char *fmt, ...)
|
||||
{
|
||||
if (do_benchmark_all) {
|
||||
|
@ -710,8 +710,10 @@ void term_exit(void);
|
||||
|
||||
void show_usage(void);
|
||||
|
||||
int check_avoptions_used(const AVDictionary *opts, const AVDictionary *opts_used,
|
||||
void *logctx, int decode);
|
||||
|
||||
int assert_file_overwrite(const char *filename);
|
||||
AVDictionary *strip_specifiers(const AVDictionary *dict);
|
||||
int find_codec(void *logctx, const char *name,
|
||||
enum AVMediaType type, int encoder, const AVCodec **codec);
|
||||
int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global);
|
||||
|
@ -1207,7 +1207,7 @@ static DemuxStream *demux_stream_alloc(Demuxer *d, AVStream *st)
|
||||
return ds;
|
||||
}
|
||||
|
||||
static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st)
|
||||
static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictionary **opts_used)
|
||||
{
|
||||
AVFormatContext *ic = d->f.ctx;
|
||||
AVCodecParameters *par = st->codecpar;
|
||||
@ -1334,7 +1334,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st)
|
||||
|
||||
if (ist->dec) {
|
||||
ret = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id,
|
||||
ic, st, ist->dec, &ds->decoder_opts);
|
||||
ic, st, ist->dec, &ds->decoder_opts, opts_used);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@ -1553,8 +1553,7 @@ int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch)
|
||||
const AVInputFormat *file_iformat = NULL;
|
||||
int err, i, ret = 0;
|
||||
int64_t timestamp;
|
||||
AVDictionary *unused_opts = NULL;
|
||||
const AVDictionaryEntry *e = NULL;
|
||||
AVDictionary *opts_used = NULL;
|
||||
const char* video_codec_name = NULL;
|
||||
const char* audio_codec_name = NULL;
|
||||
const char* subtitle_codec_name = NULL;
|
||||
@ -1826,48 +1825,21 @@ int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch)
|
||||
|
||||
/* Add all the streams from the given input file to the demuxer */
|
||||
for (int i = 0; i < ic->nb_streams; i++) {
|
||||
ret = ist_add(o, d, ic->streams[i]);
|
||||
if (ret < 0)
|
||||
ret = ist_add(o, d, ic->streams[i], &opts_used);
|
||||
if (ret < 0) {
|
||||
av_dict_free(&opts_used);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* dump the file content */
|
||||
av_dump_format(ic, f->index, filename, 0);
|
||||
|
||||
/* check if all codec options have been used */
|
||||
unused_opts = strip_specifiers(o->g->codec_opts);
|
||||
for (i = 0; i < f->nb_streams; i++) {
|
||||
DemuxStream *ds = ds_from_ist(f->streams[i]);
|
||||
e = NULL;
|
||||
while ((e = av_dict_iterate(ds->decoder_opts, e)))
|
||||
av_dict_set(&unused_opts, e->key, NULL, 0);
|
||||
}
|
||||
|
||||
e = NULL;
|
||||
while ((e = av_dict_iterate(unused_opts, e))) {
|
||||
const AVClass *class = avcodec_get_class();
|
||||
const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
const AVClass *fclass = avformat_get_class();
|
||||
const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
if (!option || foption)
|
||||
continue;
|
||||
|
||||
|
||||
if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) {
|
||||
av_log(d, AV_LOG_ERROR, "Codec AVOption %s (%s) is not a decoding "
|
||||
"option.\n", e->key, option->help ? option->help : "");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
av_log(d, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used "
|
||||
"for any stream. The most likely reason is either wrong type "
|
||||
"(e.g. a video option with no video streams) or that it is a "
|
||||
"private option of some decoder which was not actually used "
|
||||
"for any stream.\n", e->key, option->help ? option->help : "");
|
||||
}
|
||||
av_dict_free(&unused_opts);
|
||||
ret = check_avoptions_used(o->g->codec_opts, opts_used, d, 1);
|
||||
av_dict_free(&opts_used);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < o->dump_attachment.nb_opt; i++) {
|
||||
int j;
|
||||
|
@ -864,6 +864,7 @@ void of_free(OutputFile **pof)
|
||||
av_freep(&mux->sch_stream_idx);
|
||||
|
||||
av_dict_free(&mux->opts);
|
||||
av_dict_free(&mux->enc_opts_used);
|
||||
|
||||
av_packet_free(&mux->sq_pkt);
|
||||
|
||||
|
@ -99,6 +99,9 @@ typedef struct Muxer {
|
||||
|
||||
AVDictionary *opts;
|
||||
|
||||
// used to validate that all encoder avoptions have been actually used
|
||||
AVDictionary *enc_opts_used;
|
||||
|
||||
/* filesize limit expressed in bytes */
|
||||
int64_t limit_filesize;
|
||||
atomic_int_least64_t last_filesize;
|
||||
|
@ -1160,7 +1160,8 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
|
||||
const char *enc_time_base = NULL;
|
||||
|
||||
ret = filter_codec_opts(o->g->codec_opts, enc->codec_id,
|
||||
oc, st, enc->codec, &ost->encoder_opts);
|
||||
oc, st, enc->codec, &ost->encoder_opts,
|
||||
&mux->enc_opts_used);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1265,7 +1266,8 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
|
||||
}
|
||||
} else {
|
||||
ret = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st,
|
||||
NULL, &ost->encoder_opts);
|
||||
NULL, &ost->encoder_opts,
|
||||
&mux->enc_opts_used);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@ -3118,53 +3120,6 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt)
|
||||
{
|
||||
const AVClass *class = avcodec_get_class();
|
||||
const AVClass *fclass = avformat_get_class();
|
||||
const OutputFile *of = &mux->of;
|
||||
|
||||
AVDictionary *unused_opts;
|
||||
const AVDictionaryEntry *e;
|
||||
|
||||
unused_opts = strip_specifiers(codec_avopt);
|
||||
for (int i = 0; i < of->nb_streams; i++) {
|
||||
e = NULL;
|
||||
while ((e = av_dict_iterate(of->streams[i]->encoder_opts, e)))
|
||||
av_dict_set(&unused_opts, e->key, NULL, 0);
|
||||
}
|
||||
|
||||
e = NULL;
|
||||
while ((e = av_dict_iterate(unused_opts, e))) {
|
||||
const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
|
||||
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
|
||||
if (!option || foption)
|
||||
continue;
|
||||
|
||||
if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) {
|
||||
av_log(mux, AV_LOG_ERROR, "Codec AVOption %s (%s) is not an "
|
||||
"encoding option.\n", e->key, option->help ? option->help : "");
|
||||
av_dict_free(&unused_opts);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
// gop_timecode is injected by generic code but not always used
|
||||
if (!strcmp(e->key, "gop_timecode"))
|
||||
continue;
|
||||
|
||||
av_log(mux, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used "
|
||||
"for any stream. The most likely reason is either wrong type "
|
||||
"(e.g. a video option with no video streams) or that it is a "
|
||||
"private option of some encoder which was not actually used for "
|
||||
"any stream.\n", e->key, option->help ? option->help : "");
|
||||
}
|
||||
av_dict_free(&unused_opts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *output_file_item_name(void *obj)
|
||||
{
|
||||
const Muxer *mux = obj;
|
||||
@ -3272,7 +3227,8 @@ int of_open(const OptionsContext *o, const char *filename, Scheduler *sch)
|
||||
return err;
|
||||
|
||||
/* check if all codec options have been used */
|
||||
err = validate_enc_avopt(mux, o->g->codec_opts);
|
||||
err = check_avoptions_used(o->g->codec_opts, mux->enc_opts_used, mux, 0);
|
||||
av_dict_free(&mux->enc_opts_used);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -151,24 +151,6 @@ static int show_hwaccels(void *optctx, const char *opt, const char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return a copy of the input with the stream specifiers removed from the keys */
|
||||
AVDictionary *strip_specifiers(const AVDictionary *dict)
|
||||
{
|
||||
const AVDictionaryEntry *e = NULL;
|
||||
AVDictionary *ret = NULL;
|
||||
|
||||
while ((e = av_dict_iterate(dict, e))) {
|
||||
char *p = strchr(e->key, ':');
|
||||
|
||||
if (p)
|
||||
*p = 0;
|
||||
av_dict_set(&ret, e->key, e->value, 0);
|
||||
if (p)
|
||||
*p = ':';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *opt_match_per_type_str(const SpecifierOptList *sol,
|
||||
char mediatype)
|
||||
{
|
||||
|
@ -2673,7 +2673,7 @@ static int stream_component_open(VideoState *is, int stream_index)
|
||||
avctx->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
|
||||
ret = filter_codec_opts(codec_opts, avctx->codec_id, ic,
|
||||
ic->streams[stream_index], codec, &opts);
|
||||
ic->streams[stream_index], codec, &opts, NULL);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -3927,7 +3927,7 @@ static int open_input_file(InputFile *ifile, const char *filename,
|
||||
AVDictionary *opts;
|
||||
|
||||
err = filter_codec_opts(codec_opts, stream->codecpar->codec_id,
|
||||
fmt_ctx, stream, codec, &opts);
|
||||
fmt_ctx, stream, codec, &opts, NULL);
|
||||
if (err < 0)
|
||||
exit(1);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user