diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c index c64c26b5c8..e6d1a31dd3 100644 --- a/libavformat/ffmenc.c +++ b/libavformat/ffmenc.c @@ -162,6 +162,60 @@ static int ffm_write_header_codec_ctx(AVIOContext *pb, AVCodecContext *ctx, unsi #undef ENC } +static int ffm_write_recommended_config(AVIOContext *pb, AVCodecContext *ctx, unsigned tag, + const char *configuration) +{ + int ret; + const AVCodec *enc = ctx->codec ? ctx->codec : avcodec_find_encoder(ctx->codec_id); + AVIOContext *tmp; + AVDictionaryEntry *t = NULL; + AVDictionary *all = NULL, *comm = NULL, *prv = NULL; + char *buf = NULL; + + if (!enc || !enc->priv_class || !enc->priv_data_size) { + /* codec is not known/has no private options, so save everything as common options */ + if (avio_open_dyn_buf(&tmp) < 0) + return AVERROR(ENOMEM); + avio_put_str(tmp, configuration); + write_header_chunk(pb, tmp, tag); + return 0; + } + + if ((ret = av_dict_parse_string(&all, configuration, "=", ",", 0)) < 0) + return ret; + + while ((t = av_dict_get(all, "", t, AV_DICT_IGNORE_SUFFIX))) { + if (av_opt_find((void *)&enc->priv_class, t->key, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) { + if ((ret = av_dict_set(&prv, t->key, t->value, 0)) < 0) + goto fail; + } else if ((ret = av_dict_set(&comm, t->key, t->value, 0)) < 0) + goto fail; + } + + if (comm) { + if ((ret = av_dict_get_string(comm, &buf, '=', ',')) < 0 || + (ret = avio_open_dyn_buf(&tmp)) < 0) + goto fail; + avio_put_str(tmp, buf); + av_freep(&buf); + write_header_chunk(pb, tmp, tag); + } + if (prv) { + if ((ret = av_dict_get_string(prv, &buf, '=', ',')) < 0 || + (ret = avio_open_dyn_buf(&tmp)) < 0) + goto fail; + avio_put_str(tmp, buf); + write_header_chunk(pb, tmp, MKBETAG('C', 'P', 'R', 'V')); + } + + fail: + av_free(buf); + av_dict_free(&all); + av_dict_free(&comm); + av_dict_free(&prv); + return ret; +} + static int ffm_write_header(AVFormatContext *s) { FFMContext *ffm = s->priv_data; @@ -220,13 +274,25 @@ static int ffm_write_header(AVFormatContext *s) /* specific info */ switch(codec->codec_type) { case AVMEDIA_TYPE_VIDEO: - if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'V', 'I'), AV_OPT_FLAG_VIDEO_PARAM)) < 0 || - (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_VIDEO_PARAM)) < 0) + if (st->recommended_encoder_configuration) { + av_log(NULL, AV_LOG_DEBUG, "writing recommended configuration: %s\n", + st->recommended_encoder_configuration); + if ((ret = ffm_write_recommended_config(s->pb, codec, MKBETAG('S', '2', 'V', 'I'), + st->recommended_encoder_configuration)) < 0) + return ret; + } else if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'V', 'I'), AV_OPT_FLAG_VIDEO_PARAM)) < 0 || + (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_VIDEO_PARAM)) < 0) return ret; break; case AVMEDIA_TYPE_AUDIO: - if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'A', 'U'), AV_OPT_FLAG_AUDIO_PARAM)) < 0 || - (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_AUDIO_PARAM)) < 0) + if (st->recommended_encoder_configuration) { + av_log(NULL, AV_LOG_DEBUG, "writing recommended configuration: %s\n", + st->recommended_encoder_configuration); + if ((ret = ffm_write_recommended_config(s->pb, codec, MKBETAG('S', '2', 'A', 'U'), + st->recommended_encoder_configuration)) < 0) + return ret; + } else if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'A', 'U'), AV_OPT_FLAG_AUDIO_PARAM)) < 0 || + (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_AUDIO_PARAM)) < 0) return ret; break; default: diff --git a/libavformat/version.h b/libavformat/version.h index 6dcf632e32..206b265574 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 56 #define LIBAVFORMAT_VERSION_MINOR 15 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \