This patch introduces an optional index selector to the metadata stream specifier (e.g., -map 0:s:m:language:fre:0).
Currently, mapping streams by metadata (like language) selects all matching streams. This is problematic in files with multiple streams of the same language (e.g., a full French subtitle track and a forced/SDH French track). While users can currently find the absolute stream index via ffprobe, this is cumbersome for batch processing where indices vary across files. By adding a trailing index, users can now select a specific occurrence from the subset of streams that matched the metadata criteria. Example: -map 0:s:m:language:fre:0 # Selects only the first French subtitle -map 0:s:m:language:fre:1 # Selects only the second French subtitle I have verified this change with the following command: ffmpeg -i input.mkv -map 0:s:m:language:fre:0 -c copy output.sup This correctly selects the second French subtitle stream in my test file. All current FATE tests pass. Signed-off-by: Practice2001 <[email protected]> --- doc/fftools-common-opts.texi | 7 ++++--- fftools/cmdutils.c | 31 ++++++++++++++++++++++++++----- tests/fate/ffmpeg.mak | 15 +++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/doc/fftools-common-opts.texi b/doc/fftools-common-opts.texi index 7b5a11b634..a42e85d91b 100644 --- a/doc/fftools-common-opts.texi +++ b/doc/fftools-common-opts.texi @@ -75,11 +75,12 @@ are part of the program and match the @var{additional_stream_specifier}. @item #@var{stream_id} or i:@var{stream_id} Match the stream by stream id (e.g. PID in MPEG-TS container). -@item m:@var{key}[:@var{value}] +@item m:@var{key}[:@var{value}][:@var{stream_index}] Matches streams with the metadata tag @var{key} having the specified value. If @var{value} is not given, matches streams that contain the given tag with any -value. The colon character ':' in @var{key} or @var{value} needs to be -backslash-escaped. +value. If @var{stream_index} is given, matches the @var{stream_index}-th stream +among those matching the key/value criteria. The colon character ':' in +@var{key} or @var{value} needs to be backslash-escaped. @item disp:@var{dispositions}[:@var{additional_stream_specifier}] Matches streams with the given disposition(s). @var{dispositions} is a list of one or more dispositions (as printed by the @option{-dispositions} option) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index e906d4506d..ca3641ceed 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -962,8 +962,7 @@ FILE *get_preset_file(char *filename, size_t filename_size, datadir, desired_size, sizeof *datadir); if (new_datadir) { datadir = new_datadir; - datadir[datadir_len] = 0; - strncat(datadir, "/ffpresets", desired_size - 1 - datadir_len); + strcpy(datadir + datadir_len, "/ffpresets"); base[2] = datadir; } } @@ -1024,7 +1023,7 @@ int stream_specifier_parse(StreamSpecifier *ss, const char *spec, av_log(logctx, AV_LOG_TRACE, "Parsing stream specifier: %s\n", spec); while (*spec) { - if (*spec <= '9' && *spec >= '0') { /* opt:index */ + if (ss->idx == -1 && *spec <= '9' && *spec >= '0') { /* opt:index */ ss->idx = strtol(spec, &endptr, 0); av_assert0(endptr > spec); @@ -1176,8 +1175,9 @@ int stream_specifier_parse(StreamSpecifier *ss, const char *spec, "Parsed metadata: %s:%s; remainder: %s", ss->meta_key, ss->meta_val ? ss->meta_val : "<any value>", spec); - // this terminates the specifier - break; + if (*spec == ':') spec++; + + // continue parsing for possible index } else if (*spec == 'u' && (*(spec + 1) == '\0' || *(spec + 1) == ':')) { ss->usable_only = 1; spec++; @@ -1192,6 +1192,27 @@ int stream_specifier_parse(StreamSpecifier *ss, const char *spec, spec++; } + if (*spec >= '0' && *spec <= '9') { + char *endptr; + + ss->idx = strtol(spec, &endptr, 0); + + av_log(logctx, AV_LOG_TRACE, + "Parsed trailing index: %d; remainder: %s\n", ss->idx, endptr); + + spec = endptr; + } else if (*spec == ':' && *(spec + 1) >= '0' && *(spec + 1) <= '9') { + char *endptr; + + spec++; + ss->idx = strtol(spec, &endptr, 0); + + av_log(logctx, AV_LOG_TRACE, + "Parsed trailing index: %d; remainder: %s\n", ss->idx, endptr); + + spec = endptr; + } + if (*spec) { if (!allow_remainder) { av_log(logctx, AV_LOG_ERROR, diff --git a/tests/fate/ffmpeg.mak b/tests/fate/ffmpeg.mak index 57028a7936..297e9d55ae 100644 --- a/tests/fate/ffmpeg.mak +++ b/tests/fate/ffmpeg.mak @@ -280,3 +280,18 @@ FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER) += fate-ffmpeg-heif # binding the internal filtegraph with a caller defined filtergraph fate-ffmpeg-heif-merge-filtergraph: CMD = framecrc -i $(TARGET_SAMPLES)/heif-conformance/C007.heic -filter_complex "sws_flags=+accurate_rnd+bitexact\;[0:g:0]scale=w=1280:h=720[out]" -map "[out]" FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER SCALE_FILTER) += fate-ffmpeg-heif-merge-filtergraph + +# Test: Select first French subtitle from multiple French subtitles +FATE_FFMPEG += fate-ffmpeg-map-metadata-index-0 +fate-ffmpeg-map-metadata-index-0: CMD = ffmpeg -i $(TARGET_SAMPLES)/subtitles/multi-french.mkv \ + -map 0:s:m:language:fre:0 -c copy -f null - + +# Test: Select second French subtitle +FATE_FFMPEG += fate-ffmpeg-map-metadata-index-1 +fate-ffmpeg-map-metadata-index-1: CMD = ffmpeg -i $(TARGET_SAMPLES)/subtitles/multi-french.mkv \ + -map 0:s:m:language:fre:1 -c copy -f null - + +# Test: Error on out-of-range index +FATE_FFMPEG += fate-ffmpeg-map-metadata-index-error +fate-ffmpeg-map-metadata-index-error: CMD = ffmpeg -i $(TARGET_SAMPLES)/subtitles/multi-french.mkv \ + -map 0:s:m:language:fre:99 -c copy -f null - ; test $$? -ne 0 -- 2.34.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
