[FFmpeg-devel] [PATCH] avformat/utils: parse some stream specifiers recursively

Marton Balint cus at passwd.hu
Mon Feb 11 23:22:02 EET 2019



On Tue, 5 Feb 2019, Marton Balint wrote:

> This removes lots of code duplication and also allows more complex specifiers,
> for example you can use p:204:a:m:language:eng to select the English language
> audio stream from program 204.

Ping... Will apply soon.

Thanks,
Marton

>
> Signed-off-by: Marton Balint <cus at passwd.hu>
> ---
> doc/fftools-common-opts.texi |  30 +++-----
> libavformat/utils.c          | 172 +++++++++++++------------------------------
> 2 files changed, 65 insertions(+), 137 deletions(-)
>
> diff --git a/doc/fftools-common-opts.texi b/doc/fftools-common-opts.texi
> index 84705c0b68..43c1f4d2d6 100644
> --- a/doc/fftools-common-opts.texi
> +++ b/doc/fftools-common-opts.texi
> @@ -34,27 +34,21 @@ Possible forms of stream specifiers are:
> @table @option
> @item @var{stream_index}
> Matches the stream with this index. E.g. @code{-threads:1 4} would set the
> -thread count for the second stream to 4.
> - at item @var{stream_type}[:@var{stream_index}]
> +thread count for the second stream to 4. If @var{stream_index} is used as an
> +additional stream specifier (see below), then it selects stream number
> + at var{stream_index} from the matching streams.
> + at item @var{stream_type}[:@var{additional_stream_specifier}]
> @var{stream_type} is one of following: 'v' or 'V' for video, 'a' for audio, 's'
> for subtitle, 'd' for data, and 't' for attachments. 'v' matches all video
> streams, 'V' only matches video streams which are not attached pictures, video
> -thumbnails or cover arts.  If @var{stream_index} is given, then it matches
> -stream number @var{stream_index} of this type. Otherwise, it matches all
> -streams of this type.
> - at item p:@var{program_id}[:@var{stream_index}] or p:@var{program_id}[:@var{stream_type}[:@var{stream_index}]] or
> -p:@var{program_id}:m:@var{key}[:@var{value}]
> -In first version, if @var{stream_index} is given, then it matches the stream with number @var{stream_index}
> -in the program with the id @var{program_id}. Otherwise, it matches all streams in the
> -program. In the second version, @var{stream_type} is one of following: 'v' for video, 'a' for audio, 's'
> -for subtitle, 'd' for data. If @var{stream_index} is also given, then it matches
> -stream number @var{stream_index} of this type in the program with the id @var{program_id}.
> -Otherwise, if only @var{stream_type} is given, it matches all
> -streams of this type in the program with the id @var{program_id}.
> -In the third version matches streams in the program with the id @var{program_id} with the metadata
> -tag @var{key} having the specified value. If
> - at var{value} is not given, matches streams that contain the given tag with any
> -value.
> +thumbnails or cover arts. If @var{additional_stream_specifier} is used, then
> +it matches streams which both have this type and match the
> + at var{additional_stream_specifier}. Otherwise, it matches all streams of the
> +specified type.
> + at item p:@var{program_id}[:@var{additional_stream_specifier}]
> +Matches streams which are in the program with the id @var{program_id}. If
> + at var{additional_stream_specifier} is used, then it matches streams which both
> +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).
> diff --git a/libavformat/utils.c b/libavformat/utils.c
> index 7afef545fe..b3c30fe14c 100644
> --- a/libavformat/utils.c
> +++ b/libavformat/utils.c
> @@ -5097,13 +5097,21 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f
>     return fr;
> }
> 
> -int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
> -                                    const char *spec)
> -{
> -    if (*spec <= '9' && *spec >= '0') /* opt:index */
> -        return strtol(spec, NULL, 0) == st->index;
> -    else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' ||
> -             *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */
> +/* Matches a stream specifier (but ignores requested index)
> + * Returns:
> + *  - negative on error
> + *  - 0 if st is NOT a matching stream
> + *  - >0 if st is a matching stream
> + * *index is set if a specific index is requested from the matching streams. */
> +static int match_stream_specifier(AVFormatContext *s, AVStream *st,
> +                                  const char *spec, int *index)
> +{
> +    if (*spec <= '9' && *spec >= '0') { /* opt:index */
> +        if (index)
> +            *index = strtol(spec, NULL, 0);
> +        return 1;
> +    } else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' ||
> +               *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */
>         enum AVMediaType type;
>         int nopic = 0;
> 
> @@ -5128,27 +5136,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
> #endif
>         if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC))
>             return 0;
> -        if (*spec++ == ':') { /* possibly followed by :index */
> -            int i, index = strtol(spec, NULL, 0);
> -            for (i = 0; i < s->nb_streams; i++) {
> -#if FF_API_LAVF_AVCTX
> -FF_DISABLE_DEPRECATION_WARNINGS
> -                if ((s->streams[i]->codecpar->codec_type == type
> -                      || s->streams[i]->codec->codec_type == type
> -                    ) &&
> -                    !(nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) &&
> -                    index-- == 0)
> -                    return i == st->index;
> -FF_ENABLE_DEPRECATION_WARNINGS
> -#else
> -                if ((s->streams[i]->codecpar->codec_type == type) &&
> -                    !(nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) &&
> -                    index-- == 0)
> -                    return i == st->index;
> -#endif
> -            }
> -            return 0;
> -        }
> +        if (*spec++ == ':') /* possibly followed by another specifier */
> +            return match_stream_specifier(s, st, spec, index);
>         return 1;
>     } else if (*spec == 'p' && *(spec + 1) == ':') {
>         int prog_id, i, j;
> @@ -5159,99 +5148,15 @@ FF_ENABLE_DEPRECATION_WARNINGS
>             if (s->programs[i]->id != prog_id)
>                 continue;
> 
> -            if (*endptr++ == ':') {  // p:<id>:....
> -                if ( *endptr == 'a' || *endptr == 'v' ||
> -                     *endptr == 's' || *endptr == 'd') {  // p:<id>:<st_type>[:<index>]
> -                    enum AVMediaType type;
> -
> -                    switch (*endptr++) {
> -                    case 'v': type = AVMEDIA_TYPE_VIDEO;      break;
> -                    case 'a': type = AVMEDIA_TYPE_AUDIO;      break;
> -                    case 's': type = AVMEDIA_TYPE_SUBTITLE;   break;
> -                    case 'd': type = AVMEDIA_TYPE_DATA;       break;
> -                    default:  av_assert0(0);
> -                    }
> -                    if (*endptr++ == ':') {  // p:<id>:<st_type>:<index>
> -                        int stream_idx = strtol(endptr, NULL, 0), type_counter = 0;
> -                        for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) {
> -                            int stream_index = s->programs[i]->stream_index[j];
> -                            if (st->index == s->programs[i]->stream_index[j]) {
> -#if FF_API_LAVF_AVCTX
> -FF_DISABLE_DEPRECATION_WARNINGS
> -                                return type_counter == stream_idx &&
> -                                       (type == st->codecpar->codec_type ||
> -                                        type == st->codec->codec_type);
> -FF_ENABLE_DEPRECATION_WARNINGS
> -#else
> -                                return type_counter == stream_idx &&
> -                                       type == st->codecpar->codec_type;
> -#endif
> -                             }
> -#if FF_API_LAVF_AVCTX
> -FF_DISABLE_DEPRECATION_WARNINGS
> -                            if (type == s->streams[stream_index]->codecpar->codec_type ||
> -                                type == s->streams[stream_index]->codec->codec_type)
> -                                type_counter++;
> -FF_ENABLE_DEPRECATION_WARNINGS
> -#else
> -                            if (type == s->streams[stream_index]->codecpar->codec_type)
> -                                type_counter++;
> -#endif
> -                        }
> -                        return 0;
> -                    } else {  // p:<id>:<st_type>
> -                        for (j = 0; j < s->programs[i]->nb_stream_indexes; j++)
> -                            if (st->index == s->programs[i]->stream_index[j]) {
> -#if FF_API_LAVF_AVCTX
> -FF_DISABLE_DEPRECATION_WARNINGS
> -                                 return type == st->codecpar->codec_type ||
> -                                        type == st->codec->codec_type;
> -FF_ENABLE_DEPRECATION_WARNINGS
> -#else
> -                                 return type == st->codecpar->codec_type;
> -#endif
> -                            }
> -                        return 0;
> +            for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) {
> +                if (st->index == s->programs[i]->stream_index[j]) {
> +                    if (*endptr++ == ':') {  // p:<id>:....
> +                        return match_stream_specifier(s, st, endptr, index);
> +                    } else {
> +                        return 1;
>                     }
> -
> -                } else if ( *endptr == 'm') { // p:<id>:m:<metadata_spec>
> -                    AVDictionaryEntry *tag;
> -                    char *key, *val;
> -                    int ret = 0;
> -
> -                    if (*(++endptr) != ':') {
> -                        av_log(s, AV_LOG_ERROR, "Invalid stream specifier syntax, missing ':' sign after :m.\n");
> -                        return AVERROR(EINVAL);
> -                    }
> -
> -                    val = strchr(++endptr, ':');
> -                    key = val ? av_strndup(endptr, val - endptr) : av_strdup(endptr);
> -                    if (!key)
> -                        return AVERROR(ENOMEM);
> -
> -                    for (j = 0; j < s->programs[i]->nb_stream_indexes; j++)
> -                        if (st->index == s->programs[i]->stream_index[j]) {
> -                            tag = av_dict_get(st->metadata, key, NULL, 0);
> -                            if (tag && (!val || !strcmp(tag->value, val + 1)))
> -                                ret = 1;
> -
> -                            break;
> -                        }
> -
> -                    av_freep(&key);
> -                    return ret;
> -
> -                } else {  // p:<id>:<index>
> -                    int stream_idx = strtol(endptr, NULL, 0);
> -                    return stream_idx >= 0 &&
> -                           stream_idx < s->programs[i]->nb_stream_indexes &&
> -                           st->index == s->programs[i]->stream_index[stream_idx];
>                 }
>             }
> -
> -            for (j = 0; j < s->programs[i]->nb_stream_indexes; j++)
> -                if (st->index == s->programs[i]->stream_index[j])
> -                    return 1;
>         }
>         return 0;
>     } else if (*spec == '#' ||
> @@ -5333,10 +5238,39 @@ FF_ENABLE_DEPRECATION_WARNINGS
>     } else if (!*spec) /* empty specifier, matches everything */
>         return 1;
> 
> -    av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
>     return AVERROR(EINVAL);
> }
> 
> +
> +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
> +                                    const char *spec)
> +{
> +    int ret;
> +    int index = -1;
> +
> +    /* This is not really needed but saves us a loop for simple stream index specifiers. */
> +    if (*spec <= '9' && *spec >= '0') /* opt:index */
> +        return strtol(spec, NULL, 0) == st->index;
> +
> +    ret = match_stream_specifier(s, st, spec, &index);
> +    if (ret < 0) {
> +        av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
> +        return ret;
> +    }
> +    if (!ret || index < 0)
> +        return ret;
> +
> +    /* If we requested a matching stream index, we have to ensure st is that. */
> +    for (int i = 0; i < s->nb_streams && index >= 0; i++) {
> +        int ret = match_stream_specifier(s, s->streams[i], spec, NULL);
> +        if (ret < 0)
> +            return ret;
> +        if (ret > 0 && index-- == 0 && st == s->streams[i])
> +            return 1;
> +    }
> +    return 0;
> +}
> +
> int ff_generate_avci_extradata(AVStream *st)
> {
>     static const uint8_t avci100_1080p_extradata[] = {
> -- 
> 2.16.4
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list