PR #21812 opened by Practice2001 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21812 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21812.patch
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 commands: ffmpeg -i /home/family/ffmpeg_sources/ffmpeg/fate-suite/mkv/dummy_multistream.mkv -map 0:s:m:language:fre:0 -c:s ass output.ass ffmpeg -i /home/family/ffmpeg_sources/ffmpeg/fate-suite/mkv/dummy_multistream.mkv -map 0:s:m:language:fre:0 -c:s srt output.srt This selects the first French subtitle stream in my test file. **Note**: the mapping works independently of the output codec; the -c copy or compatible subtitle encoder must be used for writing the file format. Example: ffmpeg -i /home/family/ffmpeg_sources/ffmpeg/fate-suite/mkv/dummy_multistream.mkv -map 0:s:m:language:fre:0 -c:s hdmv_pgs_subtitle output.sup All current FATE tests(original + new tests in this PR) pass on my local machine. I have added those tests as comments, since the attached sample file is not present in the FATE suite. I did send a couple of requests to add that file. Closes: #20149 >From d8f401c70fed82861f7ece430182b64b5e52862d Mon Sep 17 00:00:00 2001 From: Practice2001 <[email protected]> Date: Fri, 20 Feb 2026 15:26:17 +0530 Subject: [PATCH] [PATCH] fftools: add index selector to metadata stream specifier --- doc/fftools-common-opts.texi | 7 ++++--- fftools/cmdutils.c | 28 +++++++++++++++++++++++++--- tests/fate/ffmpeg.mak | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 6 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 9a365c228d..0dc8e3c5e9 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -1023,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); @@ -1175,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++; @@ -1190,6 +1191,27 @@ int stream_specifier_parse(StreamSpecifier *ss, const char *spec, if (*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) { diff --git a/tests/fate/ffmpeg.mak b/tests/fate/ffmpeg.mak index cd00275638..5851fd8903 100644 --- a/tests/fate/ffmpeg.mak +++ b/tests/fate/ffmpeg.mak @@ -290,3 +290,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)/mkv/dummy_multistream.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)/mkv/dummy_multistream.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)/mkv/dummy_multistream.mkv \ +# -map 0:s:m:language:fre:99 -c copy -f null - ; test $$? -ne 0 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
