Jan Ekström (12021-02-08): > This way protocol or format related options can be set for all > of the files opened during concatenation both globally as well > as per-file. > --- > > Changes from v2: > > 1. Added an example, although I had issues figuring out something useful > that is not a hack for something. Ended up doing a disablement of > advanced edit list functionality, since that can sometimes lead to > unwanted behavior. >
> * First idea was to override the input format for a file without an > extension. For that, we have no AVOption it seems. Indeed, lavf does not accept it as an option. It needs to be added separately. Or we can make it an option. > * Then came the idea of utilizing the framerate option in the raw > h264 demuxer. That didn't work because apparently if there is a header > in there that probed/parsed frame rate field gets utilized. You could set the frame rate for a raw video stream. > * Third idea was either the one I picked, or analyzeduration/probesize > for MPEG-TS. I opted for the mp4 case. > > 2. Quoted the : in documentation with @code{:}. > 3. Fixed a duplicate space in a log message. > --- > > doc/demuxers.texi | 24 ++++++++++++++ > libavformat/concatdec.c | 69 ++++++++++++++++++++++++++++++++++++++++- > 2 files changed, 92 insertions(+), 1 deletion(-) > > diff --git a/doc/demuxers.texi b/doc/demuxers.texi > index 3c15ab9eee..20601f9575 100644 > --- a/doc/demuxers.texi > +++ b/doc/demuxers.texi > @@ -149,6 +149,14 @@ Metadata of the packets of the file. The specified > metadata will be set for > each file packet. You can specify this directive multiple times to add > multiple > metadata entries. > > +@item @code{input_options @var{key=value:key2=value2}} > +Input options passed on when reading a specific file, using a > @code{:}-separated > +list of key=value pairs. No! Why would you do that? It adds one level of escaping, we already have way too much of them. input option key=value singular, and can be set as many times as necessary. > Requires @code{safe} to be non-positive. Zero would be safer. > Global options > +for all files can be set with the @code{input_options} demuxer option. When > using > +both options on the list of files as well as globally via the demuxer option, > +the global ones get applied first and the file-specific options are then > applied > +on top of them. I am not sure it is the right order. Options added on the fly should take precedence over options written in a file, in general. But the global / specific distinction makes it less clear-cut. Other opinions? > + > @item @code{stream} > Introduce a stream in the virtual file. > All subsequent stream-related directives apply to the last introduced > @@ -204,6 +212,10 @@ expressed in microseconds. The duration metadata is only > set if it is known > based on the concat file. > The default is 0. > > +@item input_options > +Input options to be passed on for all opened inputs using > a :-separated list of It should say "a dictionary", with a link to where the syntax of dictionaries is documented, but whoever added AV_OPT_TYPE_DICT neglected to add the corresponding documentation paragraph :( > +key=value pairs. > + > @end table > > @subsection Examples > @@ -231,6 +243,18 @@ duration 20.0 > > file subdir/file-2.wav > @end example > + > +@item > +Disabling advanced edit list capability for the first input file via > +input_options: > +@example > +ffconcat version 1.0 > + > +file file-1.mp4 > +input_options advanced_editlist=false > + > +file file-2.mp4 > +@end example > @end itemize > > @section dash > diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c > index 6d5b9914f9..89d75cedc6 100644 > --- a/libavformat/concatdec.c > +++ b/libavformat/concatdec.c > @@ -52,6 +52,7 @@ typedef struct { > int64_t outpoint; > AVDictionary *metadata; > int nb_streams; > + AVDictionary *input_options; > } ConcatFile; > > typedef struct { > @@ -66,6 +67,7 @@ typedef struct { > ConcatMatchMode stream_match_mode; > unsigned auto_convert; > int segment_time_metadata; > + AVDictionary *input_options; > } ConcatContext; > > static int concat_probe(const AVProbeData *probe) > @@ -329,6 +331,7 @@ static int open_file(AVFormatContext *avf, unsigned > fileno) > { > ConcatContext *cat = avf->priv_data; > ConcatFile *file = &cat->files[fileno]; > + AVDictionary *options = NULL; > int ret; > > if (cat->avf) > @@ -344,12 +347,37 @@ static int open_file(AVFormatContext *avf, unsigned > fileno) > if ((ret = ff_copy_whiteblacklists(cat->avf, avf)) < 0) > return ret; > > - if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 || > + // Apply global AVOptions first > + if (cat->input_options && > + (ret = av_dict_copy(&options, cat->input_options, 0) < 0)) > + return ret; > + > + // then apply file-specific AVOptions > + if (file->input_options && > + (ret = av_dict_copy(&options, file->input_options, 0) < 0)) > + return ret; > + > + if ((ret = avformat_open_input(&cat->avf, file->url, NULL, &options)) < > 0 || > (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) { > av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url); > avformat_close_input(&cat->avf); > + av_dict_free(&options); > return ret; > } > + > + if (av_dict_count(options)) { > + AVDictionaryEntry *en = NULL; > + > + while ((en = av_dict_get(options, "", en, AV_DICT_IGNORE_SUFFIX))) { > + av_log(avf, AV_LOG_WARNING, > + "Option '%s' set to '%s' was ignored when opening %s " > + "with the %s reader!\n", > + en->key, en->value, file->url, cat->avf->iformat->name); > + } > + } > + > + av_dict_free(&options); > + > cat->cur_file = file; > file->start_time = !fileno ? 0 : > cat->files[fileno - 1].start_time + > @@ -386,6 +414,7 @@ static int concat_read_close(AVFormatContext *avf) > } > av_freep(&cat->files[i].streams); > av_dict_free(&cat->files[i].metadata); > + av_dict_free(&cat->files[i].input_options); > } > if (cat->avf) > avformat_close_input(&cat->avf); > @@ -457,6 +486,41 @@ static int concat_read_header(AVFormatContext *avf) > FAIL(AVERROR_INVALIDDATA); > } > av_freep(&metadata); > + } else if (!strncmp(keyword, "input_options", 13)) { > + if (!file) { > + av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n", > + line, keyword); > + FAIL(AVERROR_INVALIDDATA); > + } > + > + if (cat->safe > 0) { > + av_log(avf, AV_LOG_ERROR, > + "Line %d: Input options cannot be set in file list in > " > + "safe mode!\n", line); > + FAIL(AVERROR(EPERM)); > + } > + > + { > + char *input_options = av_get_token((const char **)&cursor, > + SPACE_CHARS); > + if (!input_options) { > + av_log(avf, AV_LOG_ERROR, > + "Line %d: key=value pairs required!\n", line); > + FAIL(AVERROR_INVALIDDATA); > + } > + > + if ((ret = > + av_dict_parse_string(&file->input_options, > input_options, > + "=", ":", 0)) < 0) { Just find the first = and add a single key-value. > + av_log(avf, AV_LOG_ERROR, > + "Line %d: failed to parse input options string\n", > + line); > + av_freep(&input_options); > + FAIL(AVERROR_INVALIDDATA); > + } > + > + av_freep(&input_options); > + } > } else if (!strcmp(keyword, "stream")) { > if (!avformat_new_stream(avf, NULL)) > FAIL(AVERROR(ENOMEM)); > @@ -764,6 +828,9 @@ static const AVOption options[] = { > OFFSET(auto_convert), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC }, > { "segment_time_metadata", "output file segment start time and duration > as packet metadata", > OFFSET(segment_time_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC > }, > + { "input_options", > + "set options for all opened inputs using a :-separated list of > key=value pairs", > + OFFSET(input_options), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, DEC }, > { NULL } > }; > Regards, -- Nicolas George
signature.asc
Description: PGP signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".