X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fasf%2Fasf.c;h=18c293d3cae52e0d931594e2d7abfce877bb809e;hb=54bbfac289eeeed9ba044bf9f9e1ee43e5642c78;hp=92037f5095c329250732844f144720ab548837f6;hpb=22d851e1d2c4aaf47a2f6ebf0de01b3a8e1d4cdd;p=vlc diff --git a/modules/demux/asf/asf.c b/modules/demux/asf/asf.c index 92037f5095..0622ff81a3 100644 --- a/modules/demux/asf/asf.c +++ b/modules/demux/asf/asf.c @@ -1,864 +1,1287 @@ /***************************************************************************** - * asf.c : ASFv01 file input module for vlc + * asf.c : ASF demux module ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: asf.c,v 1.18 2003/01/25 16:58:34 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 /* strdup() */ -#include -#include -#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 "asfpacket.h" #include "libasf.h" -#include "asf.h" +#include "assert.h" + +/* TODO + * - add support for the newly added object: language, bitrate, + * extended stream properties. + */ /***************************************************************************** - * Local prototypes + * Module descriptor *****************************************************************************/ -static int Activate ( vlc_object_t * ); -static void Deactivate ( vlc_object_t * ); -static int Demux ( input_thread_t * ); +static int Open ( vlc_object_t * ); +static void Close ( vlc_object_t * ); + +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 () + /***************************************************************************** - * Module descriptor + * Local prototypes *****************************************************************************/ -vlc_module_begin(); - set_description( "ASF v1.0 demuxer (file only)" ); - set_capability( "demux", 200 ); - set_callbacks( Activate, Deactivate ); - add_shortcut( "asf" ); -vlc_module_end(); - -static uint16_t GetWLE( uint8_t *p_buff ) +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 { - return( (p_buff[0]) + ( p_buff[1] <<8 ) ); -} -static uint32_t GetDWLE( uint8_t *p_buff ) + int i_cat; + + es_out_id_t *p_es; + es_format_t *p_fmt; /* format backup for video changes */ + bool b_selected; + + mtime_t i_time; /* track time*/ + + asf_track_info_t info; + +} asf_track_t; + +struct demux_sys_t { - return( p_buff[0] + ( p_buff[1] <<8 ) + - ( p_buff[2] <<16 ) + ( p_buff[3] <<24 ) ); -} + 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_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; + + 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 DemuxInit( demux_t * ); +static void DemuxEnd( demux_t * ); /***************************************************************************** - * Activate: check file and initializes ASF structures + * Open: check file and initializes ASF structures *****************************************************************************/ -static int Activate( vlc_object_t * p_this ) +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_t *p_demux = (demux_t *)p_this; + demux_sys_t *p_sys; + guid_t guid; + const uint8_t *p_peek; - demux_sys_t *p_demux; - int i_stream; + /* 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; - /* Initialize access plug-in structures. */ - if( p_input->i_mtu == 0 ) - { - /* Improve speed. */ - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE; - } + ASF_GetGUID( &guid, p_peek ); + if( !guidcmp( &guid, &asf_object_header_guid ) ) return VLC_EGENERIC; - p_input->pf_demux = Demux; + /* 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 ) ); - /* 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 v1.0 plugin discarded (cannot peek)" ); - return( -1 ); - } - GetGUID( &guid, p_peek ); - if( !CmpGUID( &guid, &asf_object_header_guid ) ) + /* Load the headers */ + if( DemuxInit( p_demux ) ) { - msg_Warn( p_input, "ASF v1.0 plugin discarded (not a valid file)" ); - return( -1 ); + free( p_sys ); + return VLC_EGENERIC; } - /* create our structure that will contains all data */ - if( !( p_input->p_demux_data = - p_demux = malloc( sizeof( demux_sys_t ) ) ) ) + 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; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Demux: read packet and send them to decoders + *****************************************************************************/ +static int Demux( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + for( int i=0; ii_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; + } } - memset( p_demux, 0, sizeof( demux_sys_t ) ); - p_demux->i_first_pts = -1; - /* Now load all object ( except raw data ) */ - if( !ASF_ReadObjectRoot( p_input, &p_demux->root, p_input->stream.b_seekable ) ) + /* Get selected tracks, especially for computing PCR */ + for( int i=0; itrack[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; } - /* Check if we have found all mandatory asf object */ - if( !p_demux->root.p_hdr || !p_demux->root.p_data ) + + for( ;; ) { - ASF_FreeObjectRoot( p_input, &p_demux->root ); - free( p_demux ); - msg_Warn( p_input, "ASF v1.0 plugin discarded (not a valid file)" ); - return( -1 ); + const uint8_t *p_peek; + mtime_t i_length; + mtime_t i_time_begin = GetMoviePTS( p_sys ); + int i_result; + + 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 + + /* Check if we have concatenated files */ + if( stream_Peek( p_demux->s, &p_peek, 16 ) == 16 ) + { + guid_t guid; + + ASF_GetGUID( &guid, p_peek ); + if( guidcmp( &guid, &asf_object_header_guid ) ) + { + msg_Warn( p_demux, "found a new ASF header" ); + /* We end this stream */ + DemuxEnd( p_demux ); + + /* 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; + } + } + + /* 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; + } + if( i_time_begin == -1 ) + { + i_time_begin = GetMoviePTS( p_sys ); + } + else + { + i_length = GetMoviePTS( p_sys ) - i_time_begin; + if( i_length < 0 || i_length >= 40 * 1000 ) break; + } } - if( !( p_demux->p_fp = ASF_FindObject( p_demux->root.p_hdr, - &asf_object_file_properties_guid, 0 ) ) ) + /* 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 ) { - ASF_FreeObjectRoot( p_input, &p_demux->root ); - free( p_demux ); - msg_Warn( p_input, "ASF v1.0 plugin discarded (missing file_properties object)" ); - return( -1 ); +#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 ); } - if( p_demux->p_fp->i_min_data_packet_size != p_demux->p_fp->i_max_data_packet_size ) + 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 ); +} + +/***************************************************************************** + * 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 ) { - ASF_FreeObjectRoot( p_input, &p_demux->root ); - free( p_demux ); - msg_Warn( p_input, "ASF v1.0 plugin discarded (invalid file_properties object)" ); - return( -1 ); + for ( int i=0; itrack[i]; + if ( tk && tk->info.p_sp && tk->i_cat == VIDEO_ES && tk->b_selected ) + { + p_sys->i_seek_track = tk->info.p_sp->i_stream_number; + break; + } + } } - p_demux->i_streams = ASF_CountObject( p_demux->root.p_hdr, - &asf_object_stream_properties_guid ); - if( !p_demux->i_streams ) + if ( p_sys->i_seek_track ) { - ASF_FreeObjectRoot( p_input, &p_demux->root ); - free( p_demux ); - msg_Warn( p_input, "ASF plugin discarded (cannot find any stream!)" ); - return( -1 ); + /* 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_sys->i_wait_keyframe = ( p_sys->b_canfastseek ) ? 25 * 30 : 25 * 5; + } } else { - input_info_category_t *p_cat = input_InfoCategory( p_input, "Asf" ); - msg_Dbg( p_input, "found %d streams", p_demux->i_streams ); - input_AddInfo( p_cat, "Number of streams", "%d" , p_demux->i_streams ); + p_sys->i_wait_keyframe = 0; } - /* create one program */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( input_InitStream( p_input, 0 ) == -1) +} + +/***************************************************************************** + * 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; + + WaitKeyframe( p_demux ); + + 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 ); +} + +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; + + msg_Dbg( p_demux, "seek with index: %i seconds, position %f", + i_date >= 0 ? (int)(i_date/1000000) : -1, f_pos ); + + if( i_date < 0 ) + i_date = p_sys->i_length * f_pos; + + 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; + + p_index = ASF_FindObject( p_sys->p_root, &asf_object_simple_index_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 ) { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot init stream" ); - return( -1 ); + msg_Warn( p_demux, "Incomplete index" ); + return VLC_EGENERIC; } - if( input_AddProgram( p_input, 0, 0) == NULL ) + + WaitKeyframe( p_demux ); + + 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 ( stream_Seek( p_demux->s, i_offset + p_sys->i_data_begin ) == VLC_SUCCESS ) { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot add program" ); - return( -1 ); + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, VLC_TS_0 + i_date ); + return VLC_SUCCESS; } - p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; - p_input->stream.i_mux_rate = 0 ; /* FIXME */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); + else return VLC_EGENERIC; +} - for( i_stream = 0; i_stream < p_demux->i_streams; i_stream ++ ) +static void SeekPrepare( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + 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_stream_t *p_stream; - asf_object_stream_properties_t *p_sp; - char psz_cat[sizeof("Stream ")+10]; - input_info_category_t *p_cat; - sprintf( psz_cat, "Stream %d", i_stream ); - p_cat = input_InfoCategory( p_input, psz_cat); + asf_track_t *tk = p_sys->track[i]; + if( !tk ) + continue; - p_sp = ASF_FindObject( p_demux->root.p_hdr, - &asf_object_stream_properties_guid, - i_stream ); + tk->i_time = -1; + if( tk->info.p_frame ) + block_ChainRelease( tk->info.p_frame ); + tk->info.p_frame = NULL; + } - p_stream = - p_demux->stream[p_sp->i_stream_number] = - malloc( sizeof( asf_stream_t ) ); - memset( p_stream, 0, sizeof( asf_stream_t ) ); - p_stream->p_sp = p_sp; + es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); +} - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_stream->p_es = - input_AddES( p_input, - p_input->stream.p_selected_program, - p_sp->i_stream_number, - 0 ); +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( demux_t *p_demux, int i_query, va_list args ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + vlc_meta_t *p_meta; + int64_t i64, *pi64; + int i; + double f, *pf; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - if( CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_audio ) ) - { - int i_codec; - if( p_sp->p_type_specific_data ) - { - i_codec = GetWLE( p_sp->p_type_specific_data ); - } - else - { - i_codec = -1; - } + switch( i_query ) + { + case DEMUX_GET_LENGTH: + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_length; + return VLC_SUCCESS; - p_stream->i_cat = AUDIO_ES; - input_AddInfo( p_cat, "Type", "Audio" ); - msg_Dbg( p_input, - "adding new audio stream(codec:0x%x,ID:%d)", - i_codec, - p_sp->i_stream_number ); - switch( i_codec ) - { - case( 0x01 ): - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'a', 'r', 'a', 'w' ); - break; - case( 0x50 ): - case( 0x55 ): - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'm','p','g','a' ); - break; - case( 0x2000 ): - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'a','5','2',' ' ); - break; - case( 0x160 ): - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'w','m','a','1' ); - break; - case( 0x161 ): - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'w','m','a','2' ); - break; - default: - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'm','s',(i_codec >> 8)&0xff,i_codec&0xff ); - } - input_AddInfo( p_cat, "Codec", "%.4s", (char*)&p_stream->p_es->i_fourcc ); - if( p_sp->i_type_specific_data_length > 0 ) - { - WAVEFORMATEX *p_wf; - size_t i_size; - uint8_t *p_data; - - i_size = p_sp->i_type_specific_data_length; - - p_wf = malloc( i_size ); - p_stream->p_es->p_waveformatex = (void*)p_wf; - p_data = p_sp->p_type_specific_data; - - p_wf->wFormatTag = GetWLE( p_data ); - p_wf->nChannels = GetWLE( p_data + 2 ); - input_AddInfo( p_cat, "Channels", "%d", p_wf->nChannels ); - p_wf->nSamplesPerSec = GetDWLE( p_data + 4 ); - input_AddInfo( p_cat, "Sample rate", "%d", p_wf->nSamplesPerSec ); - p_wf->nAvgBytesPerSec = GetDWLE( p_data + 8 ); - input_AddInfo( p_cat, "Avg. byterate", "%d", p_wf->nAvgBytesPerSec ); - p_wf->nBlockAlign = GetWLE( p_data + 12 ); - p_wf->wBitsPerSample = GetWLE( p_data + 14 ); - input_AddInfo( p_cat, "Bits Per Sample", "%d", p_wf->wBitsPerSample ); - p_wf->cbSize = __MAX( 0, - i_size - sizeof( WAVEFORMATEX )); - if( i_size > sizeof( WAVEFORMATEX ) ) - { - memcpy( (uint8_t*)p_wf + sizeof( WAVEFORMATEX ), - p_data + sizeof( WAVEFORMATEX ), - i_size - sizeof( WAVEFORMATEX ) ); - } - } + 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; + + 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 ) + { + 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; } - else - if( CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_video ) ) - { - p_stream->i_cat = VIDEO_ES; - input_AddInfo( p_cat, "Type", "Video" ); - msg_Dbg( p_input, - "adding new video stream(ID:%d)", - p_sp->i_stream_number ); - if( p_sp->p_type_specific_data ) - { - p_stream->p_es->i_fourcc = - VLC_FOURCC( p_sp->p_type_specific_data[27], - p_sp->p_type_specific_data[28], - p_sp->p_type_specific_data[29], - p_sp->p_type_specific_data[30] ); - } - else - { - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'u','n','d','f' ); - } - input_AddInfo( p_cat, "Codec", "%.4s", (char*)&p_stream->p_es->i_fourcc ); - if( p_sp->i_type_specific_data_length > 11 ) - { - BITMAPINFOHEADER *p_bih; - size_t i_size; - uint8_t *p_data; - - i_size = p_sp->i_type_specific_data_length - 11; - - p_bih = malloc( i_size ); - p_stream->p_es->p_bitmapinfoheader = (void*)p_bih; - p_data = p_sp->p_type_specific_data + 11; - - p_bih->biSize = GetDWLE( p_data ); - input_AddInfo( p_cat, "Size", "%d", p_bih->biSize ); - p_bih->biWidth = GetDWLE( p_data + 4 ); - p_bih->biHeight = GetDWLE( p_data + 8 ); - input_AddInfo( p_cat, "Resolution", "%dx%d", p_bih->biWidth, p_bih->biHeight ); - p_bih->biPlanes = GetDWLE( p_data + 12 ); - input_AddInfo( p_cat, "Planes", "%d", p_bih->biPlanes ); - p_bih->biBitCount = GetDWLE( p_data + 14 ); - input_AddInfo( p_cat, "Bits per pixel", "%d", p_bih->biBitCount ); - p_bih->biCompression= GetDWLE( p_data + 16 ); - input_AddInfo( p_cat, "Compression Rate", "%d", p_bih->biCompression ); - p_bih->biSizeImage = GetDWLE( p_data + 20 ); - input_AddInfo( p_cat, "Image Size", "%d", p_bih->biSizeImage ); - p_bih->biXPelsPerMeter = GetDWLE( p_data + 24 ); - input_AddInfo( p_cat, "X pixels per meter", "%d", p_bih->biXPelsPerMeter ); - p_bih->biYPelsPerMeter = GetDWLE( p_data + 28 ); - input_AddInfo( p_cat, "Y pixels per meter", "%d", p_bih->biYPelsPerMeter ); - p_bih->biClrUsed = GetDWLE( p_data + 32 ); - p_bih->biClrImportant = GetDWLE( p_data + 36 ); - - if( i_size > sizeof( BITMAPINFOHEADER ) ) - { - memcpy( (uint8_t*)p_bih + sizeof( BITMAPINFOHEADER ), - p_data + sizeof( BITMAPINFOHEADER ), - i_size - sizeof( BITMAPINFOHEADER ) ); - } - } + return SeekPercent( p_demux, i_query, args ); + case DEMUX_SET_ES: + { + i = (int)va_arg( args, int ); + int i_ret; + if ( i >= 0 ) + { + 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 - { - p_stream->i_cat = UNKNOWN_ES; - msg_Dbg( p_input, - "ignoring unknown stream(ID:%d)", - p_sp->i_stream_number ); - p_stream->p_es->i_fourcc = VLC_FOURCC( 'u','n','d','f' ); + { /* 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 ); } - p_stream->p_es->i_cat = p_stream->i_cat; - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( p_stream->p_es->i_fourcc != VLC_FOURCC( 'u','n','d','f' ) ) + if ( i_ret == VLC_SUCCESS ) { - input_SelectES( p_input, p_stream->p_es ); + SeekPrepare( p_demux ); + p_sys->i_seek_track = 0; + WaitKeyframe( p_demux ); } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - } - - - p_demux->i_data_begin = p_demux->root.p_data->i_object_pos + 50; - if( p_demux->root.p_data->i_object_size != 0 ) - { // local file - p_demux->i_data_end = p_demux->root.p_data->i_object_pos + - p_demux->root.p_data->i_object_size; - } - else - { // live/broacast - p_demux->i_data_end = -1; + return i_ret; } + case DEMUX_GET_POSITION: + if( p_sys->i_time < 0 ) return VLC_EGENERIC; + if( p_sys->i_length > 0 ) + { + 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 ); - // go to first packet - ASF_SeekAbsolute( p_input, p_demux->i_data_begin ); + case DEMUX_SET_POSITION: + if ( p_sys->p_fp && + ! ( p_sys->p_fp->i_flags & ASF_FILE_PROPERTIES_SEEKABLE ) ) + return VLC_EGENERIC; - vlc_mutex_lock( &p_input->stream.stream_lock ); - /* try to calculate movie time */ - if( p_demux->p_fp->i_data_packets_count > 0 ) - { - int64_t i_count; - mtime_t i_length; + SeekPrepare( p_demux ); - /* real number of packets */ - i_count = ( p_input->stream.p_selected_area->i_size - - p_demux->i_data_begin ) / - p_demux->p_fp->i_min_data_packet_size; - /* calculate the time duration in s */ - i_length = (mtime_t)p_demux->p_fp->i_play_duration / 10 * - (mtime_t)i_count / - (mtime_t)p_demux->p_fp->i_data_packets_count / - (mtime_t)1000000; - if( i_length > 0 ) + if( p_sys->b_index && p_sys->i_length > 0 ) { - p_input->stream.i_mux_rate = - p_input->stream.p_selected_area->i_size / 50 / i_length; + va_list acpy; + va_copy( acpy, args ); + f = (double)va_arg( acpy, double ); + va_end( acpy ); + + if( !SeekIndex( p_demux, -1, f ) ) + return VLC_SUCCESS; } - else + 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 ) ) { - p_input->stream.i_mux_rate = 0; + bool *pb_bool = (bool*)va_arg( args, bool * ); + *pb_bool = false; + return VLC_SUCCESS; } - + // 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 ); } - else +} + +/***************************************************************************** + * + *****************************************************************************/ +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++ ) { - /* cannot known */ - p_input->stream.i_mux_rate = 0; + const asf_track_t *tk = p_sys->track[i]; + + if( tk && tk->p_es && tk->b_selected ) + { + /* 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 ) + { + /* early fail */ + return -1; + } + else if ( tk->i_time > -1 && ( i_time == -1 || i_time > tk->i_time ) ) + { + i_time = tk->i_time; + } + } } + 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; - p_input->stream.p_selected_program->b_is_ok = 1; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + /* 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 ) + { + 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; +} +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; +} - return( 0 ); +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; } -/***************************************************************************** - * Demux: read packet and send them to decoders - *****************************************************************************/ -#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 bool Packet_DoSkip( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, bool b_packet_keyframe ) +{ + 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 == NULL ) + { + msg_Warn( p_demux, "undeclared stream[Id 0x%x]", i_stream_number ); + return true; } -static int DemuxPacket( input_thread_t *p_input, vlc_bool_t b_play_audio ) -{ - demux_sys_t *p_demux = p_input->p_demux_data; - int i_data_packet_min = p_demux->p_fp->i_min_data_packet_size; - uint8_t *p_peek; - int i_skip; + if( p_sys->i_wait_keyframe ) + { + if ( i_stream_number == p_sys->i_seek_track ) + { + if ( !b_packet_keyframe ) + { + p_sys->i_wait_keyframe--; + return true; + } + else + p_sys->i_wait_keyframe = 0; + } + else + return true; + } - int i_packet_size_left; - int i_packet_flags; - int i_packet_property; + if( !tk->p_es ) + return true; - int b_packet_multiple_payload; - int i_packet_length; - int i_packet_sequence; - int i_packet_padding_length; + return false; +} - uint32_t i_packet_send_time; - uint16_t i_packet_duration; - int i_payload; - int i_payload_count; - int i_payload_length_type; +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( input_Peek( p_input, &p_peek, i_data_packet_min ) < i_data_packet_min ) + if( p_sys->i_time < VLC_TS_0 && tk->i_time > VLC_TS_INVALID ) { - // EOF ? - msg_Warn( p_input, "cannot peek while getting new packet, EOF ?" ); - return( 0 ); + 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 } - i_skip = 0; - /* *** parse error correction if present *** */ - if( p_peek[0]&0x80 ) - { - int i_error_correction_length_type; - int i_error_correction_data_length; - int i_opaque_data_present; +#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; +} + +/***************************************************************************** + * + *****************************************************************************/ +typedef struct asf_es_priorities_t +{ + uint16_t *pi_stream_numbers; + uint16_t i_count; +} asf_es_priorities_t; - 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 +/* 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( i_error_correction_length_type != 0x00 || - i_opaque_data_present != 0 || - i_error_correction_data_length != 0x02 ) + if ( p_mutex->i_stream_number_count ) + { + /* Just set highest prio on highest in the group */ + for ( uint16_t i = 1; i < p_mutex->i_stream_number_count; i++ ) { - goto loop_error_recovery; + 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 ]; } - - i_skip += i_error_correction_data_length; } - else +} + +/* 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 ) +{ + /* 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; + + if ( p_bitrate_mutex->i_stream_number_count ) { - msg_Warn( p_input, "p_peek[0]&0x80 != 0x80" ); + /* 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 ]; + } } - 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; +} - /* 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 ); +#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;\ + }\ +} - i_packet_send_time = GetDWLE( p_peek + i_skip ); i_skip += 4; - i_packet_duration = GetWLE( p_peek + i_skip ); i_skip += 2; +static int DemuxInit( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + /* init context */ + p_sys->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++ ) + { + p_sys->track[i] = NULL; + } + p_sys->i_data_begin = 0; + p_sys->i_data_end = 0; + p_sys->i_preroll_start = 0; + p_sys->meta = NULL; -// 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; + /* 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)) ) + { + msg_Warn( p_demux, "ASF plugin discarded (not a valid file)" ); + return VLC_EGENERIC; + } + p_sys->p_fp = p_sys->p_root->p_fp; - if( b_packet_multiple_payload ) + if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) { - i_payload_count = p_peek[i_skip] & 0x3f; - i_payload_length_type = ( p_peek[i_skip] >> 6 )&0x03; - i_skip++; + msg_Warn( p_demux, "ASF plugin discarded (invalid file_properties object)" ); + goto error; } - else + + 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 ) { - i_payload_count = 1; - i_payload_length_type = 0x02; // unused + dialog_Fatal( p_demux, _("Could not demux ASF stream"), "%s", + _("DRM protected streams are not supported.") ); + goto error; } - for( i_payload = 0; i_payload < i_payload_count ; i_payload++ ) + p_sys->i_track = ASF_CountObject( p_sys->p_root->p_hdr, + &asf_object_stream_properties_guid ); + if( p_sys->i_track == 0 ) { - asf_stream_t *p_stream; + msg_Warn( p_demux, "ASF plugin discarded (cannot find any stream!)" ); + goto error; + } + msg_Dbg( p_demux, "found %u streams", p_sys->i_track ); - 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; + /* 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; - mtime_t i_pts; - mtime_t i_pts_delta; + /* 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 ); - if( i_skip >= i_packet_size_left ) - { - /* prevent some segfault with invalid file */ - break; - } + 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 }; - i_stream_number = p_peek[i_skip] & 0x7f; - i_skip++; + if( p_hdr_ext ) + { + p_languages = ASF_FindObject( p_hdr_ext, &asf_object_language_list, 0 ); - 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 ); + 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 ); + } - if( i_replicated_data_length > 1 ) // should be at least 8 bytes - { - i_pts = (mtime_t)GetDWLE( p_peek + i_skip + 4 ) * 1000; - i_skip += i_replicated_data_length; - i_pts_delta = 0; + for( unsigned i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + asf_track_t *tk; + asf_object_stream_properties_t *p_sp; + asf_object_extended_stream_properties_t *p_esp; + bool b_access_selected; - i_media_object_offset = i_tmp; - } - else if( i_replicated_data_length == 1 ) - { + p_sp = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_stream_properties_guid, + i_stream ); + p_esp = NULL; - msg_Dbg( p_input, "found compressed payload" ); + tk = p_sys->track[p_sp->i_stream_number] = malloc( sizeof( asf_track_t ) ); + memset( tk, 0, sizeof( asf_track_t ) ); - i_pts = (mtime_t)i_tmp * 1000; - i_pts_delta = (mtime_t)p_peek[i_skip] * 1000; i_skip++; + tk->i_time = -1; + tk->info.p_sp = p_sp; + tk->p_es = NULL; + tk->info.p_esp = NULL; + tk->info.p_frame = NULL; - i_media_object_offset = 0; - } - else + if ( strncmp( p_demux->psz_access, "mms", 3 ) ) { - i_pts = (mtime_t)i_packet_send_time * 1000; - i_pts_delta = 0; - - i_media_object_offset = i_tmp; + /* 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_pts = __MAX( i_pts - p_demux->p_fp->i_preroll * 1000, 0 ); - if( b_packet_multiple_payload ) - { - GETVALUE2b( i_payload_length_type, i_payload_data_length, 0 ); - } - else + /* Find the associated extended_stream_properties if any */ + if( p_hdr_ext ) { - i_payload_data_length = i_packet_length - - i_packet_padding_length - i_skip; + 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; + } + } } -#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 + es_format_t fmt; - if( !( p_stream = p_demux->stream[i_stream_number] ) ) + if( guidcmp( &p_sp->i_stream_type, &asf_object_stream_type_audio ) && + p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 ) { - msg_Warn( p_input, - "undeclared stream[Id 0x%x]", i_stream_number ); - i_skip += i_payload_data_length; - continue; // over payload - } + uint8_t *p_data = p_sp->p_type_specific_data; + int i_format; + + 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( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) && + i_format != WAVE_FORMAT_MPEGLAYER3 && + i_format != WAVE_FORMAT_MPEG ) + { + 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 ); + } - if( !p_stream->p_es || !p_stream->p_es->p_decoder_fifo ) - { - i_skip += i_payload_data_length; - continue; + msg_Dbg( p_demux, "added new audio stream(codec:0x%x,ID:%d)", + GetWLE( p_data ), p_sp->i_stream_number ); } + 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]; + es_format_Init( &fmt, VIDEO_ES, + VLC_FOURCC( p_data[16], p_data[17], + p_data[18], p_data[19] ) ); - 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 ) - { - data_packet_t *p_data; - int i_read; - // read sub payload length - if( i_replicated_data_length == 1 ) + 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 ); + + if( p_esp && p_esp->i_average_time_per_frame > 0 ) { - i_sub_payload_data_length = p_peek[i_skip]; i_skip++; - i_payload_data_pos++; + 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 ); } - else + + 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; + } + + if( p_sp->i_type_specific_data_length > 11 + + sizeof( VLC_BITMAPINFOHEADER ) ) { - i_sub_payload_data_length = i_payload_data_length; + 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 ); } - /* FIXME I don't use i_media_object_number, sould I ? */ - if( p_stream->p_pes && i_media_object_offset == 0 ) { - /* send complete packet to decoder */ - if( p_stream->p_pes->i_pes_size > 0 ) + /* 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( p_stream->p_es->p_decoder_fifo && - ( b_play_audio || p_stream->i_cat != AUDIO_ES ) ) + if( !strcmp( p_meta->record[i].psz_name, "AspectRatioX" ) ) { - input_DecodePES( p_stream->p_es->p_decoder_fifo, - p_stream->p_pes ); + 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 ); } - else + if( !strcmp( p_meta->record[i].psz_name, "AspectRatioY" ) ) { - input_DeletePES( p_input->p_method_data, - p_stream->p_pes ); + 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 ); } - p_stream->p_pes = NULL; + } + + 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_pes ) // add a new PES - { - p_stream->i_time = - ( (mtime_t)i_pts + i_payload * (mtime_t)i_pts_delta ); + /* If there is a video track then use the index for seeking */ + p_sys->b_index = b_index; - if( p_demux->i_first_pts == -1 ) + msg_Dbg( p_demux, "added new video stream(ID:%d)", + p_sp->i_stream_number ); + } + else if( guidcmp( &p_sp->i_stream_type, &asf_object_extended_stream_header ) && + p_sp->i_type_specific_data_length >= 64 ) + { + /* 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) + { + 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 ) { - p_demux->i_first_pts = p_stream->i_time; + 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 ); } - p_stream->i_time -= p_demux->i_first_pts; - - 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+DEFAULT_PTS_DELAY) * 9 /100 ); - - p_stream->p_pes->p_next = NULL; - p_stream->p_pes->i_nb_data = 0; - p_stream->p_pes->i_pes_size = 0; - } - i_read = i_sub_payload_data_length + i_skip; - if( input_SplitBuffer( p_input, &p_data, i_read ) < i_read ) + msg_Dbg( p_demux, "added new audio stream (codec:0x%x,ID:%d)", + i_format, p_sp->i_stream_number ); + } + else { - msg_Warn( p_input, "cannot read data" ); - return( 0 ); + es_format_Init( &fmt, UNKNOWN_ES, 0 ); } - p_data->p_payload_start += i_skip; - i_packet_size_left -= i_read; + } + 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'; + } - if( !p_stream->p_pes->p_first ) + /* 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++ ) + { + if ( fmt_priorities_ex.pi_stream_numbers[i] == p_sp->i_stream_number ) + { + i_priority = ES_PRIORITY_NOT_DEFAULTABLE; + break; + } + } + for( uint16_t i = 0; i < fmt_priorities_bitrate_ex.i_count; i++ ) { - p_stream->p_pes->p_first = p_stream->p_pes->p_last = p_data; + 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 ); + + if ( fmt.i_cat == VIDEO_ES ) { - p_stream->p_pes->p_last->p_next = p_data; - p_stream->p_pes->p_last = p_data; + /* Backup our video format */ + tk->p_fmt = malloc( sizeof( es_format_t ) ); + if ( tk->p_fmt ) + es_format_Copy( tk->p_fmt, &fmt ); } - 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 ) + tk->p_es = es_out_Add( p_demux->out, &fmt ); + + if( !stream_Control( p_demux->s, STREAM_GET_PRIVATE_ID_STATE, + (int) p_sp->i_stream_number, &b_access_selected ) && + b_access_selected ) { - if( input_Peek( p_input, &p_peek, i_packet_size_left ) < i_packet_size_left ) - { - // EOF ? - msg_Warn( p_input, "cannot peek, EOF ?" ); - return( 0 ); - } + p_sys->i_access_selected_track[fmt.i_cat] = p_sp->i_stream_number; } - } - } - if( i_packet_size_left > 0 ) - { - if( !ASF_SkipBytes( p_input, i_packet_size_left ) ) + } + else { - msg_Warn( p_input, "cannot skip data, EOF ?" ); - return( 0 ); + msg_Dbg( p_demux, "ignoring unknown stream(ID:%d)", + p_sp->i_stream_number ); } + + es_format_Clean( &fmt ); } - return( 1 ); + free( fmt_priorities_ex.pi_stream_numbers ); + free( fmt_priorities_bitrate_ex.pi_stream_numbers ); -loop_error_recovery: - msg_Warn( p_input, "unsupported packet header" ); - if( p_demux->p_fp->i_min_data_packet_size != p_demux->p_fp->i_max_data_packet_size ) - { - msg_Err( p_input, "unsupported packet header, fatal error" ); - return( -1 ); + 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; } - ASF_SkipBytes( p_input, i_data_packet_min ); - - return( 1 ); -} -static int Demux( input_thread_t *p_input ) -{ - demux_sys_t *p_demux = p_input->p_demux_data; - vlc_bool_t b_play_audio; - int i; + /* go to first packet */ + stream_Seek( p_demux->s, p_sys->i_data_begin ); - /* catch seek from user */ - if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT ) + /* try to calculate movie time */ + if( p_sys->p_fp->i_data_packets_count > 0 ) { - off_t i_offset; - msleep( DEFAULT_PTS_DELAY ); - i_offset = ASF_TellAbsolute( p_input ) - p_demux->i_data_begin; + uint64_t i_count; + uint64_t i_size = stream_Size( p_demux->s ); - if( i_offset < 0 ) + if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end ) { - i_offset = 0; + i_size = p_sys->i_data_end; } - /* XXX work only when i_min_data_packet_size == i_max_data_packet_size */ - i_offset += p_demux->p_fp->i_min_data_packet_size - - i_offset % p_demux->p_fp->i_min_data_packet_size; - ASF_SeekAbsolute( p_input, p_demux->i_data_begin + i_offset ); - p_demux->i_time = 0; - for( i = 0; i < 128 ; i++ ) - { -#define p_stream p_demux->stream[i] - if( p_stream ) - { - p_stream->i_time = 0; - } -#undef p_stream - } - p_demux->i_first_pts = -1; - } + /* real number of packets */ + i_count = ( i_size - p_sys->i_data_begin ) / + p_sys->p_fp->i_min_data_packet_size; - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( p_input->stream.control.i_rate == DEFAULT_RATE ) - { - b_play_audio = VLC_TRUE; - } - else - { - int i; + /* 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; - b_play_audio = VLC_TRUE; - for( i = 0; i < 128; i++ ) + if( p_sys->i_length > 0 ) { - if( p_demux->stream[i] && - p_demux->stream[i]->i_cat == VIDEO_ES && - p_demux->stream[i]->p_es && - p_demux->stream[i]->p_es->p_decoder_fifo ) - { - /* there is at least ine video track so no need to play audio */ - b_play_audio = VLC_FALSE; - } + p_sys->i_bitrate = 8 * i_size * 1000000 / p_sys->i_length; } } - vlc_mutex_unlock( &p_input->stream.stream_lock ); + /* Create meta information */ + p_sys->meta = vlc_meta_New(); - for( i = 0; i < 10; i++ ) // parse 10 packets + asf_object_content_description_t *p_cd; + if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_content_description_guid, 0 ) ) ) { - int i_result; - - if( ( i_result = DemuxPacket( p_input, b_play_audio ) ) <= 0 ) + if( p_cd->psz_title && *p_cd->psz_title ) { - return i_result; + 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 ); } } - - p_demux->i_time = -1; - for( i = 0; i < 128 ; i++ ) + /// \tood Fix Child meta for ASF tracks +#if 0 + for( i_stream = 0, i = 0; i < MAX_ASF_TRACKS; i++ ) { -#define p_stream p_demux->stream[i] - if( p_stream && p_stream->p_es && p_stream->p_es->p_decoder_fifo ) + 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] ) { - if( p_demux->i_time < 0 ) - { - p_demux->i_time = p_stream->i_time; - } - else + 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 ) { - p_demux->i_time = __MIN( p_demux->i_time, p_stream->i_time ); + 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 ) + { + vlc_meta_Add( tk, VLC_META_CODEC_DESCRIPTION, + p_cl->codec[i_stream].psz_description ); + } } + i_stream++; } -#undef p_stream } +#endif - if( p_demux->i_time >= 0 ) - { - /* update pcr XXX in mpeg scale so in 90000 unit/s */ - p_demux->i_pcr =( __MAX( p_demux->i_time /*- DEFAULT_PTS_DELAY*/, 0 ) ) * 9 / 100; + p_sys->packet_sys.pi_preroll = &p_sys->p_fp->i_preroll; + p_sys->packet_sys.pi_preroll_start = &p_sys->i_preroll_start; - /* first wait for the good time to read next packets */ - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_demux->i_pcr ); - } + return VLC_SUCCESS; + +error: + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + return VLC_EGENERIC; +} +/***************************************************************************** + * FlushRemainingPackets: flushes tail packets + *****************************************************************************/ - return( 1 ); +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++ ) + { + 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 ); + } } /***************************************************************************** - * MP4End: frees unused data + * *****************************************************************************/ -static void Deactivate( vlc_object_t * p_this ) +static void DemuxEnd( demux_t *p_demux ) { -#define FREE( p ) \ - if( p ) { free( p ); } + demux_sys_t *p_sys = p_demux->p_sys; - input_thread_t * p_input = (input_thread_t *)p_this; - demux_sys_t *p_demux = p_input->p_demux_data; - int i_stream; + if( p_sys->p_root ) + { + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + p_sys->p_root = NULL; + } + if( p_sys->meta ) + { + vlc_meta_Delete( p_sys->meta ); + p_sys->meta = NULL; + } - msg_Dbg( p_input, "Freeing all memory" ); - ASF_FreeObjectRoot( p_input, &p_demux->root ); - for( i_stream = 0; i_stream < 128; i_stream++ ) + for( int i = 0; i < MAX_ASF_TRACKS; i++ ) { -#define p_stream p_demux->stream[i_stream] - if( p_stream ) + asf_track_t *tk = p_sys->track[i]; + + if( tk ) { - if( p_stream->p_pes ) + 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 ) { - input_DeletePES( p_input->p_method_data, p_stream->p_pes ); + es_format_Clean( tk->p_fmt ); + free( tk->p_fmt ); } - free( p_stream ); + free( tk ); } -#undef p_stream + p_sys->track[i] = 0; } - FREE( p_input->p_demux_data ); -#undef FREE }