avformat/utils: parse some stream specifiers recursively

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

Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
Marton Balint 2019-02-05 00:16:36 +01:00
parent b35843e391
commit dbfd042983
2 changed files with 67 additions and 136 deletions

View File

@ -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).

View File

@ -5097,13 +5097,24 @@ 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)
/**
* Matches a stream specifier (but ignores requested index).
*
* @param index set if a specific index is requested from the matching streams
*
* @return <0 on error
* 0 if st is NOT a matching stream
* >0 if st is a matching stream
*/
static int match_stream_specifier(AVFormatContext *s, AVStream *st,
const char *spec, int *index)
{
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] */
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 +5139,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 +5151,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);
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;
}
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;
}
} 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 +5241,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[] = {