X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fasf%2Fasf.c;h=18c293d3cae52e0d931594e2d7abfce877bb809e;hb=54bbfac289eeeed9ba044bf9f9e1ee43e5642c78;hp=228336468a3434e21a822e04f27d7736ca114285;hpb=b38ec88240b239452c5cbd272c140e2695962eba;p=vlc diff --git a/modules/demux/asf/asf.c b/modules/demux/asf/asf.c index 228336468a..0622ff81a3 100644 --- a/modules/demux/asf/asf.c +++ b/modules/demux/asf/asf.c @@ -1,36 +1,54 @@ /***************************************************************************** - * asf.c : ASFv01 file input module for vlc + * asf.c : ASF demux module ***************************************************************************** - * Copyright (C) 2002-2003 VideoLAN - * $Id: asf.c,v 1.43 2003/11/20 22:10:56 fenrir Exp $ + * Copyright © 2002-2004, 2006-2008, 2010 VLC authors and VideoLAN * * Authors: Laurent Aimar * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program 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. * * This program 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include /* vlc_meta_Set*, vlc_meta_New */ +#include /* GET_PRIVATE_ID_STATE */ +#include /* VLC_BITMAPINFOHEADER, WAVEFORMATEX */ +#include +#include + +#include -#include "codecs.h" /* BITMAPINFOHEADER, WAVEFORMATEX */ +#include "asfpacket.h" #include "libasf.h" +#include "assert.h" + +/* TODO + * - add support for the newly added object: language, bitrate, + * extended stream properties. + */ /***************************************************************************** * Module descriptor @@ -38,765 +56,1232 @@ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); -vlc_module_begin(); - set_description( _("ASF v1.0 demuxer") ); - set_capability( "demux", 200 ); - set_callbacks( Open, Close ); - add_shortcut( "asf" ); -vlc_module_end(); +vlc_module_begin () + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_DEMUX ) + set_description( N_("ASF/WMV demuxer") ) + set_capability( "demux", 200 ) + set_callbacks( Open, Close ) + add_shortcut( "asf", "wmv" ) +vlc_module_end () /***************************************************************************** * Local prototypes *****************************************************************************/ -static int Demux ( input_thread_t * ); - -typedef struct asf_stream_s +static int Demux ( demux_t * ); +static int Control( demux_t *, int i_query, va_list args ); +static void FlushRemainingPackets( demux_t *p_demux ); + +#define MAX_ASF_TRACKS (ASF_MAX_STREAMNUMBER + 1) +#define ASF_PREROLL_FROM_CURRENT -1 + +/* callbacks for packet parser */ +static void Packet_UpdateTime( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, + mtime_t i_time ); +static asf_track_info_t * Packet_GetTrackInfo( asf_packet_sys_t *p_packetsys, + uint8_t i_stream_number ); +static bool Packet_DoSkip( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, bool b_packet_keyframe ); +static void Packet_Send(asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, block_t **pp_frame); +static void Packet_SetAR( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, + uint8_t i_ratio_x, uint8_t i_ratio_y ); + +typedef struct { int i_cat; es_out_id_t *p_es; + es_format_t *p_fmt; /* format backup for video changes */ + bool b_selected; - asf_object_stream_properties_t *p_sp; + mtime_t i_time; /* track time*/ - mtime_t i_time; + asf_track_info_t info; - pes_packet_t *p_pes; /* used to keep uncomplete frames */ - -} asf_stream_t; +} asf_track_t; struct demux_sys_t { - mtime_t i_time; /* µs */ + mtime_t i_time; /* s */ mtime_t i_length; /* length of file file */ + uint64_t i_bitrate; /* global file bitrate */ asf_object_root_t *p_root; asf_object_file_properties_t *p_fp; - unsigned int i_streams; - asf_stream_t *stream[128]; + unsigned int i_track; + asf_track_t *track[MAX_ASF_TRACKS]; /* track number is stored on 7 bits */ + + uint64_t i_data_begin; + uint64_t i_data_end; - int64_t i_data_begin; - int64_t i_data_end; + bool b_index; + bool b_canfastseek; + uint8_t i_seek_track; + uint8_t i_access_selected_track[ES_CATEGORY_COUNT]; /* mms, depends on access algorithm */ + unsigned int i_wait_keyframe; + + mtime_t i_preroll_start; + + asf_packet_sys_t packet_sys; + + vlc_meta_t *meta; }; static mtime_t GetMoviePTS( demux_sys_t * ); -static int DemuxPacket( input_thread_t *, vlc_bool_t b_play_audio ); +static int DemuxInit( demux_t * ); +static void DemuxEnd( demux_t * ); /***************************************************************************** * Open: check file and initializes ASF structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { - input_thread_t *p_input = (input_thread_t *)p_this; - uint8_t *p_peek; - - guid_t guid; - - demux_sys_t *p_sys; - unsigned int i_stream, i; - asf_object_content_description_t *p_cd; - - vlc_bool_t b_seekable; + demux_t *p_demux = (demux_t *)p_this; + demux_sys_t *p_sys; + guid_t guid; + const uint8_t *p_peek; - input_info_category_t *p_cat; + /* A little test to see if it could be a asf stream */ + if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 ) return VLC_EGENERIC; - /* a little test to see if it could be a asf stream */ - if( input_Peek( p_input, &p_peek, 16 ) < 16 ) - { - msg_Warn( p_input, "ASF plugin discarded (cannot peek)" ); - return VLC_EGENERIC; - } ASF_GetGUID( &guid, p_peek ); - if( !ASF_CmpGUID( &guid, &asf_object_header_guid ) ) - { - msg_Warn( p_input, "ASF plugin discarded (not a valid file)" ); - return VLC_EGENERIC; - } + if( !guidcmp( &guid, &asf_object_header_guid ) ) return VLC_EGENERIC; - /* Set p_input field */ - p_input->pf_demux = Demux; - p_input->pf_demux_control = demux_vaControlDefault; - p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) ); - memset( p_sys, 0, sizeof( demux_sys_t ) ); - p_sys->i_time = -1; - p_sys->i_length = 0; + /* Set p_demux fields */ + p_demux->pf_demux = Demux; + p_demux->pf_control = Control; + p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) ); - /* Now load all object ( except raw data ) */ - stream_Control( p_input->s, STREAM_CAN_FASTSEEK, &b_seekable ); - if( (p_sys->p_root = ASF_ReadObjectRoot( p_input->s, b_seekable )) == NULL ) + /* Load the headers */ + if( DemuxInit( p_demux ) ) { - msg_Warn( p_input, "ASF plugin discarded (not a valid file)" ); free( p_sys ); return VLC_EGENERIC; } - p_sys->p_fp = p_sys->p_root->p_fp; - if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) - { - msg_Warn( p_input, - "ASF plugin discarded (invalid file_properties object)" ); - goto error; - } + p_sys->packet_sys.p_demux = p_demux; + p_sys->packet_sys.pf_doskip = Packet_DoSkip; + p_sys->packet_sys.pf_send = Packet_Send; + p_sys->packet_sys.pf_gettrackinfo = Packet_GetTrackInfo; + p_sys->packet_sys.pf_updatetime = Packet_UpdateTime; + p_sys->packet_sys.pf_setaspectratio = Packet_SetAR; - p_sys->i_streams = ASF_CountObject( p_sys->p_root->p_hdr, - &asf_object_stream_properties_guid ); - if( !p_sys->i_streams ) - { - msg_Warn( p_input, "ASF plugin discarded (cannot find any stream!)" ); - goto error; - } + return VLC_SUCCESS; +} - msg_Dbg( p_input, "found %d streams", p_sys->i_streams ); +/***************************************************************************** + * Demux: read packet and send them to decoders + *****************************************************************************/ +static int Demux( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( input_InitStream( p_input, 0 ) == -1) + for( int i=0; istream.stream_lock ); - msg_Err( p_input, "cannot init stream" ); - goto error; + if ( p_sys->i_access_selected_track[i] > 0 ) + { + es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, + p_sys->track[p_sys->i_access_selected_track[i]]->p_es, true ); + p_sys->i_access_selected_track[i] = 0; + } } - p_input->stream.i_mux_rate = 0 ; /* updated later */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); - for( i_stream = 0; i_stream < p_sys->i_streams; i_stream ++ ) + /* Get selected tracks, especially for computing PCR */ + for( int i=0; ip_root->p_hdr, - &asf_object_stream_properties_guid, - i_stream ); + asf_track_t *tk = p_sys->track[i]; + if ( !tk ) continue; + if ( tk->p_es ) + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, & tk->b_selected ); + else + tk->b_selected = false; + } - p_stream = - p_sys->stream[p_sp->i_stream_number] = - malloc( sizeof( asf_stream_t ) ); - memset( p_stream, 0, sizeof( asf_stream_t ) ); + for( ;; ) + { + const uint8_t *p_peek; + mtime_t i_length; + mtime_t i_time_begin = GetMoviePTS( p_sys ); + int i_result; - p_stream->i_time = -1; - p_stream->p_sp = p_sp; - p_stream->p_es = NULL; + if( !vlc_object_alive (p_demux) ) + break; +#if 0 + /* FIXME: returns EOF too early for some mms streams */ + if( p_sys->i_data_end >= 0 && + stream_Tell( p_demux->s ) >= p_sys->i_data_end ) + return 0; /* EOF */ +#endif - if( ASF_CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_audio ) && - p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 ) + /* Check if we have concatenated files */ + if( stream_Peek( p_demux->s, &p_peek, 16 ) == 16 ) { - es_format_t fmt; - uint8_t *p_data = p_sp->p_type_specific_data; - - es_format_Init( &fmt, AUDIO_ES, 0 ); - wf_tag_to_fourcc( GetWLE( &p_data[0] ), &fmt.i_codec, NULL ); - fmt.audio.i_channels = GetWLE( &p_data[2] ); - fmt.audio.i_rate = GetDWLE( &p_data[4] ); - fmt.i_bitrate = GetDWLE( &p_data[8] ) * 8; - fmt.audio.i_blockalign = GetWLE( &p_data[12] ); - fmt.audio.i_bitspersample = GetWLE( &p_data[14] ); + guid_t guid; - if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) ) + ASF_GetGUID( &guid, p_peek ); + if( guidcmp( &guid, &asf_object_header_guid ) ) { - fmt.i_extra = __MIN( GetWLE( &p_data[16] ), - p_sp->i_type_specific_data_length - sizeof( WAVEFORMATEX ) ); - fmt.p_extra = malloc( fmt.i_extra ); - memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )], fmt.i_extra ); - } + msg_Warn( p_demux, "found a new ASF header" ); + /* We end this stream */ + DemuxEnd( p_demux ); - p_stream->i_cat = AUDIO_ES; - p_stream->p_es = es_out_Add( p_input->p_es_out, &fmt ); + /* And we prepare to read the next one */ + if( DemuxInit( p_demux ) ) + { + msg_Err( p_demux, "failed to load the new header" ); + dialog_Fatal( p_demux, _("Could not demux ASF stream"), "%s", + _("VLC failed to load the ASF header.") ); + return 0; + } + es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); + continue; + } + } - msg_Dbg( p_input, - "added new audio stream(codec:0x%x,ID:%d)", - GetWLE( p_data ), p_sp->i_stream_number ); + /* Read and demux a packet */ + if( ( i_result = DemuxASFPacket( &p_sys->packet_sys, + p_sys->p_fp->i_min_data_packet_size, + p_sys->p_fp->i_max_data_packet_size ) ) <= 0 ) + { + FlushRemainingPackets( p_demux ); + return i_result; } - else if( ASF_CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_video ) && - p_sp->i_type_specific_data_length >= 11 + sizeof( BITMAPINFOHEADER ) ) + if( i_time_begin == -1 ) { - es_format_t fmt; - uint8_t *p_data = &p_sp->p_type_specific_data[11]; + i_time_begin = GetMoviePTS( p_sys ); + } + else + { + i_length = GetMoviePTS( p_sys ) - i_time_begin; + if( i_length < 0 || i_length >= 40 * 1000 ) break; + } + } - es_format_Init( &fmt, VIDEO_ES, - VLC_FOURCC( p_data[16], p_data[17], p_data[18], p_data[19] ) ); - fmt.video.i_width = GetDWLE( p_data + 4 ); - fmt.video.i_height= GetDWLE( p_data + 8 ); + /* Set the PCR */ + /* WARN: Don't move it before the end of the whole chunk */ + p_sys->i_time = GetMoviePTS( p_sys ); + if( p_sys->i_time >= 0 ) + { +#ifdef ASF_DEBUG + msg_Dbg( p_demux, "Demux Loop Setting PCR to %"PRId64, VLC_TS_0 + p_sys->i_time ); +#endif + es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_time ); + } + + return 1; +} + +/***************************************************************************** + * Close: frees unused data + *****************************************************************************/ +static void Close( vlc_object_t * p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + + DemuxEnd( p_demux ); + + free( p_demux->p_sys ); +} - if( p_sp->i_type_specific_data_length > 11 + sizeof( BITMAPINFOHEADER ) ) +/***************************************************************************** + * WaitKeyframe: computes the number of frames to wait for a keyframe + *****************************************************************************/ +static void WaitKeyframe( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + if ( ! p_sys->i_seek_track ) + { + for ( int i=0; itrack[i]; + if ( tk && tk->info.p_sp && tk->i_cat == VIDEO_ES && tk->b_selected ) { - fmt.i_extra = __MIN( GetDWLE( p_data ), - p_sp->i_type_specific_data_length - 11 - sizeof( BITMAPINFOHEADER ) ); - fmt.p_extra = malloc( fmt.i_extra ); - memcpy( fmt.p_extra, &p_data[sizeof( BITMAPINFOHEADER )], fmt.i_extra ); + p_sys->i_seek_track = tk->info.p_sp->i_stream_number; + break; } + } + } - p_stream->i_cat = VIDEO_ES; - p_stream->p_es = es_out_Add( p_input->p_es_out, &fmt ); - - msg_Dbg( p_input, "added new video stream(ID:%d)", - p_sp->i_stream_number ); + if ( p_sys->i_seek_track ) + { + /* Skip forward at least 1 min */ + asf_track_t *tk = p_sys->track[p_sys->i_seek_track]; + if ( tk->info.p_esp && tk->info.p_esp->i_average_time_per_frame ) + { + /* 1 min if fastseek, otherwise 5 sec */ + /* That's a guess for bandwidth */ + uint64_t i_maxwaittime = ( p_sys->b_canfastseek ) ? 600000000 : 50000000; + i_maxwaittime /= tk->info.p_esp->i_average_time_per_frame; + p_sys->i_wait_keyframe = __MIN( i_maxwaittime, UINT_MAX ); } else { - p_stream->i_cat = UNKNOWN_ES; - msg_Dbg( p_input, "ignoring unknown stream(ID:%d)", - p_sp->i_stream_number ); + p_sys->i_wait_keyframe = ( p_sys->b_canfastseek ) ? 25 * 30 : 25 * 5; } } - - p_sys->i_data_begin = p_sys->p_root->p_data->i_object_pos + 50; - if( p_sys->p_root->p_data->i_object_size != 0 ) - { /* local file */ - p_sys->i_data_end = p_sys->p_root->p_data->i_object_pos + - p_sys->p_root->p_data->i_object_size; - } else - { /* live/broacast */ - p_sys->i_data_end = -1; + { + p_sys->i_wait_keyframe = 0; } +} - /* go to first packet */ - stream_Seek( p_input->s, p_sys->i_data_begin ); +/***************************************************************************** + * SeekIndex: goto to i_date or i_percent + *****************************************************************************/ +static int SeekPercent( demux_t *p_demux, int i_query, va_list args ) +{ + demux_sys_t *p_sys = p_demux->p_sys; - /* try to calculate movie time */ - if( p_sys->p_fp->i_data_packets_count > 0 ) - { - int64_t i_count; - int64_t i_size = stream_Size( p_input->s ); + WaitKeyframe( p_demux ); - if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end ) - { - i_size = p_sys->i_data_end; - } + msg_Dbg( p_demux, "seek with percent: waiting %i frames", p_sys->i_wait_keyframe ); + return demux_vaControlHelper( p_demux->s, __MIN( INT64_MAX, p_sys->i_data_begin ), + __MIN( INT64_MAX, p_sys->i_data_end ), + __MIN( INT64_MAX, p_sys->i_bitrate ), + __MIN( INT16_MAX, p_sys->p_fp->i_min_data_packet_size ), + i_query, args ); +} - /* real number of packets */ - i_count = ( i_size - p_sys->i_data_begin ) / - p_sys->p_fp->i_min_data_packet_size; +static int SeekIndex( demux_t *p_demux, mtime_t i_date, float f_pos ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + asf_object_index_t *p_index; - /* calculate the time duration in micro-s */ - p_sys->i_length = (mtime_t)p_sys->p_fp->i_play_duration / 10 * - (mtime_t)i_count / - (mtime_t)p_sys->p_fp->i_data_packets_count; + msg_Dbg( p_demux, "seek with index: %i seconds, position %f", + i_date >= 0 ? (int)(i_date/1000000) : -1, f_pos ); - if( p_sys->i_length > 0 ) - { - p_input->stream.i_mux_rate = - i_size / 50 * (int64_t)1000000 / p_sys->i_length; - } - } + if( i_date < 0 ) + i_date = p_sys->i_length * f_pos; - /* We add all info about this stream */ - p_cat = input_InfoCategory( p_input, "Asf" ); - if( p_sys->i_length > 0 ) - { - int64_t i_second = p_sys->i_length / (int64_t)1000000; + p_sys->i_preroll_start = i_date - (int64_t) p_sys->p_fp->i_preroll; + if ( p_sys->i_preroll_start < 0 ) p_sys->i_preroll_start = 0; - input_AddInfo( p_cat, _("Length"), "%d:%d:%d", - (int)(i_second / 36000), - (int)(( i_second / 60 ) % 60), - (int)(i_second % 60) ); - } - input_AddInfo( p_cat, _("Number of streams"), "%d" , p_sys->i_streams ); + p_index = ASF_FindObject( p_sys->p_root, &asf_object_simple_index_guid, 0 ); - if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr, - &asf_object_content_description_guid, 0 ) ) ) + uint64_t i_entry = p_sys->i_preroll_start * 10 / p_index->i_index_entry_time_interval; + if( i_entry >= p_index->i_index_entry_count ) { - if( *p_cd->psz_title ) - input_AddInfo( p_cat, _("Title"), p_cd->psz_title ); - if( p_cd->psz_author ) - input_AddInfo( p_cat, _("Author"), p_cd->psz_author ); - if( p_cd->psz_copyright ) - input_AddInfo( p_cat, _("Copyright"), p_cd->psz_copyright ); - if( *p_cd->psz_description ) - input_AddInfo( p_cat, _("Description"), p_cd->psz_description ); - if( *p_cd->psz_rating ) - input_AddInfo( p_cat, _("Rating"), p_cd->psz_rating ); + msg_Warn( p_demux, "Incomplete index" ); + return VLC_EGENERIC; } - /* FIXME to port to new way */ - for( i_stream = 0, i = 0; i < 128; i++ ) - { - asf_object_codec_list_t *p_cl = - ASF_FindObject( p_sys->p_root->p_hdr, - &asf_object_codec_list_guid, 0 ); + WaitKeyframe( p_demux ); - if( p_sys->stream[i] ) - { - char psz_cat[sizeof(_("Stream "))+10]; - sprintf( psz_cat, _("Stream %d"), i_stream ); - p_cat = input_InfoCategory( p_input, psz_cat); + uint64_t i_offset = (uint64_t)p_index->index_entry[i_entry].i_packet_number * + p_sys->p_fp->i_min_data_packet_size; - if( p_cl && i_stream < p_cl->i_codec_entries_count ) - { - input_AddInfo( p_cat, _("Codec name"), - p_cl->codec[i_stream].psz_name ); - input_AddInfo( p_cat, _("Codec description"), - p_cl->codec[i_stream].psz_description ); - } - i_stream++; - } + if ( stream_Seek( p_demux->s, i_offset + p_sys->i_data_begin ) == VLC_SUCCESS ) + { + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, VLC_TS_0 + i_date ); + return VLC_SUCCESS; } + else return VLC_EGENERIC; +} - return VLC_SUCCESS; +static void SeekPrepare( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; -error: - ASF_FreeObjectRoot( p_input->s, p_sys->p_root ); - free( p_sys ); - return VLC_EGENERIC; -} + p_sys->i_time = VLC_TS_INVALID; + p_sys->i_preroll_start = ASFPACKET_PREROLL_FROM_CURRENT; + for( int i = 0; i < MAX_ASF_TRACKS ; i++ ) + { + asf_track_t *tk = p_sys->track[i]; + if( !tk ) + continue; + tk->i_time = -1; + if( tk->info.p_frame ) + block_ChainRelease( tk->info.p_frame ); + tk->info.p_frame = NULL; + } + + es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); +} /***************************************************************************** - * Demux: read packet and send them to decoders + * Control: *****************************************************************************/ -static int Demux( input_thread_t *p_input ) +static int Control( demux_t *p_demux, int i_query, va_list args ) { - demux_sys_t *p_sys = p_input->p_demux_data; - vlc_bool_t b_play_audio; - int i; + demux_sys_t *p_sys = p_demux->p_sys; + vlc_meta_t *p_meta; + int64_t i64, *pi64; + int i; + double f, *pf; - /* catch seek from user */ - if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT ) + switch( i_query ) { - int64_t i_offset; + case DEMUX_GET_LENGTH: + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_length; + return VLC_SUCCESS; - msleep( p_input->i_pts_delay ); + case DEMUX_GET_TIME: + pi64 = (int64_t*)va_arg( args, int64_t * ); + if( p_sys->i_time < 0 ) return VLC_EGENERIC; + *pi64 = p_sys->i_time; + return VLC_SUCCESS; - i_offset = stream_Tell( p_input->s ) - p_sys->i_data_begin; - if( i_offset < 0 ) + case DEMUX_SET_TIME: + if ( p_sys->p_fp && + ! ( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) ) + return VLC_EGENERIC; + + SeekPrepare( p_demux ); + + if( p_sys->b_index && p_sys->i_length > 0 ) { - i_offset = 0; + va_list acpy; + va_copy( acpy, args ); + i64 = (int64_t)va_arg( acpy, int64_t ); + va_end( acpy ); + + if( !SeekIndex( p_demux, i64, -1 ) ) + return VLC_SUCCESS; } - if( i_offset % p_sys->p_fp->i_min_data_packet_size > 0 ) + return SeekPercent( p_demux, i_query, args ); + + case DEMUX_SET_ES: + { + i = (int)va_arg( args, int ); + int i_ret; + if ( i >= 0 ) { - i_offset -= i_offset % p_sys->p_fp->i_min_data_packet_size; + i++; /* video/audio-es variable starts 0 */ + msg_Dbg( p_demux, "Requesting access to enable stream %d", i ); + i_ret = stream_Control( p_demux->s, STREAM_SET_PRIVATE_ID_STATE, i, true ); + } + else + { /* i contains -1 * es_category */ + msg_Dbg( p_demux, "Requesting access to disable stream %d", i ); + i_ret = stream_Control( p_demux->s, STREAM_SET_PRIVATE_ID_STATE, i, false ); } - if( stream_Seek( p_input->s, i_offset + p_sys->i_data_begin ) ) + if ( i_ret == VLC_SUCCESS ) { - msg_Warn( p_input, "cannot resynch after seek (EOF?)" ); - return -1; + SeekPrepare( p_demux ); + p_sys->i_seek_track = 0; + WaitKeyframe( p_demux ); } + return i_ret; + } - p_sys->i_time = -1; - for( i = 0; i < 128 ; i++ ) + case DEMUX_GET_POSITION: + if( p_sys->i_time < 0 ) return VLC_EGENERIC; + if( p_sys->i_length > 0 ) { -#define p_stream p_sys->stream[i] - if( p_stream ) - { - p_stream->i_time = -1; - } -#undef p_stream + pf = (double*)va_arg( args, double * ); + *pf = p_sys->i_time / (double)p_sys->i_length; + return VLC_SUCCESS; } - } + return demux_vaControlHelper( p_demux->s, + __MIN( INT64_MAX, p_sys->i_data_begin ), + __MIN( INT64_MAX, p_sys->i_data_end ), + __MIN( INT64_MAX, p_sys->i_bitrate ), + __MIN( INT16_MAX, p_sys->p_fp->i_min_data_packet_size ), + i_query, args ); - /* Check if we need to send the audio data to decoder */ - b_play_audio = !p_input->stream.control.b_mute; + case DEMUX_SET_POSITION: + if ( p_sys->p_fp && + ! ( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) ) + return VLC_EGENERIC; - for( ;; ) - { - mtime_t i_length; - mtime_t i_time_begin = GetMoviePTS( p_sys ); - int i_result; + SeekPrepare( p_demux ); - if( p_input->b_die ) + if( p_sys->b_index && p_sys->i_length > 0 ) { - break; - } + va_list acpy; + va_copy( acpy, args ); + f = (double)va_arg( acpy, double ); + va_end( acpy ); - if( ( i_result = DemuxPacket( p_input, b_play_audio ) ) <= 0 ) - { - return i_result; + if( !SeekIndex( p_demux, -1, f ) ) + return VLC_SUCCESS; } - if( i_time_begin == -1 ) + return SeekPercent( p_demux, i_query, args ); + + case DEMUX_GET_META: + p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* ); + vlc_meta_Merge( p_meta, p_sys->meta ); + return VLC_SUCCESS; + + case DEMUX_CAN_SEEK: + if ( p_sys->p_fp && + ! ( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) ) { - i_time_begin = GetMoviePTS( p_sys ); + bool *pb_bool = (bool*)va_arg( args, bool * ); + *pb_bool = false; + return VLC_SUCCESS; } - else + // ft + + default: + return demux_vaControlHelper( p_demux->s, + __MIN( INT64_MAX, p_sys->i_data_begin ), + __MIN( INT64_MAX, p_sys->i_data_end), + __MIN( INT64_MAX, p_sys->i_bitrate ), + ( p_sys->p_fp ) ? __MIN( INT_MAX, p_sys->p_fp->i_min_data_packet_size ) : 1, + i_query, args ); + } +} + +/***************************************************************************** + * + *****************************************************************************/ +static mtime_t GetMoviePTS( demux_sys_t *p_sys ) +{ + mtime_t i_time = -1; + int i; + /* As some tracks might have been deselected by access, the PCR might + * stop updating */ + for( i = 0; i < MAX_ASF_TRACKS ; i++ ) + { + const asf_track_t *tk = p_sys->track[i]; + + if( tk && tk->p_es && tk->b_selected ) { - i_length = GetMoviePTS( p_sys ) - i_time_begin; - if( i_length < 0 || i_length >= 40 * 1000 ) + /* Skip discrete tracks */ + if ( tk->i_cat != VIDEO_ES && tk->i_cat != AUDIO_ES ) + continue; + + /* We need to have all ES seen once, as they might have lower DTS */ + if ( tk->i_time + (int64_t)p_sys->p_fp->i_preroll * 1000 < 0 ) { - break; + /* early fail */ + return -1; + } + else if ( tk->i_time > -1 && ( i_time == -1 || i_time > tk->i_time ) ) + { + i_time = tk->i_time; } } } - p_sys->i_time = GetMoviePTS( p_sys ); - if( p_sys->i_time >= 0 ) + return i_time; +} + +static void Packet_SetAR( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, + uint8_t i_ratio_x, uint8_t i_ratio_y ) +{ + demux_t *p_demux = p_packetsys->p_demux; + asf_track_t *tk = p_demux->p_sys->track[i_stream_number]; + if ( tk->p_fmt->video.i_sar_num == i_ratio_x && tk->p_fmt->video.i_sar_den == i_ratio_y ) + return; + + /* Only apply if origin pixel size >= 1x1, due to broken yacast */ + if ( tk->p_fmt->video.i_height * i_ratio_x > tk->p_fmt->video.i_width * i_ratio_y ) { - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_sys->i_time * 9 / 100 ); + vout_thread_t *p_vout = input_GetVout( p_demux->p_input ); + if ( p_vout ) + { + msg_Info( p_demux, "Changing aspect ratio to %i/%i", i_ratio_x, i_ratio_y ); + vout_ChangeAspectRatio( p_vout, i_ratio_x, i_ratio_y ); + vlc_object_release( p_vout ); + } } + tk->p_fmt->video.i_sar_num = i_ratio_x; + tk->p_fmt->video.i_sar_den = i_ratio_y; +} - return( 1 ); +static void Packet_UpdateTime( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, + mtime_t i_time ) +{ + asf_track_t *tk = p_packetsys->p_demux->p_sys->track[i_stream_number]; + if ( tk ) + tk->i_time = i_time; } -/***************************************************************************** - * Close: frees unused data - *****************************************************************************/ -static void Close( vlc_object_t * p_this ) +static asf_track_info_t * Packet_GetTrackInfo( asf_packet_sys_t *p_packetsys, + uint8_t i_stream_number ) +{ + asf_track_t *tk = p_packetsys->p_demux->p_sys->track[i_stream_number]; + if (!tk) + return NULL; + else + return & tk->info; +} + +static bool Packet_DoSkip( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, bool b_packet_keyframe ) { - input_thread_t *p_input = (input_thread_t *)p_this; - demux_sys_t *p_sys = p_input->p_demux_data; - int i_stream; + demux_t *p_demux = p_packetsys->p_demux; + demux_sys_t *p_sys = p_demux->p_sys; + const asf_track_t *tk = p_sys->track[i_stream_number]; - msg_Dbg( p_input, "Freeing all memory" ); + if( tk == NULL ) + { + msg_Warn( p_demux, "undeclared stream[Id 0x%x]", i_stream_number ); + return true; + } - ASF_FreeObjectRoot( p_input->s, p_sys->p_root ); - for( i_stream = 0; i_stream < 128; i_stream++ ) + if( p_sys->i_wait_keyframe ) { -#define p_stream p_sys->stream[i_stream] - if( p_stream ) + if ( i_stream_number == p_sys->i_seek_track ) { - if( p_stream->p_pes ) + if ( !b_packet_keyframe ) { - input_DeletePES( p_input->p_method_data, p_stream->p_pes ); + p_sys->i_wait_keyframe--; + return true; } - free( p_stream ); + else + p_sys->i_wait_keyframe = 0; } -#undef p_stream + else + return true; } - free( p_sys ); + + if( !tk->p_es ) + return true; + + return false; } +static void Packet_Send(asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, block_t **pp_frame) +{ + demux_t *p_demux = p_packetsys->p_demux; + demux_sys_t *p_sys = p_demux->p_sys; + const asf_track_t *tk = p_sys->track[i_stream_number]; + if ( !tk ) + return; + + block_t *p_gather = block_ChainGather( *pp_frame ); + + if( p_sys->i_time < VLC_TS_0 && tk->i_time > VLC_TS_INVALID ) + { + p_sys->i_time = tk->i_time; + es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_time ); +#ifdef ASF_DEBUG + msg_Dbg( p_demux, " setting PCR to %"PRId64, VLC_TS_0 + p_sys->i_time ); +#endif + } + +#ifdef ASF_DEBUG + msg_Dbg( p_demux, " sending packet dts %"PRId64" pts %"PRId64" pcr %"PRId64, p_gather->i_dts, p_gather->i_pts, p_sys->i_time ); +#endif + es_out_Send( p_demux->out, tk->p_es, p_gather ); + *pp_frame = NULL; +} /***************************************************************************** * *****************************************************************************/ -static mtime_t GetMoviePTS( demux_sys_t *p_sys ) +typedef struct asf_es_priorities_t { - mtime_t i_time; - int i_stream; + uint16_t *pi_stream_numbers; + uint16_t i_count; +} asf_es_priorities_t; - i_time = -1; - for( i_stream = 0; i_stream < 128 ; i_stream++ ) +/* Fills up our exclusion list */ +static void ASF_fillup_es_priorities_ex( demux_sys_t *p_sys, void *p_hdr, + asf_es_priorities_t *p_prios ) +{ + /* Find stream exclusions */ + asf_object_advanced_mutual_exclusion_t *p_mutex = + ASF_FindObject( p_hdr, &asf_object_advanced_mutual_exclusion, 0 ); + if (! p_mutex ) return; + +#if ( UINT_MAX > SIZE_MAX / 2 ) + if ( p_sys->i_track > (size_t)SIZE_MAX / sizeof(uint16_t) ) + return; +#endif + p_prios->pi_stream_numbers = malloc( (size_t)p_sys->i_track * sizeof(uint16_t) ); + if ( !p_prios->pi_stream_numbers ) return; + + if ( p_mutex->i_stream_number_count ) { -#define p_stream p_sys->stream[i_stream] - if( p_stream && p_stream->p_es && p_stream->i_time > 0) + /* Just set highest prio on highest in the group */ + for ( uint16_t i = 1; i < p_mutex->i_stream_number_count; i++ ) { - if( i_time < 0 ) - { - i_time = p_stream->i_time; - } - else - { - i_time = __MIN( i_time, p_stream->i_time ); - } + if ( p_prios->i_count > p_sys->i_track || i > p_sys->i_track ) break; + p_prios->pi_stream_numbers[ p_prios->i_count++ ] = p_mutex->pi_stream_number[ i ]; } -#undef p_stream } - - return( i_time ); } -#define GETVALUE2b( bits, var, def ) \ - switch( (bits)&0x03 ) \ - { \ - case 1: var = p_peek[i_skip]; i_skip++; break; \ - case 2: var = GetWLE( p_peek + i_skip ); i_skip+= 2; break; \ - case 3: var = GetDWLE( p_peek + i_skip ); i_skip+= 4; break; \ - case 0: \ - default: var = def; break;\ - } - -static int DemuxPacket( input_thread_t *p_input, vlc_bool_t b_play_audio ) +/* Fills up our bitrate exclusion list */ +static void ASF_fillup_es_bitrate_priorities_ex( demux_sys_t *p_sys, void *p_hdr, + asf_es_priorities_t *p_prios ) { - demux_sys_t *p_sys = p_input->p_demux_data; - int i_data_packet_min = p_sys->p_fp->i_min_data_packet_size; - uint8_t *p_peek; - int i_skip; + /* Find bitrate exclusions */ + asf_object_bitrate_mutual_exclusion_t *p_bitrate_mutex = + ASF_FindObject( p_hdr, &asf_object_bitrate_mutual_exclusion_guid, 0 ); + if (! p_bitrate_mutex ) return; + +#if ( UINT_MAX > SIZE_MAX / 2 ) + if ( p_sys->i_track > (size_t)SIZE_MAX / sizeof(uint16_t) ) + return; +#endif + p_prios->pi_stream_numbers = malloc( (size_t)p_sys->i_track * sizeof( uint16_t ) ); + if ( !p_prios->pi_stream_numbers ) return; - int i_packet_size_left; - int i_packet_flags; - int i_packet_property; + if ( p_bitrate_mutex->i_stream_number_count ) + { + /* Just remove < highest */ + for ( uint16_t i = 1; i < p_bitrate_mutex->i_stream_number_count; i++ ) + { + if ( p_prios->i_count > p_sys->i_track || i > p_sys->i_track ) break; + p_prios->pi_stream_numbers[ p_prios->i_count++ ] = p_bitrate_mutex->pi_stream_numbers[ i ]; + } + } - int b_packet_multiple_payload; - int i_packet_length; - int i_packet_sequence; - int i_packet_padding_length; +} - uint32_t i_packet_send_time; - uint16_t i_packet_duration; - int i_payload; - int i_payload_count; - int i_payload_length_type; +#define GET_CHECKED( target, getter, maxtarget, temp ) \ +{\ + temp i_temp = getter;\ + if ( i_temp > maxtarget ) {\ + msg_Warn( p_demux, "rejecting stream %u : " #target " overflow", i_stream );\ + es_format_Clean( &fmt );\ + goto error;\ + } else {\ + target = i_temp;\ + }\ +} +static int DemuxInit( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; - if( stream_Peek( p_input->s, &p_peek,i_data_packet_min)i_time = -1; + p_sys->i_length = 0; + p_sys->i_bitrate = 0; + p_sys->p_root = NULL; + p_sys->p_fp = NULL; + p_sys->b_index = 0; + p_sys->i_track = 0; + p_sys->i_seek_track = 0; + p_sys->i_wait_keyframe = 0; + for( int i = 0; i < MAX_ASF_TRACKS; i++ ) { - // EOF ? - msg_Warn( p_input, "cannot peek while getting new packet, EOF ?" ); - return( 0 ); + p_sys->track[i] = NULL; } - i_skip = 0; + p_sys->i_data_begin = 0; + p_sys->i_data_end = 0; + p_sys->i_preroll_start = 0; + p_sys->meta = NULL; - /* *** parse error correction if present *** */ - if( p_peek[0]&0x80 ) + /* Now load all object ( except raw data ) */ + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_canfastseek ); + if( !(p_sys->p_root = ASF_ReadObjectRoot(p_demux->s, p_sys->b_canfastseek)) ) { - unsigned int i_error_correction_length_type; - unsigned int i_error_correction_data_length; - unsigned int i_opaque_data_present; - - i_error_correction_data_length = p_peek[0] & 0x0f; // 4bits - i_opaque_data_present = ( p_peek[0] >> 4 )& 0x01; // 1bit - i_error_correction_length_type = ( p_peek[0] >> 5 ) & 0x03; // 2bits - i_skip += 1; // skip error correction flags - - if( i_error_correction_length_type != 0x00 || - i_opaque_data_present != 0 || - i_error_correction_data_length != 0x02 ) - { - goto loop_error_recovery; - } - - i_skip += i_error_correction_data_length; + msg_Warn( p_demux, "ASF plugin discarded (not a valid file)" ); + return VLC_EGENERIC; } - else + p_sys->p_fp = p_sys->p_root->p_fp; + + if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) { - msg_Warn( p_input, "p_peek[0]&0x80 != 0x80" ); + msg_Warn( p_demux, "ASF plugin discarded (invalid file_properties object)" ); + goto error; } - /* sanity check */ - if( i_skip + 2 >= i_data_packet_min ) + if ( ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_content_encryption_guid, 0 ) != NULL + || ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_extended_content_encryption_guid, 0 ) != NULL + || ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_advanced_content_encryption_guid, 0 ) != NULL ) { - goto loop_error_recovery; + dialog_Fatal( p_demux, _("Could not demux ASF stream"), "%s", + _("DRM protected streams are not supported.") ); + goto error; } - i_packet_flags = p_peek[i_skip]; i_skip++; - i_packet_property = p_peek[i_skip]; i_skip++; - - b_packet_multiple_payload = i_packet_flags&0x01; + p_sys->i_track = ASF_CountObject( p_sys->p_root->p_hdr, + &asf_object_stream_properties_guid ); + if( p_sys->i_track == 0 ) + { + msg_Warn( p_demux, "ASF plugin discarded (cannot find any stream!)" ); + goto error; + } + msg_Dbg( p_demux, "found %u streams", p_sys->i_track ); - /* read some value */ - GETVALUE2b( i_packet_flags >> 5, i_packet_length, i_data_packet_min ); - GETVALUE2b( i_packet_flags >> 1, i_packet_sequence, 0 ); - GETVALUE2b( i_packet_flags >> 3, i_packet_padding_length, 0 ); + /* check if index is available */ + asf_object_index_t *p_index = ASF_FindObject( p_sys->p_root, + &asf_object_simple_index_guid, 0 ); + const bool b_index = p_index && p_index->i_index_entry_count; - i_packet_send_time = GetDWLE( p_peek + i_skip ); i_skip += 4; - i_packet_duration = GetWLE( p_peek + i_skip ); i_skip += 2; + /* Find the extended header if any */ + asf_object_t *p_hdr_ext = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_header_extension_guid, 0 ); -// i_packet_size_left = i_packet_length; // XXX données reellement lu - /* FIXME I have to do that for some file, I don't known why */ - i_packet_size_left = i_data_packet_min; + asf_object_language_list_t *p_languages = NULL; + asf_es_priorities_t fmt_priorities_ex = { NULL, 0 }; + asf_es_priorities_t fmt_priorities_bitrate_ex = { NULL, 0 }; - if( b_packet_multiple_payload ) - { - i_payload_count = p_peek[i_skip] & 0x3f; - i_payload_length_type = ( p_peek[i_skip] >> 6 )&0x03; - i_skip++; - } - else + if( p_hdr_ext ) { - i_payload_count = 1; - i_payload_length_type = 0x02; // unused + p_languages = ASF_FindObject( p_hdr_ext, &asf_object_language_list, 0 ); + + ASF_fillup_es_priorities_ex( p_sys, p_hdr_ext, &fmt_priorities_ex ); + ASF_fillup_es_bitrate_priorities_ex( p_sys, p_hdr_ext, &fmt_priorities_bitrate_ex ); } - for( i_payload = 0; i_payload < i_payload_count ; i_payload++ ) + for( unsigned i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) { - asf_stream_t *p_stream; + asf_track_t *tk; + asf_object_stream_properties_t *p_sp; + asf_object_extended_stream_properties_t *p_esp; + bool b_access_selected; - int i_stream_number; - int i_media_object_number; - int i_media_object_offset; - int i_replicated_data_length; - int i_payload_data_length; - int i_payload_data_pos; - int i_sub_payload_data_length; - int i_tmp; + p_sp = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_stream_properties_guid, + i_stream ); + p_esp = NULL; + + tk = p_sys->track[p_sp->i_stream_number] = malloc( sizeof( asf_track_t ) ); + memset( tk, 0, sizeof( asf_track_t ) ); - mtime_t i_pts; - mtime_t i_pts_delta; + tk->i_time = -1; + tk->info.p_sp = p_sp; + tk->p_es = NULL; + tk->info.p_esp = NULL; + tk->info.p_frame = NULL; - if( i_skip >= i_packet_size_left ) + if ( strncmp( p_demux->psz_access, "mms", 3 ) ) { - /* prevent some segfault with invalid file */ - break; + /* Check (not mms) if this track is selected (ie will receive data) */ + if( !stream_Control( p_demux->s, STREAM_GET_PRIVATE_ID_STATE, + (int) p_sp->i_stream_number, &b_access_selected ) && + !b_access_selected ) + { + tk->i_cat = UNKNOWN_ES; + msg_Dbg( p_demux, "ignoring not selected stream(ID:%u) (by access)", + p_sp->i_stream_number ); + continue; + } } - i_stream_number = p_peek[i_skip] & 0x7f; - i_skip++; + /* Find the associated extended_stream_properties if any */ + if( p_hdr_ext ) + { + int i_ext_stream = ASF_CountObject( p_hdr_ext, + &asf_object_extended_stream_properties_guid ); + for( int i = 0; i < i_ext_stream; i++ ) + { + asf_object_t *p_tmp = + ASF_FindObject( p_hdr_ext, + &asf_object_extended_stream_properties_guid, i ); + if( p_tmp->ext_stream.i_stream_number == p_sp->i_stream_number ) + { + p_esp = &p_tmp->ext_stream; + tk->info.p_esp = p_esp; + break; + } + } + } - GETVALUE2b( i_packet_property >> 4, i_media_object_number, 0 ); - GETVALUE2b( i_packet_property >> 2, i_tmp, 0 ); - GETVALUE2b( i_packet_property, i_replicated_data_length, 0 ); + es_format_t fmt; - if( i_replicated_data_length > 1 ) // should be at least 8 bytes + if( guidcmp( &p_sp->i_stream_type, &asf_object_stream_type_audio ) && + p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 ) { - i_pts = (mtime_t)GetDWLE( p_peek + i_skip + 4 ) * 1000; - i_skip += i_replicated_data_length; - i_pts_delta = 0; + uint8_t *p_data = p_sp->p_type_specific_data; + int i_format; - i_media_object_offset = i_tmp; + es_format_Init( &fmt, AUDIO_ES, 0 ); + i_format = GetWLE( &p_data[0] ); + wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL ); + + GET_CHECKED( fmt.audio.i_channels, GetWLE( &p_data[2] ), + 255, uint16_t ); + GET_CHECKED( fmt.audio.i_rate, GetDWLE( &p_data[4] ), + UINT_MAX, uint32_t ); + GET_CHECKED( fmt.i_bitrate, GetDWLE( &p_data[8] ) * 8, + UINT_MAX, uint32_t ); + fmt.audio.i_blockalign = GetWLE( &p_data[12] ); + fmt.audio.i_bitspersample = GetWLE( &p_data[14] ); - if( i_skip >= i_packet_size_left ) + if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) && + i_format != WAVE_FORMAT_MPEGLAYER3 && + i_format != WAVE_FORMAT_MPEG ) { - break; + GET_CHECKED( fmt.i_extra, __MIN( GetWLE( &p_data[16] ), + p_sp->i_type_specific_data_length - + sizeof( WAVEFORMATEX ) ), + INT_MAX, uint32_t ); + fmt.p_extra = malloc( fmt.i_extra ); + memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )], + fmt.i_extra ); } + + msg_Dbg( p_demux, "added new audio stream(codec:0x%x,ID:%d)", + GetWLE( p_data ), p_sp->i_stream_number ); } - else if( i_replicated_data_length == 1 ) + else if( guidcmp( &p_sp->i_stream_type, + &asf_object_stream_type_video ) && + p_sp->i_type_specific_data_length >= 11 + + sizeof( VLC_BITMAPINFOHEADER ) ) { + uint8_t *p_data = &p_sp->p_type_specific_data[11]; - msg_Dbg( p_input, "found compressed payload" ); + es_format_Init( &fmt, VIDEO_ES, + VLC_FOURCC( p_data[16], p_data[17], + p_data[18], p_data[19] ) ); - i_pts = (mtime_t)i_tmp * 1000; - i_pts_delta = (mtime_t)p_peek[i_skip] * 1000; i_skip++; + GET_CHECKED( fmt.video.i_width, GetDWLE( p_data + 4 ), + UINT_MAX, uint32_t ); + GET_CHECKED( fmt.video.i_height, GetDWLE( p_data + 8 ), + UINT_MAX, uint32_t ); - i_media_object_offset = 0; - } - else - { - i_pts = (mtime_t)i_packet_send_time * 1000; - i_pts_delta = 0; + if( p_esp && p_esp->i_average_time_per_frame > 0 ) + { + fmt.video.i_frame_rate = 10000000; + GET_CHECKED( fmt.video.i_frame_rate_base, + p_esp->i_average_time_per_frame, + UINT_MAX, uint64_t ); + } - i_media_object_offset = i_tmp; - } + if( fmt.i_codec == VLC_FOURCC( 'D','V','R',' ') ) + { + /* DVR-MS special ASF */ + fmt.i_codec = VLC_FOURCC( 'm','p','g','2' ) ; + fmt.b_packetized = false; + } - i_pts = __MAX( i_pts - p_sys->p_fp->i_preroll * 1000, 0 ); - if( b_packet_multiple_payload ) - { - GETVALUE2b( i_payload_length_type, i_payload_data_length, 0 ); - } - else - { - i_payload_data_length = i_packet_length - - i_packet_padding_length - i_skip; - } + if( p_sp->i_type_specific_data_length > 11 + + sizeof( VLC_BITMAPINFOHEADER ) ) + { + GET_CHECKED( fmt.i_extra, __MIN( GetDWLE( p_data ), + p_sp->i_type_specific_data_length - 11 - + sizeof( VLC_BITMAPINFOHEADER ) ), + UINT_MAX, uint32_t ); + fmt.p_extra = malloc( fmt.i_extra ); + memcpy( fmt.p_extra, &p_data[sizeof( VLC_BITMAPINFOHEADER )], + fmt.i_extra ); + } - if( i_payload_data_length < 0 || i_skip + i_payload_data_length > i_packet_size_left ) - { - break; - } + /* Look for an aspect ratio */ + if( p_sys->p_root->p_metadata ) + { + asf_object_metadata_t *p_meta = p_sys->p_root->p_metadata; + unsigned int i_aspect_x = 0, i_aspect_y = 0; + uint32_t i; + for( i = 0; i < p_meta->i_record_entries_count; i++ ) + { + if( !strcmp( p_meta->record[i].psz_name, "AspectRatioX" ) ) + { + if( (!i_aspect_x && !p_meta->record[i].i_stream) || + p_meta->record[i].i_stream == + p_sp->i_stream_number ) + GET_CHECKED( i_aspect_x, p_meta->record[i].i_val, + UINT_MAX, uint64_t ); + } + if( !strcmp( p_meta->record[i].psz_name, "AspectRatioY" ) ) + { + if( (!i_aspect_y && !p_meta->record[i].i_stream) || + p_meta->record[i].i_stream == + p_sp->i_stream_number ) + GET_CHECKED( i_aspect_y, p_meta->record[i].i_val, + UINT_MAX, uint64_t ); + } + } -#if 0 - msg_Dbg( p_input, - "payload(%d/%d) stream_number:%d media_object_number:%d media_object_offset:%d replicated_data_length:%d payload_data_length %d", - i_payload + 1, - i_payload_count, - i_stream_number, - i_media_object_number, - i_media_object_offset, - i_replicated_data_length, - i_payload_data_length ); -#endif + if( i_aspect_x && i_aspect_y ) + { + fmt.video.i_sar_num = i_aspect_x; + fmt.video.i_sar_den = i_aspect_y; + } + } - if( !( p_stream = p_sys->stream[i_stream_number] ) ) - { - msg_Warn( p_input, - "undeclared stream[Id 0x%x]", i_stream_number ); - i_skip += i_payload_data_length; - continue; // over payload - } + /* If there is a video track then use the index for seeking */ + p_sys->b_index = b_index; - if( !p_stream->p_es ) - { - i_skip += i_payload_data_length; - continue; + msg_Dbg( p_demux, "added new video stream(ID:%d)", + p_sp->i_stream_number ); } - - - for( i_payload_data_pos = 0; - i_payload_data_pos < i_payload_data_length && - i_packet_size_left > 0; - i_payload_data_pos += i_sub_payload_data_length ) + else if( guidcmp( &p_sp->i_stream_type, &asf_object_extended_stream_header ) && + p_sp->i_type_specific_data_length >= 64 ) { - data_packet_t *p_data; - int i_read; - // read sub payload length - if( i_replicated_data_length == 1 ) + /* Now follows a 64 byte header of which we don't know much */ + guid_t *p_ref = (guid_t *)p_sp->p_type_specific_data; + uint8_t *p_data = p_sp->p_type_specific_data + 64; + unsigned int i_data = p_sp->i_type_specific_data_length - 64; + + msg_Dbg( p_demux, "Ext stream header detected. datasize = %d", p_sp->i_type_specific_data_length ); + if( guidcmp( p_ref, &asf_object_extended_stream_type_audio ) && + i_data >= sizeof( WAVEFORMATEX ) - 2) { - i_sub_payload_data_length = p_peek[i_skip]; i_skip++; - i_payload_data_pos++; + uint16_t i_format; + es_format_Init( &fmt, AUDIO_ES, 0 ); + i_format = GetWLE( &p_data[0] ); + if( i_format == 0 ) + fmt.i_codec = VLC_CODEC_A52; + else + wf_tag_to_fourcc( i_format, &fmt.i_codec, NULL ); + GET_CHECKED( fmt.audio.i_channels, GetWLE( &p_data[2] ), + 255, uint16_t ); + GET_CHECKED( fmt.audio.i_rate, GetDWLE( &p_data[4] ), + UINT_MAX, uint32_t ); + GET_CHECKED( fmt.i_bitrate, GetDWLE( &p_data[8] ) * 8, + UINT_MAX, uint32_t ); + fmt.audio.i_blockalign = GetWLE( &p_data[12] ); + fmt.audio.i_bitspersample = GetWLE( &p_data[14] ); + fmt.b_packetized = true; + + if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) && + i_format != WAVE_FORMAT_MPEGLAYER3 && + i_format != WAVE_FORMAT_MPEG && i_data >= 19 ) + { + GET_CHECKED( fmt.i_extra, __MIN( GetWLE( &p_data[16] ), + p_sp->i_type_specific_data_length - + sizeof( WAVEFORMATEX ) ), + INT_MAX, uint32_t ); + fmt.p_extra = malloc( fmt.i_extra ); + memcpy( fmt.p_extra, &p_data[sizeof( WAVEFORMATEX )], + fmt.i_extra ); + } + + msg_Dbg( p_demux, "added new audio stream (codec:0x%x,ID:%d)", + i_format, p_sp->i_stream_number ); } else { - i_sub_payload_data_length = i_payload_data_length; + es_format_Init( &fmt, UNKNOWN_ES, 0 ); + } + } + else + { + es_format_Init( &fmt, UNKNOWN_ES, 0 ); + } + + tk->i_cat = fmt.i_cat; + if( fmt.i_cat != UNKNOWN_ES ) + { + if( p_esp && p_languages && + p_esp->i_language_index < p_languages->i_language ) + { + fmt.psz_language = strdup( p_languages->ppsz_language[p_esp->i_language_index] ); + char *p; + if( fmt.psz_language && (p = strchr( fmt.psz_language, '-' )) ) + *p = '\0'; } - /* FIXME I don't use i_media_object_number, sould I ? */ - if( p_stream->p_pes && i_media_object_offset == 0 ) + /* Set our priority so we won't get multiple videos */ + int i_priority = ES_PRIORITY_SELECTABLE_MIN; + for( uint16_t i = 0; i < fmt_priorities_ex.i_count; i++ ) { - /* send complete packet to decoder */ - if( p_stream->p_pes->i_pes_size > 0 ) + if ( fmt_priorities_ex.pi_stream_numbers[i] == p_sp->i_stream_number ) { - es_out_Send( p_input->p_es_out, p_stream->p_es, p_stream->p_pes ); - p_stream->p_pes = NULL; + i_priority = ES_PRIORITY_NOT_DEFAULTABLE; + break; } } - - if( !p_stream->p_pes ) // add a new PES + for( uint16_t i = 0; i < fmt_priorities_bitrate_ex.i_count; i++ ) { - p_stream->i_time = - ( (mtime_t)i_pts + i_payload * (mtime_t)i_pts_delta ); - - p_stream->p_pes = input_NewPES( p_input->p_method_data ); - p_stream->p_pes->i_dts = - p_stream->p_pes->i_pts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - p_stream->i_time * 9 /100 ); - - //msg_Err( p_input, "stream[0x%2x] pts=%lld", i_stream_number, p_stream->p_pes->i_pts ); - p_stream->p_pes->p_next = NULL; - p_stream->p_pes->i_nb_data = 0; - p_stream->p_pes->i_pes_size = 0; + if ( fmt_priorities_bitrate_ex.pi_stream_numbers[i] == p_sp->i_stream_number ) + { + i_priority = ES_PRIORITY_NOT_DEFAULTABLE; + break; + } } + fmt.i_priority = i_priority; + + if ( i_stream <= INT_MAX ) + fmt.i_id = i_stream; + else + msg_Warn( p_demux, "Can't set fmt.i_id to match stream id %u", i_stream ); - i_read = i_sub_payload_data_length + i_skip; - if((p_data = stream_DataPacket( p_input->s,i_read,VLC_TRUE))==NULL) + if ( fmt.i_cat == VIDEO_ES ) { - msg_Warn( p_input, "cannot read data" ); - return( 0 ); + /* Backup our video format */ + tk->p_fmt = malloc( sizeof( es_format_t ) ); + if ( tk->p_fmt ) + es_format_Copy( tk->p_fmt, &fmt ); } - p_data->p_payload_start += i_skip; - i_packet_size_left -= i_read; + tk->p_es = es_out_Add( p_demux->out, &fmt ); - if( !p_stream->p_pes->p_first ) + if( !stream_Control( p_demux->s, STREAM_GET_PRIVATE_ID_STATE, + (int) p_sp->i_stream_number, &b_access_selected ) && + b_access_selected ) { - p_stream->p_pes->p_first = p_stream->p_pes->p_last = p_data; + p_sys->i_access_selected_track[fmt.i_cat] = p_sp->i_stream_number; } - else - { - p_stream->p_pes->p_last->p_next = p_data; - p_stream->p_pes->p_last = p_data; - } - p_stream->p_pes->i_pes_size += i_sub_payload_data_length; - p_stream->p_pes->i_nb_data++; - i_skip = 0; - if( i_packet_size_left > 0 ) + } + else + { + msg_Dbg( p_demux, "ignoring unknown stream(ID:%d)", + p_sp->i_stream_number ); + } + + es_format_Clean( &fmt ); + } + + free( fmt_priorities_ex.pi_stream_numbers ); + free( fmt_priorities_bitrate_ex.pi_stream_numbers ); + + p_sys->i_data_begin = p_sys->p_root->p_data->i_object_pos + 50; + if( p_sys->p_root->p_data->i_object_size != 0 ) + { /* local file */ + p_sys->i_data_end = p_sys->p_root->p_data->i_object_pos + + p_sys->p_root->p_data->i_object_size; + p_sys->i_data_end = __MIN( (uint64_t)stream_Size( p_demux->s ), p_sys->i_data_end ); + } + else + { /* live/broacast */ + p_sys->i_data_end = 0; + } + + /* go to first packet */ + stream_Seek( p_demux->s, p_sys->i_data_begin ); + + /* try to calculate movie time */ + if( p_sys->p_fp->i_data_packets_count > 0 ) + { + uint64_t i_count; + uint64_t i_size = stream_Size( p_demux->s ); + + if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end ) + { + i_size = p_sys->i_data_end; + } + + /* real number of packets */ + i_count = ( i_size - p_sys->i_data_begin ) / + p_sys->p_fp->i_min_data_packet_size; + + /* calculate the time duration in micro-s */ + p_sys->i_length = (mtime_t)p_sys->p_fp->i_play_duration / 10 * + (mtime_t)i_count / + (mtime_t)p_sys->p_fp->i_data_packets_count - p_sys->p_fp->i_preroll * 1000; + if( p_sys->i_length < 0 ) + p_sys->i_length = 0; + + if( p_sys->i_length > 0 ) + { + p_sys->i_bitrate = 8 * i_size * 1000000 / p_sys->i_length; + } + } + + /* Create meta information */ + p_sys->meta = vlc_meta_New(); + + asf_object_content_description_t *p_cd; + if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_content_description_guid, 0 ) ) ) + { + if( p_cd->psz_title && *p_cd->psz_title ) + { + vlc_meta_SetTitle( p_sys->meta, p_cd->psz_title ); + } + if( p_cd->psz_artist && *p_cd->psz_artist ) + { + vlc_meta_SetArtist( p_sys->meta, p_cd->psz_artist ); + } + if( p_cd->psz_copyright && *p_cd->psz_copyright ) + { + vlc_meta_SetCopyright( p_sys->meta, p_cd->psz_copyright ); + } + if( p_cd->psz_description && *p_cd->psz_description ) + { + vlc_meta_SetDescription( p_sys->meta, p_cd->psz_description ); + } + if( p_cd->psz_rating && *p_cd->psz_rating ) + { + vlc_meta_SetRating( p_sys->meta, p_cd->psz_rating ); + } + } + /// \tood Fix Child meta for ASF tracks +#if 0 + for( i_stream = 0, i = 0; i < MAX_ASF_TRACKS; i++ ) + { + asf_object_codec_list_t *p_cl = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_codec_list_guid, 0 ); + + if( p_sys->track[i] ) + { + vlc_meta_t *tk = vlc_meta_New(); + TAB_APPEND( p_sys->meta->i_track, p_sys->meta->track, tk ); + + if( p_cl && i_stream < p_cl->i_codec_entries_count ) { - if( stream_Peek( p_input->s, &p_peek, i_packet_size_left ) - < i_packet_size_left ) + if( p_cl->codec[i_stream].psz_name && + *p_cl->codec[i_stream].psz_name ) + { + vlc_meta_Add( tk, VLC_META_CODEC_NAME, + p_cl->codec[i_stream].psz_name ); + } + if( p_cl->codec[i_stream].psz_description && + *p_cl->codec[i_stream].psz_description ) { - // EOF ? - msg_Warn( p_input, "cannot peek, EOF ?" ); - return( 0 ); + vlc_meta_Add( tk, VLC_META_CODEC_DESCRIPTION, + p_cl->codec[i_stream].psz_description ); } } + i_stream++; } } +#endif + + p_sys->packet_sys.pi_preroll = &p_sys->p_fp->i_preroll; + p_sys->packet_sys.pi_preroll_start = &p_sys->i_preroll_start; - if( i_packet_size_left > 0 ) + return VLC_SUCCESS; + +error: + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + return VLC_EGENERIC; +} + +/***************************************************************************** + * FlushRemainingPackets: flushes tail packets + *****************************************************************************/ + +static void FlushRemainingPackets( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + for ( unsigned int i = 0; i < MAX_ASF_TRACKS; i++ ) { - if( stream_Read( p_input->s, NULL, i_packet_size_left ) - < i_packet_size_left ) - { - msg_Warn( p_input, "cannot skip data, EOF ?" ); - return( 0 ); - } + asf_track_t *tk = p_sys->track[i]; + if( tk && tk->info.p_frame ) + Packet_Send( &p_sys->packet_sys, i, &tk->info.p_frame ); } +} - return( 1 ); +/***************************************************************************** + * + *****************************************************************************/ +static void DemuxEnd( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; -loop_error_recovery: - msg_Warn( p_input, "unsupported packet header" ); - if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) + if( p_sys->p_root ) + { + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + p_sys->p_root = NULL; + } + if( p_sys->meta ) { - msg_Err( p_input, "unsupported packet header, fatal error" ); - return( -1 ); + vlc_meta_Delete( p_sys->meta ); + p_sys->meta = NULL; } - stream_Read( p_input->s, NULL, i_data_packet_min ); - return( 1 ); -} + for( int i = 0; i < MAX_ASF_TRACKS; i++ ) + { + asf_track_t *tk = p_sys->track[i]; + + if( tk ) + { + if( tk->info.p_frame ) + block_ChainRelease( tk->info.p_frame ); + if( tk->p_es ) + { + es_out_Del( p_demux->out, tk->p_es ); + } + if ( tk->p_fmt ) + { + es_format_Clean( tk->p_fmt ); + free( tk->p_fmt ); + } + free( tk ); + } + p_sys->track[i] = 0; + } +}