@ -980,220 +980,312 @@ FILE *get_preset_file(char *filename, size_t filename_size,
return f ;
}
/**
* Matches a stream specifier ( but ignores requested index ) .
*
* @ param indexptr set to point to the requested stream index if there is one
*
* @ return < 0 on error
* 0 if st is NOT a matching stream
* > 0 if st is a matching stream
*/
static int match_stream_specifier ( const AVFormatContext * s , const AVStream * st ,
const char * spec , const char * * indexptr ,
const AVStreamGroup * * g , const AVProgram * * p )
void stream_specifier_uninit ( StreamSpecifier * ss )
{
int match = 1 ; /* Stores if the specifier matches so far. */
av_freep ( & ss - > meta_key ) ;
av_freep ( & ss - > meta_val ) ;
av_freep ( & ss - > remainder ) ;
memset ( ss , 0 , sizeof ( * ss ) ) ;
}
int stream_specifier_parse ( StreamSpecifier * ss , const char * spec ,
int allow_remainder , void * logctx )
{
char * endptr ;
int ret ;
memset ( ss , 0 , sizeof ( * ss ) ) ;
ss - > idx = - 1 ;
ss - > media_type = AVMEDIA_TYPE_UNKNOWN ;
ss - > stream_list = STREAM_LIST_ALL ;
av_log ( logctx , AV_LOG_TRACE , " Parsing stream specifier: %s \n " , spec ) ;
while ( * spec ) {
if ( * spec < = ' 9 ' & & * spec > = ' 0 ' ) { /* opt:index */
if ( indexptr )
* indexptr = spec ;
return match ;
ss - > idx = strtol ( spec , & endptr , 0 ) ;
av_assert0 ( endptr > spec ) ;
spec = endptr ;
av_log ( logctx , AV_LOG_TRACE ,
" Parsed index: %d; remainder: %s \n " , ss - > idx , spec ) ;
// this terminates the specifier
break ;
} else if ( * spec = = ' v ' | | * spec = = ' a ' | | * spec = = ' s ' | | * spec = = ' d ' | |
* spec = = ' t ' | | * spec = = ' V ' ) { /* opt:[vasdtV] */
enum AVMediaType type ;
int nopic = 0 ;
if ( ss - > media_type ! = AVMEDIA_TYPE_UNKNOWN ) {
av_log ( logctx , AV_LOG_ERROR , " Stream type specified multiple times \n " ) ;
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
switch ( * spec + + ) {
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 ;
case ' t ' : type = AVMEDIA_TYPE_ATTACHMENT ; break ;
case ' V ' : type = AVMEDIA_TYPE_VIDEO ; nopic = 1 ; break ;
case ' v ' : ss - > media_type = AVMEDIA_TYPE_VIDEO ; break ;
case ' a ' : ss - > media_type = AVMEDIA_TYPE_AUDIO ; break ;
case ' s ' : ss - > media_type = AVMEDIA_TYPE_SUBTITLE ; break ;
case ' d ' : ss - > media_type = AVMEDIA_TYPE_DATA ; break ;
case ' t ' : ss - > media_type = AVMEDIA_TYPE_ATTACHMENT ; break ;
case ' V ' : ss - > media_type = AVMEDIA_TYPE_VIDEO ;
ss - > no_apic = 1 ; break ;
default : av_assert0 ( 0 ) ;
}
if ( * spec & & * spec + + ! = ' : ' ) /* If we are not at the end, then another specifier must follow. */
return AVERROR ( EINVAL ) ;
if ( type ! = st - > codecpar - > codec_type )
match = 0 ;
if ( nopic & & ( st - > disposition & AV_DISPOSITION_ATTACHED_PIC ) )
match = 0 ;
av_log ( logctx , AV_LOG_TRACE , " Parsed media type: %s; remainder: %s \n " ,
av_get_media_type_string ( ss - > media_type ) , spec ) ;
} else if ( * spec = = ' g ' & & * ( spec + 1 ) = = ' : ' ) {
int64_t group_idx = - 1 , group_id = - 1 ;
int found = 0 ;
char * endptr ;
if ( ss - > stream_list ! = STREAM_LIST_ALL )
goto multiple_stream_lists ;
spec + = 2 ;
if ( * spec = = ' # ' | | ( * spec = = ' i ' & & * ( spec + 1 ) = = ' : ' ) ) {
ss - > stream_list = STREAM_LIST_GROUP_ID ;
spec + = 1 + ( * spec = = ' i ' ) ;
group_id = strtol ( spec , & endptr , 0 ) ;
if ( spec = = endptr | | ( * endptr & & * endptr + + ! = ' : ' ) )
return AVERROR ( EINVAL ) ;
spec = endptr ;
} else {
group_idx = strtol ( spec , & endptr , 0 ) ;
/* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */
if ( spec = = endptr | | ( * endptr & & * endptr + + ! = ' : ' ) )
return AVERROR ( EINVAL ) ;
spec = endptr ;
}
if ( match ) {
if ( group_id > 0 ) {
for ( unsigned i = 0 ; i < s - > nb_stream_groups ; i + + ) {
if ( group_id = = s - > stream_groups [ i ] - > id ) {
group_idx = i ;
break ;
}
}
}
if ( group_idx < 0 | | group_idx > = s - > nb_stream_groups )
return AVERROR ( EINVAL ) ;
for ( unsigned j = 0 ; j < s - > stream_groups [ group_idx ] - > nb_streams ; j + + ) {
if ( st - > index = = s - > stream_groups [ group_idx ] - > streams [ j ] - > index ) {
found = 1 ;
if ( g )
* g = s - > stream_groups [ group_idx ] ;
break ;
}
}
} else
ss - > stream_list = STREAM_LIST_GROUP_IDX ;
ss - > list_id = strtol ( spec , & endptr , 0 ) ;
if ( spec = = endptr ) {
av_log ( logctx , AV_LOG_ERROR , " Expected stream group idx/ID, got: %s \n " , spec ) ;
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
if ( ! found )
match = 0 ;
spec = endptr ;
av_log ( logctx , AV_LOG_TRACE , " Parsed stream group %s: % " PRId64 " ; remainder: %s \n " ,
ss - > stream_list = = STREAM_LIST_GROUP_ID ? " ID " : " index " , ss - > list_id , spec ) ;
} else if ( * spec = = ' p ' & & * ( spec + 1 ) = = ' : ' ) {
int prog_id ;
int found = 0 ;
char * endptr ;
if ( ss - > stream_list ! = STREAM_LIST_ALL )
goto multiple_stream_lists ;
ss - > stream_list = STREAM_LIST_PROGRAM ;
spec + = 2 ;
prog_id = strtol ( spec , & endptr , 0 ) ;
/* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */
if ( spec = = endptr | | ( * endptr & & * endptr + + ! = ' : ' ) )
return AVERROR ( EINVAL ) ;
spec = endptr ;
if ( match ) {
for ( unsigned i = 0 ; i < s - > nb_programs ; i + + ) {
if ( s - > programs [ i ] - > id ! = prog_id )
continue ;
for ( unsigned j = 0 ; j < s - > programs [ i ] - > nb_stream_indexes ; j + + ) {
if ( st - > index = = s - > programs [ i ] - > stream_index [ j ] ) {
found = 1 ;
if ( p )
* p = s - > programs [ i ] ;
i = s - > nb_programs ;
break ;
}
}
}
ss - > list_id = strtol ( spec , & endptr , 0 ) ;
if ( spec = = endptr ) {
av_log ( logctx , AV_LOG_ERROR , " Expected program ID, got: %s \n " , spec ) ;
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
if ( ! found )
match = 0 ;
spec = endptr ;
av_log ( logctx , AV_LOG_TRACE ,
" Parsed program ID: % " PRId64 " ; remainder: %s \n " , ss - > list_id , spec ) ;
} else if ( * spec = = ' # ' | |
( * spec = = ' i ' & & * ( spec + 1 ) = = ' : ' ) ) {
int stream_id ;
char * endptr ;
if ( ss - > stream_list ! = STREAM_LIST_ALL )
goto multiple_stream_lists ;
ss - > stream_list = STREAM_LIST_STREAM_ID ;
spec + = 1 + ( * spec = = ' i ' ) ;
stream_id = strtol ( spec , & endptr , 0 ) ;
if ( spec = = endptr | | * endptr ) /* Disallow empty id and make sure we are at the end. */
return AVERROR ( EINVAL ) ;
return match & & ( stream_id = = st - > id ) ;
ss - > list_id = strtol ( spec , & endptr , 0 ) ;
if ( spec = = endptr ) {
av_log ( logctx , AV_LOG_ERROR , " Expected stream ID, got: %s \n " , spec ) ;
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
spec = endptr ;
av_log ( logctx , AV_LOG_TRACE ,
" Parsed stream ID: % " PRId64 " ; remainder: %s \n " , ss - > list_id , spec ) ;
// this terminates the specifier
break ;
} else if ( * spec = = ' m ' & & * ( spec + 1 ) = = ' : ' ) {
const AVDictionaryEntry * tag ;
char * key , * val ;
int ret ;
if ( match ) {
spec + = 2 ;
val = strchr ( spec , ' : ' ) ;
key = val ? av_strndup ( spec , val - spec ) : av_strdup ( spec ) ;
if ( ! key )
return AVERROR ( ENOMEM ) ;
tag = av_dict_get ( st - > metadata , key , NULL , 0 ) ;
if ( tag ) {
if ( ! val | | ! strcmp ( tag - > value , val + 1 ) )
ret = 1 ;
else
ret = 0 ;
} else
ret = 0 ;
av_freep ( & key ) ;
av_assert0 ( ! ss - > meta_key & & ! ss - > meta_val ) ;
spec + = 2 ;
ss - > meta_key = av_get_token ( & spec , " : " ) ;
if ( ! ss - > meta_key ) {
ret = AVERROR ( ENOMEM ) ;
goto fail ;
}
return match & & ret ;
} else if ( * spec = = ' u ' & & * ( spec + 1 ) = = ' \0 ' ) {
const AVCodecParameters * par = st - > codecpar ;
int val ;
if ( * spec = = ' : ' ) {
spec + + ;
ss - > meta_val = av_get_token ( & spec , " : " ) ;
if ( ! ss - > meta_val ) {
ret = AVERROR ( ENOMEM ) ;
goto fail ;
}
}
av_log ( logctx , AV_LOG_TRACE ,
" Parsed metadata: %s:%s; remainder: %s " , ss - > meta_key ,
ss - > meta_val ? ss - > meta_val : " <any value> " , spec ) ;
// this terminates the specifier
break ;
} else if ( * spec = = ' u ' & & ( * ( spec + 1 ) = = ' \0 ' | | * ( spec + 1 ) = = ' : ' ) ) {
ss - > usable_only = 1 ;
spec + + ;
av_log ( logctx , AV_LOG_ERROR , " Parsed 'usable only' \n " ) ;
// this terminates the specifier
break ;
} else
break ;
if ( * spec = = ' : ' )
spec + + ;
}
if ( * spec ) {
if ( ! allow_remainder ) {
av_log ( logctx , AV_LOG_ERROR ,
" Trailing garbage at the end of a stream specifier: %s \n " ,
spec ) ;
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
if ( * spec = = ' : ' )
spec + + ;
ss - > remainder = av_strdup ( spec ) ;
if ( ! ss - > remainder ) {
ret = AVERROR ( EINVAL ) ;
goto fail ;
}
}
return 0 ;
multiple_stream_lists :
av_log ( logctx , AV_LOG_ERROR ,
" Cannot combine multiple program/group designators in a "
" single stream specifier " ) ;
ret = AVERROR ( EINVAL ) ;
fail :
stream_specifier_uninit ( ss ) ;
return ret ;
}
unsigned stream_specifier_match ( const StreamSpecifier * ss ,
const AVFormatContext * s , const AVStream * st ,
void * logctx )
{
const AVStreamGroup * g = NULL ;
const AVProgram * p = NULL ;
int start_stream = 0 , nb_streams ;
int nb_matched = 0 ;
switch ( ss - > stream_list ) {
case STREAM_LIST_STREAM_ID :
// <n-th> stream with given ID makes no sense and should be impossible to request
av_assert0 ( ss - > idx < 0 ) ;
// return early if we know for sure the stream does not match
if ( st - > id ! = ss - > list_id )
return 0 ;
start_stream = st - > index ;
nb_streams = st - > index + 1 ;
break ;
case STREAM_LIST_ALL :
start_stream = ss - > idx > = 0 ? 0 : st - > index ;
nb_streams = st - > index + 1 ;
break ;
case STREAM_LIST_PROGRAM :
for ( unsigned i = 0 ; i < s - > nb_programs ; i + + ) {
if ( s - > programs [ i ] - > id = = ss - > list_id ) {
p = s - > programs [ i ] ;
break ;
}
}
if ( ! p ) {
av_log ( logctx , AV_LOG_WARNING , " No program with ID % " PRId64 " exists, "
" stream specifier can never match \n " , ss - > list_id ) ;
return 0 ;
}
nb_streams = p - > nb_stream_indexes ;
break ;
case STREAM_LIST_GROUP_ID :
for ( unsigned i = 0 ; i < s - > nb_stream_groups ; i + + ) {
if ( ss - > list_id = = s - > stream_groups [ i ] - > id ) {
g = s - > stream_groups [ i ] ;
break ;
}
}
// fall-through
case STREAM_LIST_GROUP_IDX :
if ( ss - > stream_list = = STREAM_LIST_GROUP_IDX & &
ss - > list_id > = 0 & & ss - > list_id < s - > nb_stream_groups )
g = s - > stream_groups [ ss - > list_id ] ;
if ( ! g ) {
av_log ( logctx , AV_LOG_WARNING , " No stream group with group %s % "
PRId64 " exists, stream specifier can never match \n " ,
ss - > stream_list = = STREAM_LIST_GROUP_ID ? " ID " : " index " ,
ss - > list_id ) ;
return 0 ;
}
nb_streams = g - > nb_streams ;
break ;
default : av_assert0 ( 0 ) ;
}
for ( int i = start_stream ; i < nb_streams ; i + + ) {
const AVStream * candidate = s - > streams [ g ? g - > streams [ i ] - > index :
p ? p - > stream_index [ i ] : i ] ;
if ( ss - > media_type ! = AVMEDIA_TYPE_UNKNOWN & &
( ss - > media_type ! = candidate - > codecpar - > codec_type | |
( ss - > no_apic & & ( candidate - > disposition & AV_DISPOSITION_ATTACHED_PIC ) ) ) )
continue ;
if ( ss - > meta_key ) {
const AVDictionaryEntry * tag = av_dict_get ( candidate - > metadata ,
ss - > meta_key , NULL , 0 ) ;
if ( ! tag )
continue ;
if ( ss - > meta_val & & strcmp ( tag - > value , ss - > meta_val ) )
continue ;
}
if ( ss - > usable_only ) {
const AVCodecParameters * par = candidate - > codecpar ;
switch ( par - > codec_type ) {
case AVMEDIA_TYPE_AUDIO :
val = par - > sample_rate & & par - > ch_layout . nb_channels ;
if ( par - > format = = AV_SAMPLE_FMT_NONE )
return 0 ;
if ( ! par - > sample_rate | | ! par - > ch_layout . nb_channels | |
par - > format = = AV_SAMPLE_FMT_NONE )
continue ;
break ;
case AVMEDIA_TYPE_VIDEO :
val = par - > width & & par - > height ;
if ( par - > format = = AV_PIX_FMT_NONE )
return 0 ;
if ( ! par - > width | | ! par - > height | | par - > format = = AV_PIX_FMT_NONE )
continue ;
break ;
case AVMEDIA_TYPE_UNKNOWN :
val = 0 ;
break ;
default :
val = 1 ;
break ;
continue ;
}
return match & & ( par - > codec_id ! = AV_CODEC_ID_NONE & & val ! = 0 ) ;
} else {
return AVERROR ( EINVAL ) ;
}
if ( st = = candidate )
return ss - > idx < 0 | | ss - > idx = = nb_matched ;
nb_matched + + ;
}
return match ;
return 0 ;
}
int check_stream_specifier ( AVFormatContext * s , AVStream * st , const char * spec )
{
int ret , index ;
char * endptr ;
const char * indexptr = NULL ;
const AVStreamGroup * g = NULL ;
const AVProgram * p = NULL ;
int nb_streams ;
StreamSpecifier ss ;
int ret ;
ret = match_stream_specifier ( s , st , spec , & indexptr , & g , & p ) ;
ret = stream_specifier_parse ( & ss , spec , 0 , NULL ) ;
if ( ret < 0 )
goto error ;
if ( ! indexptr )
return ret ;
index = strtol ( indexptr , & endptr , 0 ) ;
if ( * endptr ) { /* We can't have anything after the requested index. */
ret = AVERROR ( EINVAL ) ;
goto error ;
}
/* This is not really needed but saves us a loop for simple stream index specifiers. */
if ( spec = = indexptr )
return ( index = = st - > index ) ;
/* If we requested a matching stream index, we have to ensure st is that. */
nb_streams = g ? g - > nb_streams : ( p ? p - > nb_stream_indexes : s - > nb_streams ) ;
for ( int i = 0 ; i < nb_streams & & index > = 0 ; i + + ) {
unsigned idx = g ? g - > streams [ i ] - > index : ( p ? p - > stream_index [ i ] : i ) ;
const AVStream * candidate = s - > streams [ idx ] ;
ret = match_stream_specifier ( s , candidate , spec , NULL , NULL , NULL ) ;
if ( ret < 0 )
goto error ;
if ( ret > 0 & & index - - = = 0 & & st = = candidate )
return 1 ;
}
return 0 ;
error :
if ( ret = = AVERROR ( EINVAL ) )
av_log ( s , AV_LOG_ERROR , " Invalid stream specifier: %s. \n " , spec ) ;
ret = stream_specifier_match ( & ss , s , st , NULL ) ;
stream_specifier_uninit ( & ss ) ;
return ret ;
}