From patchwork Tue Feb 5 01:35:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marton Balint X-Patchwork-Id: 11973 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 243A44486AC for ; Tue, 5 Feb 2019 03:35:58 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0901768ABB1; Tue, 5 Feb 2019 03:35:58 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from iq.passwd.hu (iq.passwd.hu [217.27.212.140]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D869468AB11 for ; Tue, 5 Feb 2019 03:35:51 +0200 (EET) Received: from localhost (localhost [127.0.0.1]) by iq.passwd.hu (Postfix) with ESMTP id BBD58E131F; Tue, 5 Feb 2019 02:35:51 +0100 (CET) X-Virus-Scanned: amavisd-new at passwd.hu Received: from iq.passwd.hu ([127.0.0.1]) by localhost (iq.passwd.hu [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id v76TxYmaokgW; Tue, 5 Feb 2019 02:35:50 +0100 (CET) Received: from bluegene.passwd.hu (localhost [127.0.0.1]) by iq.passwd.hu (Postfix) with ESMTP id C1437E04A0; Tue, 5 Feb 2019 02:35:49 +0100 (CET) From: Marton Balint To: ffmpeg-devel@ffmpeg.org Date: Tue, 5 Feb 2019 02:35:43 +0100 Message-Id: <20190205013543.31424-1-cus@passwd.hu> X-Mailer: git-send-email 2.16.4 Subject: [FFmpeg-devel] [PATCH] avformat/utils: parse some stream specifiers recursively X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Marton Balint MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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. Signed-off-by: Marton Balint --- 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. -@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 +@var{stream_index} from the matching streams. +@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. -@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 -@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 +@var{additional_stream_specifier}. Otherwise, it matches all streams of the +specified type. +@item p:@var{program_id}[:@var{additional_stream_specifier}] +Matches streams which are in the program with the id @var{program_id}. If +@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::.... - if ( *endptr == 'a' || *endptr == 'v' || - *endptr == 's' || *endptr == 'd') { // p::[:] - 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::: - 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:: - 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::.... + return match_stream_specifier(s, st, endptr, index); + } else { + return 1; } - - } else if ( *endptr == 'm') { // p::m: - 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:: - 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[] = {