/*
* ASF compatible demuxer
* Copyright ( c ) 2000 , 2001 Fabrice Bellard
*
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* FFmpeg is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <inttypes.h>
# include "libavutil/attributes.h"
# include "libavutil/avassert.h"
# include "libavutil/avstring.h"
# include "libavutil/bswap.h"
# include "libavutil/common.h"
# include "libavutil/dict.h"
# include "libavutil/internal.h"
# include "libavutil/mathematics.h"
# include "libavutil/opt.h"
# include "avformat.h"
# include "avio_internal.h"
# include "avlanguage.h"
# include "demux.h"
# include "internal.h"
# include "riff.h"
# include "asf.h"
# include "asfcrypt.h"
typedef struct ASFPayload {
uint8_t type ;
uint16_t size ;
} ASFPayload ;
typedef struct ASFStream {
int num ;
unsigned char seq ;
/* use for reading */
AVPacket pkt ;
int frag_offset ;
int packet_obj_size ;
int timestamp ;
int64_t duration ;
int skip_to_key ;
int pkt_clean ;
int ds_span ; /* descrambling */
int ds_packet_size ;
int ds_chunk_size ;
int64_t packet_pos ;
uint16_t stream_language_index ;
int palette_changed ;
uint32_t palette [ 256 ] ;
int payload_ext_ct ;
ASFPayload payload [ 8 ] ;
} ASFStream ;
typedef struct ASFContext {
const AVClass * class ;
int asfid2avid [ 128 ] ; ///< conversion table from asf ID 2 AVStream ID
ASFStream streams [ 128 ] ; ///< it's max number and it's not that big
uint32_t stream_bitrates [ 128 ] ; ///< max number of streams, bitrate for each (for streaming)
AVRational dar [ 128 ] ;
char stream_languages [ 128 ] [ 6 ] ; ///< max number of streams, language for each (RFC1766, e.g. en-US)
/* non streamed additonnal info */
/* packet filling */
int packet_size_left ;
/* only for reading */
uint64_t data_offset ; ///< beginning of the first data packet
uint64_t data_object_offset ; ///< data object offset (excl. GUID & size)
uint64_t data_object_size ; ///< size of the data object
int index_read ;
ASFMainHeader hdr ;
int packet_flags ;
int packet_property ;
int packet_timestamp ;
int packet_segsizetype ;
int packet_segments ;
int packet_seq ;
int packet_replic_size ;
int packet_key_frame ;
int packet_padsize ;
unsigned int packet_frag_offset ;
unsigned int packet_frag_size ;
int64_t packet_frag_timestamp ;
int ts_is_pts ;
int packet_multi_size ;
int packet_time_delta ;
int packet_time_start ;
int64_t packet_pos ;
int stream_index ;
ASFStream * asf_st ; ///< currently decoded stream
int no_resync_search ;
int export_xmp ;
int uses_std_ecc ;
} ASFContext ;
static const AVOption options [ ] = {
{ " no_resync_search " , " Don't try to resynchronize by looking for a certain optional start code " , offsetof ( ASFContext , no_resync_search ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , AV_OPT_FLAG_DECODING_PARAM } ,
{ " export_xmp " , " Export full XMP metadata " , offsetof ( ASFContext , export_xmp ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , AV_OPT_FLAG_DECODING_PARAM } ,
{ NULL } ,
} ;
static const AVClass asf_class = {
. class_name = " asf demuxer " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
# undef NDEBUG
# include <assert.h>
# define ASF_MAX_STREAMS 127
# define FRAME_HEADER_SIZE 6
// Fix Me! FRAME_HEADER_SIZE may be different.
// (7 is known to be too large for GipsyGuitar.wmv)
# ifdef DEBUG
static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
0xce , 0x75 , 0xf8 , 0x7b , 0x8d , 0x46 , 0xd1 , 0x11 , 0x8d , 0x82 , 0x00 , 0x60 , 0x97 , 0xc9 , 0xa2 , 0xb2
} ;
static const ff_asf_guid asf_audio_conceal_none = {
// 0x40, 0xa4, 0xf1, 0x49, 0x4ece, 0x11d0, 0xa3, 0xac, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6
// New value lifted from avifile
0x00 , 0x57 , 0xfb , 0x20 , 0x55 , 0x5B , 0xCF , 0x11 , 0xa8 , 0xfd , 0x00 , 0x80 , 0x5f , 0x5c , 0x44 , 0x2b
} ;
# define PRINT_IF_GUID(g, cmp) \
if ( ! ff_guidcmp ( g , & ( cmp ) ) ) \
av_log ( NULL , AV_LOG_TRACE , " (GUID: %s) " , # cmp )
static void print_guid ( ff_asf_guid * g )
{
int i ;
PRINT_IF_GUID ( g , ff_asf_header ) ;
else PRINT_IF_GUID ( g , ff_asf_file_header ) ;
else PRINT_IF_GUID ( g , ff_asf_stream_header ) ;
else PRINT_IF_GUID ( g , ff_asf_audio_stream ) ;
else PRINT_IF_GUID ( g , asf_audio_conceal_none ) ;
else PRINT_IF_GUID ( g , ff_asf_video_stream ) ;
else PRINT_IF_GUID ( g , ff_asf_video_conceal_none ) ;
else PRINT_IF_GUID ( g , ff_asf_command_stream ) ;
else PRINT_IF_GUID ( g , ff_asf_comment_header ) ;
else PRINT_IF_GUID ( g , ff_asf_codec_comment_header ) ;
else PRINT_IF_GUID ( g , ff_asf_codec_comment1_header ) ;
else PRINT_IF_GUID ( g , ff_asf_data_header ) ;
else PRINT_IF_GUID ( g , ff_asf_simple_index_header ) ;
else PRINT_IF_GUID ( g , ff_asf_head1_guid ) ;
else PRINT_IF_GUID ( g , ff_asf_head2_guid ) ;
else PRINT_IF_GUID ( g , ff_asf_my_guid ) ;
else PRINT_IF_GUID ( g , ff_asf_ext_stream_header ) ;
else PRINT_IF_GUID ( g , ff_asf_extended_content_header ) ;
else PRINT_IF_GUID ( g , ff_asf_ext_stream_embed_stream_header ) ;
else PRINT_IF_GUID ( g , ff_asf_ext_stream_audio_stream ) ;
else PRINT_IF_GUID ( g , ff_asf_metadata_header ) ;
else PRINT_IF_GUID ( g , ff_asf_metadata_library_header ) ;
else PRINT_IF_GUID ( g , ff_asf_marker_header ) ;
else PRINT_IF_GUID ( g , stream_bitrate_guid ) ;
else PRINT_IF_GUID ( g , ff_asf_language_guid ) ;
else
av_log ( NULL , AV_LOG_TRACE , " (GUID: unknown) " ) ;
for ( i = 0 ; i < 16 ; i + + )
av_log ( NULL , AV_LOG_TRACE , " 0x%02x, " , ( * g ) [ i ] ) ;
av_log ( NULL , AV_LOG_TRACE , " } \n " ) ;
}
# undef PRINT_IF_GUID
# else
# define print_guid(g) while(0)
# endif
static int asf_probe ( const AVProbeData * pd )
{
/* check file header */
if ( ! ff_guidcmp ( pd - > buf , & ff_asf_header ) )
return AVPROBE_SCORE_MAX ;
else
return 0 ;
}
/* size of type 2 (BOOL) is 32bit for "Extended Content Description Object"
* but 16 bit for " Metadata Object " and " Metadata Library Object " */
static int get_value ( AVIOContext * pb , int type , int type2_size )
{
switch ( type ) {
case ASF_BOOL :
return ( type2_size = = 32 ) ? avio_rl32 ( pb ) : avio_rl16 ( pb ) ;
case ASF_DWORD :
return avio_rl32 ( pb ) ;
case ASF_QWORD :
return avio_rl64 ( pb ) ;
case ASF_WORD :
return avio_rl16 ( pb ) ;
default :
return INT_MIN ;
}
}
static void get_tag ( AVFormatContext * s , const char * key , int type , int len , int type2_size )
{
ASFContext * asf = s - > priv_data ;
char * value = NULL ;
int64_t off = avio_tell ( s - > pb ) ;
# define LEN 22
av_assert0 ( ( unsigned ) len < ( INT_MAX - LEN ) / 2 ) ;
if ( ! asf - > export_xmp & & ! strncmp ( key , " xmp " , 3 ) )
goto finish ;
value = av_malloc ( 2 * len + LEN ) ;
if ( ! value )
goto finish ;
switch ( type ) {
case ASF_UNICODE :
avio_get_str16le ( s - > pb , len , value , 2 * len + 1 ) ;
break ;
case - 1 : // ASCI
avio_read ( s - > pb , value , len ) ;
value [ len ] = 0 ;
break ;
case ASF_BYTE_ARRAY :
if ( ff_asf_handle_byte_array ( s , key , len ) > 0 )
av_log ( s , AV_LOG_VERBOSE , " Unsupported byte array in tag %s. \n " , key ) ;
goto finish ;
case ASF_BOOL :
case ASF_DWORD :
case ASF_QWORD :
case ASF_WORD : {
uint64_t num = get_value ( s - > pb , type , type2_size ) ;
snprintf ( value , LEN , " % " PRIu64 , num ) ;
break ;
}
case ASF_GUID :
av_log ( s , AV_LOG_DEBUG , " Unsupported GUID value in tag %s. \n " , key ) ;
goto finish ;
default :
av_log ( s , AV_LOG_DEBUG ,
" Unsupported value type %d in tag %s. \n " , type , key ) ;
goto finish ;
}
if ( * value )
av_dict_set ( & s - > metadata , key , value , 0 ) ;
finish :
av_freep ( & value ) ;
avio_seek ( s - > pb , off + len , SEEK_SET ) ;
}
static int asf_read_file_properties ( AVFormatContext * s )
{
ASFContext * asf = s - > priv_data ;
AVIOContext * pb = s - > pb ;
ff_get_guid ( pb , & asf - > hdr . guid ) ;
asf - > hdr . file_size = avio_rl64 ( pb ) ;
asf - > hdr . create_time = avio_rl64 ( pb ) ;
avio_rl64 ( pb ) ; /* number of packets */
asf - > hdr . play_time = avio_rl64 ( pb ) ;
asf - > hdr . send_time = avio_rl64 ( pb ) ;
asf - > hdr . preroll = avio_rl32 ( pb ) ;
asf - > hdr . ignore = avio_rl32 ( pb ) ;
asf - > hdr . flags = avio_rl32 ( pb ) ;
asf - > hdr . min_pktsize = avio_rl32 ( pb ) ;
asf - > hdr . max_pktsize = avio_rl32 ( pb ) ;
if ( asf - > hdr . min_pktsize > = ( 1U < < 29 ) )
return AVERROR_INVALIDDATA ;
asf - > hdr . max_bitrate = avio_rl32 ( pb ) ;
s - > packet_size = asf - > hdr . max_pktsize ;
return 0 ;
}
static int asf_read_stream_properties ( AVFormatContext * s , int64_t size )
{
ASFContext * asf = s - > priv_data ;
AVIOContext * pb = s - > pb ;
AVStream * st ;
FFStream * sti ;
ASFStream * asf_st ;
ff_asf_guid g ;
enum AVMediaType type ;
int type_specific_size , sizeX ;
unsigned int tag1 ;
int64_t pos1 , pos2 , start_time ;
int test_for_ext_stream_audio , is_dvr_ms_audio = 0 ;
if ( s - > nb_streams = = ASF_MAX_STREAMS ) {
av_log ( s , AV_LOG_ERROR , " too many streams \n " ) ;
return AVERROR ( EINVAL ) ;
}
pos1 = avio_tell ( pb ) ;
st = avformat_new_stream ( s , NULL ) ;
if ( ! st )
return AVERROR ( ENOMEM ) ;
sti = ffstream ( st ) ;
avpriv_set_pts_info ( st , 32 , 1 , 1000 ) ; /* 32 bit pts in ms */
start_time = asf - > hdr . preroll ;
if ( ! ( asf - > hdr . flags & 0x01 ) ) { // if we aren't streaming...
int64_t fsize = avio_size ( pb ) ;
if ( fsize < = 0 | | ( int64_t ) asf - > hdr . file_size < = 0 | |
FFABS ( fsize - ( int64_t ) asf - > hdr . file_size ) < FFMIN ( fsize , asf - > hdr . file_size ) / 20 )
st - > duration = asf - > hdr . play_time /
( 10000000 / 1000 ) - start_time ;
}
ff_get_guid ( pb , & g ) ;
test_for_ext_stream_audio = 0 ;
if ( ! ff_guidcmp ( & g , & ff_asf_audio_stream ) ) {
type = AVMEDIA_TYPE_AUDIO ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_video_stream ) ) {
type = AVMEDIA_TYPE_VIDEO ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_jfif_media ) ) {
type = AVMEDIA_TYPE_VIDEO ;
st - > codecpar - > codec_id = AV_CODEC_ID_MJPEG ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_command_stream ) ) {
type = AVMEDIA_TYPE_DATA ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_ext_stream_embed_stream_header ) ) {
test_for_ext_stream_audio = 1 ;
type = AVMEDIA_TYPE_UNKNOWN ;
} else {
return - 1 ;
}
ff_get_guid ( pb , & g ) ;
avio_skip ( pb , 8 ) ; /* total_size */
type_specific_size = avio_rl32 ( pb ) ;
avio_rl32 ( pb ) ;
st - > id = avio_rl16 ( pb ) & 0x7f ; /* stream id */
// mapping of asf ID to AV stream ID;
asf - > asfid2avid [ st - > id ] = s - > nb_streams - 1 ;
asf_st = & asf - > streams [ st - > id ] ;
avio_rl32 ( pb ) ;
if ( test_for_ext_stream_audio ) {
ff_get_guid ( pb , & g ) ;
if ( ! ff_guidcmp ( & g , & ff_asf_ext_stream_audio_stream ) ) {
type = AVMEDIA_TYPE_AUDIO ;
is_dvr_ms_audio = 1 ;
ff_get_guid ( pb , & g ) ;
avio_rl32 ( pb ) ;
avio_rl32 ( pb ) ;
avio_rl32 ( pb ) ;
ff_get_guid ( pb , & g ) ;
avio_rl32 ( pb ) ;
}
}
st - > codecpar - > codec_type = type ;
if ( type = = AVMEDIA_TYPE_AUDIO ) {
int ret = ff_get_wav_header ( s , pb , st - > codecpar , type_specific_size , 0 ) ;
if ( ret < 0 )
return ret ;
if ( is_dvr_ms_audio ) {
// codec_id and codec_tag are unreliable in dvr_ms
// files. Set them later by probing stream.
sti - > request_probe = 1 ;
st - > codecpar - > codec_tag = 0 ;
}
if ( st - > codecpar - > codec_id = = AV_CODEC_ID_AAC )
sti - > need_parsing = AVSTREAM_PARSE_NONE ;
else
sti - > need_parsing = AVSTREAM_PARSE_FULL ;
/* We have to init the frame size at some point .... */
pos2 = avio_tell ( pb ) ;
if ( size > = ( pos2 + 8 - pos1 + 24 ) ) {
asf_st - > ds_span = avio_r8 ( pb ) ;
asf_st - > ds_packet_size = avio_rl16 ( pb ) ;
asf_st - > ds_chunk_size = avio_rl16 ( pb ) ;
avio_rl16 ( pb ) ; // ds_data_size
avio_r8 ( pb ) ; // ds_silence_data
}
if ( asf_st - > ds_span > 1 ) {
if ( ! asf_st - > ds_chunk_size | |
( asf_st - > ds_packet_size / asf_st - > ds_chunk_size < = 1 ) | |
asf_st - > ds_packet_size % asf_st - > ds_chunk_size )
asf_st - > ds_span = 0 ; // disable descrambling
}
} else if ( type = = AVMEDIA_TYPE_VIDEO & &
size - ( avio_tell ( pb ) - pos1 + 24 ) > = 51 ) {
avio_rl32 ( pb ) ;
avio_rl32 ( pb ) ;
avio_r8 ( pb ) ;
avio_rl16 ( pb ) ; /* size */
sizeX = avio_rl32 ( pb ) ; /* size */
st - > codecpar - > width = avio_rl32 ( pb ) ;
st - > codecpar - > height = avio_rl32 ( pb ) ;
/* not available for asf */
avio_rl16 ( pb ) ; /* panes */
st - > codecpar - > bits_per_coded_sample = avio_rl16 ( pb ) ; /* depth */
tag1 = avio_rl32 ( pb ) ;
avio_skip ( pb , 20 ) ;
if ( sizeX > 40 ) {
if ( size < sizeX - 40 | | sizeX - 40 > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE )
return AVERROR_INVALIDDATA ;
st - > codecpar - > extradata_size = ffio_limit ( pb , sizeX - 40 ) ;
st - > codecpar - > extradata = av_mallocz ( st - > codecpar - > extradata_size +
AV_INPUT_BUFFER_PADDING_SIZE ) ;
if ( ! st - > codecpar - > extradata )
return AVERROR ( ENOMEM ) ;
avio_read ( pb , st - > codecpar - > extradata , st - > codecpar - > extradata_size ) ;
}
/* Extract palette from extradata if bpp <= 8 */
/* This code assumes that extradata contains only palette */
/* This is true for all paletted codecs implemented in libavcodec */
if ( st - > codecpar - > extradata_size & & ( st - > codecpar - > bits_per_coded_sample < = 8 ) ) {
# if HAVE_BIGENDIAN
int i ;
for ( i = 0 ; i < FFMIN ( st - > codecpar - > extradata_size , AVPALETTE_SIZE ) / 4 ; i + + )
asf_st - > palette [ i ] = av_bswap32 ( ( ( uint32_t * ) st - > codecpar - > extradata ) [ i ] ) ;
# else
memcpy ( asf_st - > palette , st - > codecpar - > extradata ,
FFMIN ( st - > codecpar - > extradata_size , AVPALETTE_SIZE ) ) ;
# endif
asf_st - > palette_changed = 1 ;
}
st - > codecpar - > codec_tag = tag1 ;
st - > codecpar - > codec_id = ff_codec_get_id ( ff_codec_bmp_tags , tag1 ) ;
if ( tag1 = = MKTAG ( ' D ' , ' V ' , ' R ' , ' ' ) ) {
sti - > need_parsing = AVSTREAM_PARSE_FULL ;
/* issue658 contains wrong w/h and MS even puts a fake seq header
* with wrong w / h in extradata while a correct one is in the stream .
* maximum lameness */
st - > codecpar - > width =
st - > codecpar - > height = 0 ;
av_freep ( & st - > codecpar - > extradata ) ;
st - > codecpar - > extradata_size = 0 ;
}
if ( st - > codecpar - > codec_id = = AV_CODEC_ID_H264 )
sti - > need_parsing = AVSTREAM_PARSE_FULL_ONCE ;
if ( st - > codecpar - > codec_id = = AV_CODEC_ID_MPEG4 )
sti - > need_parsing = AVSTREAM_PARSE_FULL_ONCE ;
}
pos2 = avio_tell ( pb ) ;
avio_skip ( pb , size - ( pos2 - pos1 + 24 ) ) ;
return 0 ;
}
static int asf_read_ext_stream_properties ( AVFormatContext * s )
{
ASFContext * asf = s - > priv_data ;
AVIOContext * pb = s - > pb ;
ff_asf_guid g ;
int ext_len , payload_ext_ct , stream_ct , i ;
uint32_t leak_rate , stream_num ;
unsigned int stream_languageid_index ;
avio_rl64 ( pb ) ; // starttime
avio_rl64 ( pb ) ; // endtime
leak_rate = avio_rl32 ( pb ) ; // leak-datarate
avio_rl32 ( pb ) ; // bucket-datasize
avio_rl32 ( pb ) ; // init-bucket-fullness
avio_rl32 ( pb ) ; // alt-leak-datarate
avio_rl32 ( pb ) ; // alt-bucket-datasize
avio_rl32 ( pb ) ; // alt-init-bucket-fullness
avio_rl32 ( pb ) ; // max-object-size
avio_rl32 ( pb ) ; // flags (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved)
stream_num = avio_rl16 ( pb ) ; // stream-num
stream_languageid_index = avio_rl16 ( pb ) ; // stream-language-id-index
if ( stream_num < 128 )
asf - > streams [ stream_num ] . stream_language_index = stream_languageid_index ;
avio_rl64 ( pb ) ; // avg frametime in 100ns units
stream_ct = avio_rl16 ( pb ) ; // stream-name-count
payload_ext_ct = avio_rl16 ( pb ) ; // payload-extension-system-count
if ( stream_num < 128 ) {
asf - > stream_bitrates [ stream_num ] = leak_rate ;
asf - > streams [ stream_num ] . payload_ext_ct = 0 ;
}
for ( i = 0 ; i < stream_ct ; i + + ) {
avio_rl16 ( pb ) ;
ext_len = avio_rl16 ( pb ) ;
avio_skip ( pb , ext_len ) ;
}
for ( i = 0 ; i < payload_ext_ct ; i + + ) {
int size ;
ff_get_guid ( pb , & g ) ;
size = avio_rl16 ( pb ) ;
ext_len = avio_rl32 ( pb ) ;
if ( ext_len < 0 )
return AVERROR_INVALIDDATA ;
avio_skip ( pb , ext_len ) ;
if ( stream_num < 128 & & i < FF_ARRAY_ELEMS ( asf - > streams [ stream_num ] . payload ) ) {
ASFPayload * p = & asf - > streams [ stream_num ] . payload [ i ] ;
p - > type = g [ 0 ] ;
p - > size = size ;
av_log ( s , AV_LOG_DEBUG , " Payload extension %x %d \n " , g [ 0 ] , p - > size ) ;
asf - > streams [ stream_num ] . payload_ext_ct + + ;
}
}
return 0 ;
}
static int asf_read_content_desc ( AVFormatContext * s )
{
AVIOContext * pb = s - > pb ;
int len1 , len2 , len3 , len4 , len5 ;
len1 = avio_rl16 ( pb ) ;
len2 = avio_rl16 ( pb ) ;
len3 = avio_rl16 ( pb ) ;
len4 = avio_rl16 ( pb ) ;
len5 = avio_rl16 ( pb ) ;
get_tag ( s , " title " , 0 , len1 , 32 ) ;
get_tag ( s , " author " , 0 , len2 , 32 ) ;
get_tag ( s , " copyright " , 0 , len3 , 32 ) ;
get_tag ( s , " comment " , 0 , len4 , 32 ) ;
avio_skip ( pb , len5 ) ;
return 0 ;
}
static int asf_read_ext_content_desc ( AVFormatContext * s )
{
AVIOContext * pb = s - > pb ;
ASFContext * asf = s - > priv_data ;
int desc_count , i , ret ;
desc_count = avio_rl16 ( pb ) ;
for ( i = 0 ; i < desc_count ; i + + ) {
int name_len , value_type , value_len ;
char name [ 1024 ] ;
name_len = avio_rl16 ( pb ) ;
if ( name_len % 2 ) // must be even, broken lavf versions wrote len-1
name_len + = 1 ;
if ( ( ret = avio_get_str16le ( pb , name_len , name , sizeof ( name ) ) ) < name_len )
avio_skip ( pb , name_len - ret ) ;
value_type = avio_rl16 ( pb ) ;
value_len = avio_rl16 ( pb ) ;
if ( ! value_type & & value_len % 2 )
value_len + = 1 ;
/* My sample has that stream set to 0 maybe that mean the container.
* ASF stream count starts at 1. I am using 0 to the container value
* since it ' s unused . */
if ( ! strcmp ( name , " AspectRatioX " ) )
asf - > dar [ 0 ] . num = get_value ( s - > pb , value_type , 32 ) ;
else if ( ! strcmp ( name , " AspectRatioY " ) )
asf - > dar [ 0 ] . den = get_value ( s - > pb , value_type , 32 ) ;
else
get_tag ( s , name , value_type , value_len , 32 ) ;
}
return 0 ;
}
static int asf_read_language_list ( AVFormatContext * s )
{
AVIOContext * pb = s - > pb ;
ASFContext * asf = s - > priv_data ;
int j , ret ;
int stream_count = avio_rl16 ( pb ) ;
for ( j = 0 ; j < stream_count ; j + + ) {
char lang [ 6 ] ;
unsigned int lang_len = avio_r8 ( pb ) ;
if ( ( ret = avio_get_str16le ( pb , lang_len , lang ,
sizeof ( lang ) ) ) < lang_len )
avio_skip ( pb , lang_len - ret ) ;
if ( j < 128 )
av_strlcpy ( asf - > stream_languages [ j ] , lang ,
sizeof ( * asf - > stream_languages ) ) ;
}
return 0 ;
}
static int asf_read_metadata ( AVFormatContext * s )
{
AVIOContext * pb = s - > pb ;
ASFContext * asf = s - > priv_data ;
int n , stream_num , name_len_utf16 , name_len_utf8 , value_len ;
int ret , i ;
n = avio_rl16 ( pb ) ;
for ( i = 0 ; i < n ; i + + ) {
uint8_t * name ;
int value_type ;
avio_rl16 ( pb ) ; // lang_list_index
stream_num = avio_rl16 ( pb ) ;
name_len_utf16 = avio_rl16 ( pb ) ;
value_type = avio_rl16 ( pb ) ; /* value_type */
value_len = avio_rl32 ( pb ) ;
if ( value_len < 0 | | value_len > UINT16_MAX )
return AVERROR_INVALIDDATA ;
name_len_utf8 = 2 * name_len_utf16 + 1 ;
name = av_malloc ( name_len_utf8 ) ;
if ( ! name )
return AVERROR ( ENOMEM ) ;
if ( ( ret = avio_get_str16le ( pb , name_len_utf16 , name , name_len_utf8 ) ) < name_len_utf16 )
avio_skip ( pb , name_len_utf16 - ret ) ;
av_log ( s , AV_LOG_TRACE , " %d stream %d name_len %2d type %d len %4d <%s> \n " ,
i , stream_num , name_len_utf16 , value_type , value_len , name ) ;
if ( ! strcmp ( name , " AspectRatioX " ) ) {
int aspect_x = get_value ( s - > pb , value_type , 16 ) ;
if ( stream_num < 128 )
asf - > dar [ stream_num ] . num = aspect_x ;
} else if ( ! strcmp ( name , " AspectRatioY " ) ) {
int aspect_y = get_value ( s - > pb , value_type , 16 ) ;
if ( stream_num < 128 )
asf - > dar [ stream_num ] . den = aspect_y ;
} else {
get_tag ( s , name , value_type , value_len , 16 ) ;
}
av_freep ( & name ) ;
}
return 0 ;
}
static int asf_read_marker ( AVFormatContext * s )
{
AVIOContext * pb = s - > pb ;
ASFContext * asf = s - > priv_data ;
int i , count , name_len , ret ;
char name [ 1024 ] ;
avio_rl64 ( pb ) ; // reserved 16 bytes
avio_rl64 ( pb ) ; // ...
count = avio_rl32 ( pb ) ; // markers count
avio_rl16 ( pb ) ; // reserved 2 bytes
name_len = avio_rl16 ( pb ) ; // name length
avio_skip ( pb , name_len ) ;
for ( i = 0 ; i < count ; i + + ) {
int64_t pres_time ;
int name_len ;
if ( avio_feof ( pb ) )
return AVERROR_INVALIDDATA ;
avio_rl64 ( pb ) ; // offset, 8 bytes
pres_time = avio_rl64 ( pb ) ; // presentation time
pres_time - = asf - > hdr . preroll * 10000 ;
avio_rl16 ( pb ) ; // entry length
avio_rl32 ( pb ) ; // send time
avio_rl32 ( pb ) ; // flags
name_len = avio_rl32 ( pb ) ; // name length
if ( ( unsigned ) name_len > INT_MAX / 2 )
return AVERROR_INVALIDDATA ;
if ( ( ret = avio_get_str16le ( pb , name_len * 2 , name ,
sizeof ( name ) ) ) < name_len )
avio_skip ( pb , name_len - ret ) ;
avpriv_new_chapter ( s , i , ( AVRational ) { 1 , 10000000 } , pres_time ,
AV_NOPTS_VALUE , name ) ;
}
return 0 ;
}
static int asf_read_header ( AVFormatContext * s )
{
ASFContext * asf = s - > priv_data ;
ff_asf_guid g ;
AVIOContext * pb = s - > pb ;
int i ;
int64_t gsize ;
ff_get_guid ( pb , & g ) ;
if ( ff_guidcmp ( & g , & ff_asf_header ) )
return AVERROR_INVALIDDATA ;
avio_rl64 ( pb ) ;
avio_rl32 ( pb ) ;
avio_r8 ( pb ) ;
avio_r8 ( pb ) ;
memset ( & asf - > asfid2avid , - 1 , sizeof ( asf - > asfid2avid ) ) ;
for ( i = 0 ; i < 128 ; i + + )
asf - > streams [ i ] . stream_language_index = 128 ; // invalid stream index means no language info
for ( ; ; ) {
uint64_t gpos = avio_tell ( pb ) ;
int ret = 0 ;
ff_get_guid ( pb , & g ) ;
gsize = avio_rl64 ( pb ) ;
print_guid ( & g ) ;
if ( ! ff_guidcmp ( & g , & ff_asf_data_header ) ) {
asf - > data_object_offset = avio_tell ( pb ) ;
/* If not streaming, gsize is not unlimited (how?),
* and there is enough space in the file . . */
if ( ! ( asf - > hdr . flags & 0x01 ) & & gsize > = 100 )
asf - > data_object_size = gsize - 24 ;
else
asf - > data_object_size = ( uint64_t ) - 1 ;
break ;
}
if ( gsize < 24 )
return AVERROR_INVALIDDATA ;
if ( ! ff_guidcmp ( & g , & ff_asf_file_header ) ) {
ret = asf_read_file_properties ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_stream_header ) ) {
ret = asf_read_stream_properties ( s , gsize ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_comment_header ) ) {
asf_read_content_desc ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_language_guid ) ) {
asf_read_language_list ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_extended_content_header ) ) {
asf_read_ext_content_desc ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_metadata_header ) ) {
asf_read_metadata ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_metadata_library_header ) ) {
asf_read_metadata ( s ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_ext_stream_header ) ) {
asf_read_ext_stream_properties ( s ) ;
// there could be an optional stream properties object to follow
// if so the next iteration will pick it up
continue ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_head1_guid ) ) {
ff_get_guid ( pb , & g ) ;
avio_skip ( pb , 6 ) ;
continue ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_marker_header ) ) {
asf_read_marker ( s ) ;
} else if ( avio_feof ( pb ) ) {
return AVERROR_EOF ;
} else {
if ( ! s - > keylen ) {
if ( ! ff_guidcmp ( & g , & ff_asf_content_encryption ) ) {
AVPacket * const pkt = ffformatcontext ( s ) - > parse_pkt ;
unsigned int len ;
av_log ( s , AV_LOG_WARNING ,
" DRM protected stream detected, decoding will likely fail! \n " ) ;
len = avio_rl32 ( pb ) ;
av_log ( s , AV_LOG_DEBUG , " Secret data: \n " ) ;
if ( ( ret = av_get_packet ( pb , pkt , len ) ) < 0 )
return ret ;
av_hex_dump_log ( s , AV_LOG_DEBUG , pkt - > data , pkt - > size ) ;
av_packet_unref ( pkt ) ;
len = avio_rl32 ( pb ) ;
if ( len > UINT16_MAX )
return AVERROR_INVALIDDATA ;
get_tag ( s , " ASF_Protection_Type " , - 1 , len , 32 ) ;
len = avio_rl32 ( pb ) ;
if ( len > UINT16_MAX )
return AVERROR_INVALIDDATA ;
get_tag ( s , " ASF_Key_ID " , - 1 , len , 32 ) ;
len = avio_rl32 ( pb ) ;
if ( len > UINT16_MAX )
return AVERROR_INVALIDDATA ;
get_tag ( s , " ASF_License_URL " , - 1 , len , 32 ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_ext_content_encryption ) ) {
av_log ( s , AV_LOG_WARNING ,
" Ext DRM protected stream detected, decoding will likely fail! \n " ) ;
av_dict_set ( & s - > metadata , " encryption " , " ASF Extended Content Encryption " , 0 ) ;
} else if ( ! ff_guidcmp ( & g , & ff_asf_digital_signature ) ) {
av_log ( s , AV_LOG_INFO , " Digital signature detected! \n " ) ;
}
}
}
if ( ret < 0 )
return ret ;
if ( avio_tell ( pb ) ! = gpos + gsize )
av_log ( s , AV_LOG_DEBUG ,
" gpos mismatch our pos=% " PRIu64 " , end=% " PRId64 " \n " ,
avio_tell ( pb ) - gpos , gsize ) ;
avio_seek ( pb , gpos + gsize , SEEK_SET ) ;
}
ff_get_guid ( pb , & g ) ;
avio_rl64 ( pb ) ;
avio_r8 ( pb ) ;
avio_r8 ( pb ) ;
if ( avio_feof ( pb ) )
return AVERROR_EOF ;
asf - > data_offset = avio_tell ( pb ) ;
asf - > packet_size_left = 0 ;
for ( i = 0 ; i < 128 ; i + + ) {
int stream_num = asf - > asfid2avid [ i ] ;
if ( stream_num > = 0 ) {
AVStream * st = s - > streams [ stream_num ] ;
if ( ! st - > codecpar - > bit_rate )
st - > codecpar - > bit_rate = asf - > stream_bitrates [ i ] ;
if ( asf - > dar [ i ] . num > 0 & & asf - > dar [ i ] . den > 0 ) {
av_reduce ( & st - > sample_aspect_ratio . num ,
& st - > sample_aspect_ratio . den ,
asf - > dar [ i ] . num , asf - > dar [ i ] . den , INT_MAX ) ;
} else if ( ( asf - > dar [ 0 ] . num > 0 ) & & ( asf - > dar [ 0 ] . den > 0 ) & &
// Use ASF container value if the stream doesn't set AR.
( st - > codecpar - > codec_type = = AVMEDIA_TYPE_VIDEO ) )
av_reduce ( & st - > sample_aspect_ratio . num ,
& st - > sample_aspect_ratio . den ,
asf - > dar [ 0 ] . num , asf - > dar [ 0 ] . den , INT_MAX ) ;
av_log ( s , AV_LOG_TRACE , " i=%d, st->codecpar->codec_type:%d, asf->dar %d:%d sar=%d:%d \n " ,
i , st - > codecpar - > codec_type , asf - > dar [ i ] . num , asf - > dar [ i ] . den ,
st - > sample_aspect_ratio . num , st - > sample_aspect_ratio . den ) ;
// copy and convert language codes to the frontend
if ( asf - > streams [ i ] . stream_language_index < 128 ) {
const char * rfc1766 = asf - > stream_languages [ asf - > streams [ i ] . stream_language_index ] ;
if ( rfc1766 & & strlen ( rfc1766 ) > 1 ) {
const char primary_tag [ 3 ] = { rfc1766 [ 0 ] , rfc1766 [ 1 ] , ' \0 ' } ; // ignore country code if any
const char * iso6392 = ff_convert_lang_to ( primary_tag ,
AV_LANG_ISO639_2_BIBL ) ;
if ( iso6392 )
av_dict_set ( & st - > metadata , " language " , iso6392 , 0 ) ;
}
}
}
}
ff_metadata_conv ( & s - > metadata , NULL , ff_asf_metadata_conv ) ;
return 0 ;
}
# define DO_2BITS(bits, var, defval) \
switch ( bits & 3 ) { \
case 3 : \
var = avio_rl32 ( pb ) ; \
rsize + = 4 ; \
break ; \
case 2 : \
var = avio_rl16 ( pb ) ; \
rsize + = 2 ; \
break ; \
case 1 : \
var = avio_r8 ( pb ) ; \
rsize + + ; \
break ; \
default : \
var = defval ; \
break ; \
}
/**
* Load a single ASF packet into the demuxer .
* @ param s demux context
* @ param pb context to read data from
* @ return 0 on success , < 0 on error
*/
static int asf_get_packet ( AVFormatContext * s , AVIOContext * pb )
{
ASFContext * asf = s - > priv_data ;
uint32_t packet_length , padsize ;
int rsize = 8 ;
int c , d , e , off ;
if ( asf - > uses_std_ecc > 0 ) {
// if we do not know packet size, allow skipping up to 32 kB
off = 32768 ;
if ( asf - > no_resync_search )
off = 3 ;
// else if (s->packet_size > 0 && !asf->uses_std_ecc)
// off = (avio_tell(pb) - ffformatcontext(s)->data_offset) % s->packet_size + 3;
c = d = e = - 1 ;
while ( off - - > 0 ) {
c = d ;
d = e ;
e = avio_r8 ( pb ) ;
if ( c = = 0x82 & & ! d & & ! e )
break ;
}
if ( c ! = 0x82 ) {
/* This code allows handling of -EAGAIN at packet boundaries (i.e.
* if the packet sync code above triggers - EAGAIN ) . This does not
* imply complete - EAGAIN handling support at random positions in
* the stream . */
if ( pb - > error = = AVERROR ( EAGAIN ) )
return AVERROR ( EAGAIN ) ;
if ( ! avio_feof ( pb ) )
av_log ( s , AV_LOG_ERROR ,
" ff asf bad header %x at:% " PRId64 " \n " , c , avio_tell ( pb ) ) ;
}
if ( ( c & 0x8f ) = = 0x82 ) {
if ( d | | e ) {
if ( ! avio_feof ( pb ) )
av_log ( s , AV_LOG_ERROR , " ff asf bad non zero \n " ) ;
return AVERROR_INVALIDDATA ;
}
c = avio_r8 ( pb ) ;
d = avio_r8 ( pb ) ;
rsize + = 3 ;
} else if ( ! avio_feof ( pb ) ) {
avio_seek ( pb , - 1 , SEEK_CUR ) ; // FIXME
}
} else {
c = avio_r8 ( pb ) ;
if ( c & 0x80 ) {
rsize + + ;
if ( ! ( c & 0x60 ) ) {
d = avio_r8 ( pb ) ;
e = avio_r8 ( pb ) ;
avio_seek ( pb , ( c & 0xF ) - 2 , SEEK_CUR ) ;
rsize + = c & 0xF ;
}
if ( c ! = 0x82 )
avpriv_request_sample ( s , " Invalid ECC byte " ) ;
if ( ! asf - > uses_std_ecc )
asf - > uses_std_ecc = ( c = = 0x82 & & ! d & & ! e ) ? 1 : - 1 ;
c = avio_r8 ( pb ) ;
} else
asf - > uses_std_ecc = - 1 ;
d = avio_r8 ( pb ) ;
}
asf - > packet_flags = c ;
asf - > packet_property = d ;
DO_2BITS ( asf - > packet_flags > > 5 , packet_length , s - > packet_size ) ;
DO_2BITS ( asf - > packet_flags > > 1 , padsize , 0 ) ; // sequence ignored
DO_2BITS ( asf - > packet_flags > > 3 , padsize , 0 ) ; // padding length
// the following checks prevent overflows and infinite loops
if ( ! packet_length | | packet_length > = ( 1U < < 29 ) ) {
av_log ( s , AV_LOG_ERROR ,
" invalid packet_length % " PRIu32 " at:% " PRId64 " \n " ,
packet_length , avio_tell ( pb ) ) ;
return AVERROR_INVALIDDATA ;
}
if ( padsize > = packet_length ) {
av_log ( s , AV_LOG_ERROR ,
" invalid padsize % " PRIu32 " at:% " PRId64 " \n " , padsize , avio_tell ( pb ) ) ;
return AVERROR_INVALIDDATA ;
}
asf - > packet_timestamp = avio_rl32 ( pb ) ;
avio_rl16 ( pb ) ; /* duration */
// rsize has at least 11 bytes which have to be present
if ( asf - > packet_flags & 0x01 ) {
asf - > packet_segsizetype = avio_r8 ( pb ) ;
rsize + + ;
asf - > packet_segments = asf - > packet_segsizetype & 0x3f ;
} else {
asf - > packet_segments = 1 ;
asf - > packet_segsizetype = 0x80 ;
}
if ( rsize > packet_length - padsize ) {
asf - > packet_size_left = 0 ;
av_log ( s , AV_LOG_ERROR ,
" invalid packet header length %d for pktlen % " PRIu32 " -% " PRIu32 " at % " PRId64 " \n " ,
rsize , packet_length , padsize , avio_tell ( pb ) ) ;
return AVERROR_INVALIDDATA ;
}
asf - > packet_size_left = packet_length - padsize - rsize ;
if ( packet_length < asf - > hdr . min_pktsize )
padsize + = asf - > hdr . min_pktsize - packet_length ;
asf - > packet_padsize = padsize ;
av_log ( s , AV_LOG_TRACE , " packet: size=%d padsize=%d left=%d \n " ,
s - > packet_size , asf - > packet_padsize , asf - > packet_size_left ) ;
return 0 ;
}
/**
*
* @ return < 0 if error
*/
static int asf_read_frame_header ( AVFormatContext * s , AVIOContext * pb )
{
ASFContext * asf = s - > priv_data ;
ASFStream * asfst ;
int rsize = 1 ;
int num = avio_r8 ( pb ) ;
int i ;
int64_t ts0 , ts1 av_unused ;
asf - > packet_segments - - ;
asf - > packet_key_frame = num > > 7 ;
asf - > stream_index = asf - > asfid2avid [ num & 0x7f ] ;
asfst = & asf - > streams [ num & 0x7f ] ;
// sequence should be ignored!
DO_2BITS ( asf - > packet_property > > 4 , asf - > packet_seq , 0 ) ;
DO_2BITS ( asf - > packet_property > > 2 , asf - > packet_frag_offset , 0 ) ;
DO_2BITS ( asf - > packet_property , asf - > packet_replic_size , 0 ) ;
av_log ( asf , AV_LOG_TRACE , " key:%d stream:%d seq:%d offset:%d replic_size:%d num:%X packet_property %X \n " ,
asf - > packet_key_frame , asf - > stream_index , asf - > packet_seq ,
asf - > packet_frag_offset , asf - > packet_replic_size , num , asf - > packet_property ) ;
if ( rsize + ( int64_t ) asf - > packet_replic_size > asf - > packet_size_left ) {
av_log ( s , AV_LOG_ERROR , " packet_replic_size %d is invalid \n " , asf - > packet_replic_size ) ;
return AVERROR_INVALIDDATA ;
}
if ( asf - > packet_replic_size > = 8 ) {
int64_t end = avio_tell ( pb ) + asf - > packet_replic_size ;
AVRational aspect ;
asfst - > packet_obj_size = avio_rl32 ( pb ) ;
if ( asfst - > packet_obj_size > = ( 1 < < 24 ) | | asfst - > packet_obj_size < 0 ) {
av_log ( s , AV_LOG_ERROR , " packet_obj_size %d invalid \n " , asfst - > packet_obj_size ) ;
asfst - > packet_obj_size = 0 ;
return AVERROR_INVALIDDATA ;
}
asf - > packet_frag_timestamp = avio_rl32 ( pb ) ; // timestamp
for ( i = 0 ; i < asfst - > payload_ext_ct ; i + + ) {
ASFPayload * p = & asfst - > payload [ i ] ;
int size = p - > size ;
int64_t payend ;
if ( size = = 0xFFFF )
size = avio_rl16 ( pb ) ;
payend = avio_tell ( pb ) + size ;
if ( payend > end ) {
av_log ( s , AV_LOG_ERROR , " too long payload \n " ) ;
break ;
}
switch ( p - > type ) {
case 0x50 :
// duration = avio_rl16(pb);
break ;
case 0x54 :
aspect . num = avio_r8 ( pb ) ;
aspect . den = avio_r8 ( pb ) ;
if ( aspect . num > 0 & & aspect . den > 0 & & asf - > stream_index > = 0 ) {
s - > streams [ asf - > stream_index ] - > sample_aspect_ratio = aspect ;
}
break ;
case 0x2A :
avio_skip ( pb , 8 ) ;
ts0 = avio_rl64 ( pb ) ;
ts1 = avio_rl64 ( pb ) ;
if ( ts0 ! = - 1 ) asf - > packet_frag_timestamp = ts0 / 10000 ;
else asf - > packet_frag_timestamp = AV_NOPTS_VALUE ;
asf - > ts_is_pts = 1 ;
break ;
case 0x5B :
case 0xB7 :
case 0xCC :
case 0xC0 :
case 0xA0 :
//unknown
break ;
}
avio_seek ( pb , payend , SEEK_SET ) ;
}
avio_seek ( pb , end , SEEK_SET ) ;
rsize + = asf - > packet_replic_size ; // FIXME - check validity
} else if ( asf - > packet_replic_size = = 1 ) {
// multipacket - frag_offset is beginning timestamp
asf - > packet_time_start = asf - > packet_frag_offset ;
asf - > packet_frag_offset = 0 ;
asf - > packet_frag_timestamp = asf - > packet_timestamp ;
asf - > packet_time_delta = avio_r8 ( pb ) ;
rsize + + ;
} else if ( asf - > packet_replic_size ! = 0 ) {
av_log ( s , AV_LOG_ERROR , " unexpected packet_replic_size of %d \n " ,
asf - > packet_replic_size ) ;
return AVERROR_INVALIDDATA ;
}
if ( asf - > packet_flags & 0x01 ) {
DO_2BITS ( asf - > packet_segsizetype > > 6 , asf - > packet_frag_size , 0 ) ; // 0 is illegal
if ( rsize > asf - > packet_size_left ) {
av_log ( s , AV_LOG_ERROR , " packet_replic_size is invalid \n " ) ;
return AVERROR_INVALIDDATA ;
} else if ( asf - > packet_frag_size > asf - > packet_size_left - rsize ) {
if ( asf - > packet_frag_size > asf - > packet_size_left - rsize + asf - > packet_padsize ) {
av_log ( s , AV_LOG_ERROR , " packet_frag_size is invalid (%d>%d-%d+%d) \n " ,
asf - > packet_frag_size , asf - > packet_size_left , rsize , asf - > packet_padsize ) ;
return AVERROR_INVALIDDATA ;
} else {
int diff = asf - > packet_frag_size - ( asf - > packet_size_left - rsize ) ;
asf - > packet_size_left + = diff ;
asf - > packet_padsize - = diff ;
}
}
} else {
asf - > packet_frag_size = asf - > packet_size_left - rsize ;
}
if ( asf - > packet_replic_size = = 1 ) {
asf - > packet_multi_size = asf - > packet_frag_size ;
if ( asf - > packet_multi_size > asf - > packet_size_left )
return AVERROR_INVALIDDATA ;
}
asf - > packet_size_left - = rsize ;
return 0 ;
}
/**
* Parse data from individual ASF packets ( which were previously loaded
* with asf_get_packet ( ) ) .
* @ param s demux context
* @ param pb context to read data from
* @ param pkt pointer to store packet data into
* @ return 0 if data was stored in pkt , < 0 on error or 1 if more ASF
* packets need to be loaded ( through asf_get_packet ( ) )
*/
static int asf_parse_packet ( AVFormatContext * s , AVIOContext * pb , AVPacket * pkt )
{
ASFContext * asf = s - > priv_data ;
ASFStream * asf_st = 0 ;
for ( ; ; ) {
int ret ;
if ( avio_feof ( pb ) )
return AVERROR_EOF ;
if ( asf - > packet_size_left < FRAME_HEADER_SIZE | |
asf - > packet_segments < 1 & & asf - > packet_time_start = = 0 ) {
int ret = asf - > packet_size_left + asf - > packet_padsize ;
if ( asf - > packet_size_left & & asf - > packet_size_left < FRAME_HEADER_SIZE )
av_log ( s , AV_LOG_WARNING , " Skip due to FRAME_HEADER_SIZE \n " ) ;
assert ( ret > = 0 ) ;
/* fail safe */
avio_skip ( pb , ret ) ;
asf - > packet_pos = avio_tell ( pb ) ;
if ( asf - > data_object_size ! = ( uint64_t ) - 1 & &
( asf - > packet_pos - asf - > data_object_offset > = asf - > data_object_size ) )
return AVERROR_EOF ; /* Do not exceed the size of the data object */
return 1 ;
}
if ( asf - > packet_time_start = = 0 ) {
if ( asf_read_frame_header ( s , pb ) < 0 ) {
asf - > packet_time_start = asf - > packet_segments = 0 ;
continue ;
}
if ( asf - > stream_index < 0 | |
s - > streams [ asf - > stream_index ] - > discard > = AVDISCARD_ALL | |
( ! asf - > packet_key_frame & &
( s - > streams [ asf - > stream_index ] - > discard > = AVDISCARD_NONKEY | | asf - > streams [ s - > streams [ asf - > stream_index ] - > id ] . skip_to_key ) ) ) {
asf - > packet_time_start = 0 ;
/* unhandled packet (should not happen) */
avio_skip ( pb , asf - > packet_frag_size ) ;
asf - > packet_size_left - = asf - > packet_frag_size ;
if ( asf - > stream_index < 0 )
av_log ( s , AV_LOG_ERROR , " ff asf skip %d (unknown stream) \n " ,
asf - > packet_frag_size ) ;
continue ;
}
asf - > asf_st = & asf - > streams [ s - > streams [ asf - > stream_index ] - > id ] ;
if ( ! asf - > packet_frag_offset )
asf - > asf_st - > skip_to_key = 0 ;
}
asf_st = asf - > asf_st ;
av_assert0 ( asf_st ) ;
if ( ! asf_st - > frag_offset & & asf - > packet_frag_offset ) {
av_log ( s , AV_LOG_TRACE , " skipping asf data pkt with fragment offset for "
" stream:%d, expected:%d but got %d from pkt) \n " ,
asf - > stream_index , asf_st - > frag_offset ,
asf - > packet_frag_offset ) ;
avio_skip ( pb , asf - > packet_frag_size ) ;
asf - > packet_size_left - = asf - > packet_frag_size ;
continue ;
}
if ( asf - > packet_replic_size = = 1 ) {
// frag_offset is here used as the beginning timestamp
asf - > packet_frag_timestamp = asf - > packet_time_start ;
asf - > packet_time_start + = asf - > packet_time_delta ;
asf_st - > packet_obj_size = asf - > packet_frag_size = avio_r8 ( pb ) ;
asf - > packet_size_left - - ;
asf - > packet_multi_size - - ;
if ( asf - > packet_multi_size < asf_st - > packet_obj_size ) {
asf - > packet_time_start = 0 ;
avio_skip ( pb , asf - > packet_multi_size ) ;
asf - > packet_size_left - = asf - > packet_multi_size ;
continue ;
}
asf - > packet_multi_size - = asf_st - > packet_obj_size ;
}
if ( asf_st - > pkt . size ! = asf_st - > packet_obj_size | |
// FIXME is this condition sufficient?
asf_st - > frag_offset + asf - > packet_frag_size > asf_st - > pkt . size ) {
int ret ;
if ( asf_st - > pkt . data ) {
av_log ( s , AV_LOG_INFO ,
" freeing incomplete packet size %d, new %d \n " ,
asf_st - > pkt . size , asf_st - > packet_obj_size ) ;
asf_st - > frag_offset = 0 ;
av_packet_unref ( & asf_st - > pkt ) ;
}
/* new packet */
if ( ( ret = av_new_packet ( & asf_st - > pkt , asf_st - > packet_obj_size ) ) < 0 )
return ret ;
asf_st - > seq = asf - > packet_seq ;
if ( asf - > packet_frag_timestamp ! = AV_NOPTS_VALUE ) {
if ( asf - > ts_is_pts ) {
asf_st - > pkt . pts = asf - > packet_frag_timestamp - asf - > hdr . preroll ;
} else
asf_st - > pkt . dts = asf - > packet_frag_timestamp - asf - > hdr . preroll ;
}
asf_st - > pkt . stream_index = asf - > stream_index ;
asf_st - > pkt . pos = asf_st - > packet_pos = asf - > packet_pos ;
asf_st - > pkt_clean = 0 ;
if ( asf_st - > pkt . data & & asf_st - > palette_changed ) {
uint8_t * pal ;
pal = av_packet_new_side_data ( & asf_st - > pkt , AV_PKT_DATA_PALETTE ,
AVPALETTE_SIZE ) ;
if ( ! pal ) {
av_log ( s , AV_LOG_ERROR , " Cannot append palette to packet \n " ) ;
} else {
memcpy ( pal , asf_st - > palette , AVPALETTE_SIZE ) ;
asf_st - > palette_changed = 0 ;
}
}
av_log ( asf , AV_LOG_TRACE , " new packet: stream:%d key:%d packet_key:%d audio:%d size:%d \n " ,
asf - > stream_index , asf - > packet_key_frame ,
asf_st - > pkt . flags & AV_PKT_FLAG_KEY ,
s - > streams [ asf - > stream_index ] - > codecpar - > codec_type = = AVMEDIA_TYPE_AUDIO ,
asf_st - > packet_obj_size ) ;
if ( s - > streams [ asf - > stream_index ] - > codecpar - > codec_type = = AVMEDIA_TYPE_AUDIO )
asf - > packet_key_frame = 1 ;
if ( asf - > packet_key_frame )
asf_st - > pkt . flags | = AV_PKT_FLAG_KEY ;
}
/* read data */
av_log ( asf , AV_LOG_TRACE , " READ PACKET s:%d os:%d o:%d,%d l:%d DATA:%p \n " ,
s - > packet_size , asf_st - > pkt . size , asf - > packet_frag_offset ,
asf_st - > frag_offset , asf - > packet_frag_size , asf_st - > pkt . data ) ;
asf - > packet_size_left - = asf - > packet_frag_size ;
if ( asf - > packet_size_left < 0 )
continue ;
if ( asf - > packet_frag_offset > = asf_st - > pkt . size | |
asf - > packet_frag_size > asf_st - > pkt . size - asf - > packet_frag_offset ) {
av_log ( s , AV_LOG_ERROR ,
" packet fragment position invalid %u,%u not in %u \n " ,
asf - > packet_frag_offset , asf - > packet_frag_size ,
asf_st - > pkt . size ) ;
continue ;
}
if ( asf - > packet_frag_offset ! = asf_st - > frag_offset & & ! asf_st - > pkt_clean ) {
memset ( asf_st - > pkt . data + asf_st - > frag_offset , 0 , asf_st - > pkt . size - asf_st - > frag_offset ) ;
asf_st - > pkt_clean = 1 ;
}
ret = avio_read ( pb , asf_st - > pkt . data + asf - > packet_frag_offset ,
asf - > packet_frag_size ) ;
if ( ret ! = asf - > packet_frag_size ) {
if ( ret < 0 | | asf - > packet_frag_offset + ret = = 0 )
return ret < 0 ? ret : AVERROR_EOF ;
if ( asf_st - > ds_span > 1 ) {
// scrambling, we can either drop it completely or fill the remainder
// TODO: should we fill the whole packet instead of just the current
// fragment?
memset ( asf_st - > pkt . data + asf - > packet_frag_offset + ret , 0 ,
asf - > packet_frag_size - ret ) ;
ret = asf - > packet_frag_size ;
} else {
// no scrambling, so we can return partial packets
av_shrink_packet ( & asf_st - > pkt , asf - > packet_frag_offset + ret ) ;
}
}
if ( s - > key & & s - > keylen = = 20 )
ff_asfcrypt_dec ( s - > key , asf_st - > pkt . data + asf - > packet_frag_offset ,
ret ) ;
asf_st - > frag_offset + = ret ;
/* test if whole packet is read */
if ( asf_st - > frag_offset = = asf_st - > pkt . size ) {
// workaround for macroshit radio DVR-MS files
if ( s - > streams [ asf - > stream_index ] - > codecpar - > codec_id = = AV_CODEC_ID_MPEG2VIDEO & &
asf_st - > pkt . size > 100 ) {
int i ;
for ( i = 0 ; i < asf_st - > pkt . size & & ! asf_st - > pkt . data [ i ] ; i + + )
;
if ( i = = asf_st - > pkt . size ) {
av_log ( s , AV_LOG_DEBUG , " discarding ms fart \n " ) ;
asf_st - > frag_offset = 0 ;
av_packet_unref ( & asf_st - > pkt ) ;
continue ;
}
}
/* return packet */
if ( asf_st - > ds_span > 1 ) {
if ( asf_st - > pkt . size ! = asf_st - > ds_packet_size * asf_st - > ds_span ) {
av_log ( s , AV_LOG_ERROR ,
" pkt.size != ds_packet_size * ds_span (%d %d %d) \n " ,
asf_st - > pkt . size , asf_st - > ds_packet_size ,
asf_st - > ds_span ) ;
} else {
/* packet descrambling */
AVBufferRef * buf = av_buffer_alloc ( asf_st - > pkt . size +
AV_INPUT_BUFFER_PADDING_SIZE ) ;
if ( buf ) {
uint8_t * newdata = buf - > data ;
int offset = 0 ;
memset ( newdata + asf_st - > pkt . size , 0 ,
AV_INPUT_BUFFER_PADDING_SIZE ) ;
while ( offset < asf_st - > pkt . size ) {
int off = offset / asf_st - > ds_chunk_size ;
int row = off / asf_st - > ds_span ;
int col = off % asf_st - > ds_span ;
int idx = row + col * asf_st - > ds_packet_size / asf_st - > ds_chunk_size ;
assert ( offset + asf_st - > ds_chunk_size < = asf_st - > pkt . size ) ;
assert ( idx + 1 < = asf_st - > pkt . size / asf_st - > ds_chunk_size ) ;
memcpy ( newdata + offset ,
asf_st - > pkt . data + idx * asf_st - > ds_chunk_size ,
asf_st - > ds_chunk_size ) ;
offset + = asf_st - > ds_chunk_size ;
}
av_buffer_unref ( & asf_st - > pkt . buf ) ;
asf_st - > pkt . buf = buf ;
asf_st - > pkt . data = buf - > data ;
}
}
}
asf_st - > frag_offset = 0 ;
* pkt = asf_st - > pkt ;
asf_st - > pkt . buf = 0 ;
asf_st - > pkt . size = 0 ;
asf_st - > pkt . data = 0 ;
asf_st - > pkt . side_data_elems = 0 ;
asf_st - > pkt . side_data = NULL ;
break ; // packet completed
}
}
return 0 ;
}
static int asf_read_packet ( AVFormatContext * s , AVPacket * pkt )
{
ASFContext * asf = s - > priv_data ;
for ( ; ; ) {
int ret ;
/* parse cached packets, if any */
if ( ( ret = asf_parse_packet ( s , s - > pb , pkt ) ) < = 0 )
return ret ;
if ( ( ret = asf_get_packet ( s , s - > pb ) ) < 0 )
assert ( asf - > packet_size_left < FRAME_HEADER_SIZE | |
asf - > packet_segments < 1 ) ;
asf - > packet_time_start = 0 ;
}
}
// Added to support seeking after packets have been read
// If information is not reset, read_packet fails due to
// leftover information from previous reads
static void asf_reset_header ( AVFormatContext * s )
{
ASFContext * asf = s - > priv_data ;
ASFStream * asf_st ;
int i ;
asf - > packet_size_left = 0 ;
asf - > packet_flags = 0 ;
asf - > packet_property = 0 ;
asf - > packet_timestamp = 0 ;
asf - > packet_segsizetype = 0 ;
asf - > packet_segments = 0 ;
asf - > packet_seq = 0 ;
asf - > packet_replic_size = 0 ;
asf - > packet_key_frame = 0 ;
asf - > packet_padsize = 0 ;
asf - > packet_frag_offset = 0 ;
asf - > packet_frag_size = 0 ;
asf - > packet_frag_timestamp = 0 ;
asf - > packet_multi_size = 0 ;
asf - > packet_time_delta = 0 ;
asf - > packet_time_start = 0 ;
for ( i = 0 ; i < 128 ; i + + ) {
asf_st = & asf - > streams [ i ] ;
av_packet_unref ( & asf_st - > pkt ) ;
asf_st - > packet_obj_size = 0 ;
asf_st - > frag_offset = 0 ;
asf_st - > seq = 0 ;
}
asf - > asf_st = NULL ;
}
static void skip_to_key ( AVFormatContext * s )
{
ASFContext * asf = s - > priv_data ;
int i ;
for ( i = 0 ; i < 128 ; i + + ) {
int j = asf - > asfid2avid [ i ] ;
ASFStream * asf_st = & asf - > streams [ i ] ;
if ( j < 0 | | s - > streams [ j ] - > codecpar - > codec_type ! = AVMEDIA_TYPE_VIDEO )
continue ;
asf_st - > skip_to_key = 1 ;
}
}
static int asf_read_close ( AVFormatContext * s )
{
asf_reset_header ( s ) ;
return 0 ;
}
static int64_t asf_read_pts ( AVFormatContext * s , int stream_index ,
int64_t * ppos , int64_t pos_limit )
{
FFFormatContext * const si = ffformatcontext ( s ) ;
ASFContext * asf = s - > priv_data ;
AVPacket pkt1 , * pkt = & pkt1 ;
ASFStream * asf_st ;
int64_t pts ;
int64_t pos = * ppos ;
int i ;
int64_t start_pos [ ASF_MAX_STREAMS ] ;
for ( i = 0 ; i < s - > nb_streams ; i + + )
start_pos [ i ] = pos ;
if ( s - > packet_size > 0 )
pos = ( pos + s - > packet_size - 1 - si - > data_offset ) /
s - > packet_size * s - > packet_size +
si - > data_offset ;
* ppos = pos ;
if ( avio_seek ( s - > pb , pos , SEEK_SET ) < 0 )
return AV_NOPTS_VALUE ;
ff_read_frame_flush ( s ) ;
asf_reset_header ( s ) ;
for ( ; ; ) {
if ( av_read_frame ( s , pkt ) < 0 ) {
av_log ( s , AV_LOG_INFO , " asf_read_pts failed \n " ) ;
return AV_NOPTS_VALUE ;
}
pts = pkt - > dts ;
if ( pkt - > flags & AV_PKT_FLAG_KEY ) {
i = pkt - > stream_index ;
asf_st = & asf - > streams [ s - > streams [ i ] - > id ] ;
// assert((asf_st->packet_pos - s->data_offset) % s->packet_size == 0);
pos = asf_st - > packet_pos ;
av_assert1 ( pkt - > pos = = asf_st - > packet_pos ) ;
av_add_index_entry ( s - > streams [ i ] , pos , pts , pkt - > size ,
pos - start_pos [ i ] + 1 , AVINDEX_KEYFRAME ) ;
start_pos [ i ] = asf_st - > packet_pos + 1 ;
if ( pkt - > stream_index = = stream_index ) {
av_packet_unref ( pkt ) ;
break ;
}
}
av_packet_unref ( pkt ) ;
}
* ppos = pos ;
return pts ;
}
static int asf_build_simple_index ( AVFormatContext * s , int stream_index )
{
ff_asf_guid g ;
ASFContext * asf = s - > priv_data ;
int64_t current_pos = avio_tell ( s - > pb ) ;
int64_t ret ;
if ( ( ret = avio_seek ( s - > pb , asf - > data_object_offset + asf - > data_object_size , SEEK_SET ) ) < 0 ) {
return ret ;
}
if ( ( ret = ff_get_guid ( s - > pb , & g ) ) < 0 )
goto end ;
/* the data object can be followed by other top-level objects,
* skip them until the simple index object is reached */
while ( ff_guidcmp ( & g , & ff_asf_simple_index_header ) ) {
int64_t gsize = avio_rl64 ( s - > pb ) ;
if ( gsize < 24 | | avio_feof ( s - > pb ) ) {
goto end ;
}
avio_skip ( s - > pb , gsize - 24 ) ;
if ( ( ret = ff_get_guid ( s - > pb , & g ) ) < 0 )
goto end ;
}
{
int64_t itime , last_pos = - 1 ;
int pct , ict ;
int i ;
int64_t av_unused gsize = avio_rl64 ( s - > pb ) ;
if ( ( ret = ff_get_guid ( s - > pb , & g ) ) < 0 )
goto end ;
itime = avio_rl64 ( s - > pb ) ;
pct = avio_rl32 ( s - > pb ) ;
ict = avio_rl32 ( s - > pb ) ;
av_log ( s , AV_LOG_DEBUG ,
" itime:0x% " PRIx64 " , pct:%d, ict:%d \n " , itime , pct , ict ) ;
for ( i = 0 ; i < ict ; i + + ) {
int pktnum = avio_rl32 ( s - > pb ) ;
int pktct = avio_rl16 ( s - > pb ) ;
int64_t pos = ffformatcontext ( s ) - > data_offset + s - > packet_size * ( int64_t ) pktnum ;
int64_t index_pts = FFMAX ( av_rescale ( itime , i , 10000 ) - asf - > hdr . preroll , 0 ) ;
if ( avio_feof ( s - > pb ) ) {
ret = AVERROR_INVALIDDATA ;
goto end ;
}
if ( pos ! = last_pos ) {
av_log ( s , AV_LOG_DEBUG , " pktnum:%d, pktct:%d pts: % " PRId64 " \n " ,
pktnum , pktct , index_pts ) ;
av_add_index_entry ( s - > streams [ stream_index ] , pos , index_pts ,
s - > packet_size , 0 , AVINDEX_KEYFRAME ) ;
last_pos = pos ;
}
}
asf - > index_read = ict > 1 ;
}
end :
// if (avio_feof(s->pb)) {
// ret = 0;
// }
avio_seek ( s - > pb , current_pos , SEEK_SET ) ;
return ret ;
}
static int asf_read_seek ( AVFormatContext * s , int stream_index ,
int64_t pts , int flags )
{
ASFContext * asf = s - > priv_data ;
AVStream * st = s - > streams [ stream_index ] ;
FFStream * const sti = ffstream ( st ) ;
int ret = 0 ;
if ( s - > packet_size < = 0 )
return - 1 ;
/* Try using the protocol's read_seek if available */
if ( s - > pb ) {
int64_t ret = avio_seek_time ( s - > pb , stream_index , pts , flags ) ;
if ( ret > = 0 )
asf_reset_header ( s ) ;
if ( ret ! = AVERROR ( ENOSYS ) )
return ret ;
}
/* explicitly handle the case of seeking to 0 */
if ( ! pts ) {
asf_reset_header ( s ) ;
avio_seek ( s - > pb , ffformatcontext ( s ) - > data_offset , SEEK_SET ) ;
return 0 ;
}
if ( ! asf - > index_read ) {
ret = asf_build_simple_index ( s , stream_index ) ;
if ( ret < 0 )
asf - > index_read = - 1 ;
}
if ( asf - > index_read > 0 & & sti - > index_entries ) {
int index = av_index_search_timestamp ( st , pts , flags ) ;
if ( index > = 0 ) {
/* find the position */
uint64_t pos = sti - > index_entries [ index ] . pos ;
/* do the seek */
av_log ( s , AV_LOG_DEBUG , " SEEKTO: % " PRId64 " \n " , pos ) ;
if ( avio_seek ( s - > pb , pos , SEEK_SET ) < 0 )
return - 1 ;
asf_reset_header ( s ) ;
skip_to_key ( s ) ;
return 0 ;
}
}
/* no index or seeking by index failed */
if ( ff_seek_frame_binary ( s , stream_index , pts , flags ) < 0 )
return - 1 ;
asf_reset_header ( s ) ;
skip_to_key ( s ) ;
return 0 ;
}
const AVInputFormat ff_asf_demuxer = {
. name = " asf " ,
. long_name = NULL_IF_CONFIG_SMALL ( " ASF (Advanced / Active Streaming Format) " ) ,
. priv_data_size = sizeof ( ASFContext ) ,
. read_probe = asf_probe ,
. read_header = asf_read_header ,
. read_packet = asf_read_packet ,
. read_close = asf_read_close ,
. read_seek = asf_read_seek ,
. read_timestamp = asf_read_pts ,
. flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH ,
. priv_class = & asf_class ,
} ;