X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fasf%2Fasf.c;h=c315a0486f90c0f9db6e063c917040f9be6f13c2;hb=0ade81b0ebaa94d8529e4784b460bc921be70f54;hp=b19fbf2e79be214c5355f48cb10782ac0e4bd8b7;hpb=74e8f4f6ac00d56ac3cbc2412fb19fe489ac5218;p=vlc diff --git a/modules/demux/asf/asf.c b/modules/demux/asf/asf.c index b19fbf2e79..c315a0486f 100644 --- a/modules/demux/asf/asf.c +++ b/modules/demux/asf/asf.c @@ -1,15 +1,16 @@ /***************************************************************************** - * asf.c : ASFv01 file input module for vlc + * asf.c : ASF demux module ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: asf.c,v 1.4 2002/11/10 16:31:20 fenrir Exp $ + * Copyright (C) 2002-2003 the VideoLAN team + * $Id$ + * * 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 * (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 @@ -17,301 +18,269 @@ * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include /* malloc(), free() */ -#include /* strdup() */ -#include -#include #include #include +#include "vlc_meta.h" + +#include "codecs.h" /* BITMAPINFOHEADER, WAVEFORMATEX */ #include "libasf.h" -#include "asf.h" -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Activate ( vlc_object_t * ); -static void Deactivate ( vlc_object_t * ); -static int Demux ( input_thread_t * ); +/* TODO + * - add support for the newly added object: language, bitrate, + * extended stream properties. + */ /***************************************************************************** * Module descriptor *****************************************************************************/ +static int Open ( vlc_object_t * ); +static void Close ( vlc_object_t * ); + vlc_module_begin(); - set_description( "ASF v1.0 demuxer (file only)" ); - set_capability( "demux", 200 ); - set_callbacks( Activate, Deactivate ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_DEMUX ); + set_description( _("ASF v1.0 demuxer") ); + set_capability( "demux2", 200 ); + set_callbacks( Open, Close ); add_shortcut( "asf" ); vlc_module_end(); -static u16 GetWLE( u8 *p_buff ) + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Demux ( demux_t * ); +static int Control( demux_t *, int i_query, va_list args ); + +typedef struct { - return( (p_buff[0]) + ( p_buff[1] <<8 ) ); -} -static u32 GetDWLE( u8 *p_buff ) -{ - return( p_buff[0] + ( p_buff[1] <<8 ) + - ( p_buff[2] <<16 ) + ( p_buff[3] <<24 ) ); -} + int i_cat; + + es_out_id_t *p_es; + + asf_object_stream_properties_t *p_sp; + + mtime_t i_time; + + block_t *p_frame; /* use to gather complete frame */ + +} asf_track_t; + +struct demux_sys_t +{ + mtime_t i_time; /* s */ + mtime_t i_length; /* length of file file */ + int64_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[128]; + + int64_t i_data_begin; + int64_t i_data_end; + + vlc_meta_t *meta; +}; + +static mtime_t GetMoviePTS( demux_sys_t * ); +static int DemuxInit( demux_t * ); +static void DemuxEnd( demux_t * ); +static int DemuxPacket( demux_t * ); /***************************************************************************** - * Activate: check file and initializes ASF structures + * Open: check file and initializes ASF structures *****************************************************************************/ -static int Activate( vlc_object_t * p_this ) -{ - input_thread_t *p_input = (input_thread_t *)p_this; - u8 *p_peek; - guid_t guid; - - demux_sys_t *p_demux; - int i_stream; - - /* Initialize access plug-in structures. */ - if( p_input->i_mtu == 0 ) - { - /* Improve speed. */ - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE ; - } +static int Open( vlc_object_t * p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + demux_sys_t *p_sys; + guid_t guid; + uint8_t *p_peek; - p_input->pf_demux = Demux; + /* 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 v1.0 plugin discarded (cannot peek)" ); - return( -1 ); - } - GetGUID( &guid, p_peek ); - if( !CmpGUID( &guid, &asf_object_header_guid ) ) - { - msg_Warn( p_input, "ASF v1.0 plugin discarded (not a valid file)" ); - return( -1 ); - } + ASF_GetGUID( &guid, p_peek ); + if( !ASF_CmpGUID( &guid, &asf_object_header_guid ) ) return VLC_EGENERIC; - /* create our structure that will contains all data */ - if( !( p_input->p_demux_data = - p_demux = malloc( sizeof( demux_sys_t ) ) ) ) - { - msg_Err( p_input, "out of memory" ); - return( -1 ); - } - memset( p_demux, 0, sizeof( demux_sys_t ) ); + /* Set p_demux fields */ + p_demux->pf_demux = Demux; + p_demux->pf_control = Control; + p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); + memset( p_sys, 0, sizeof( demux_sys_t ) ); - /* Now load all object ( except raw data ) */ - if( !ASF_ReadObjectRoot( p_input, &p_demux->root, p_input->stream.b_seekable ) ) - { - msg_Warn( p_input, "ASF v1.0 plugin discarded (not a valid file)" ); - free( p_demux ); - return( -1 ); - } - /* Check if we have found all mandatory asf object */ - if( !p_demux->root.p_hdr || !p_demux->root.p_data ) - { - 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 ); - } - - if( !( p_demux->p_fp = ASF_FindObject( p_demux->root.p_hdr, - &asf_object_file_properties_guid, 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 ); - } - - if( p_demux->p_fp->i_min_data_packet_size != p_demux->p_fp->i_max_data_packet_size ) - { - 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 ); - } - - p_demux->i_streams = ASF_CountObject( p_demux->root.p_hdr, - &asf_object_stream_properties_guid ); - if( !p_demux->i_streams ) + /* Load the headers */ + if( DemuxInit( p_demux ) ) { - ASF_FreeObjectRoot( p_input, &p_demux->root ); - free( p_demux ); - msg_Warn( p_input, "ASF plugin discarded (cannot find any stream!)" ); - return( -1 ); - } - else - { - msg_Dbg( p_input, "found %d streams", p_demux->i_streams ); + return VLC_EGENERIC; } + return VLC_SUCCESS; +} - /* create one program */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( input_InitStream( p_input, 0 ) == -1) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot init stream" ); - return( -1 ); - } - if( input_AddProgram( p_input, 0, 0) == NULL ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "cannot add program" ); - return( -1 ); - } - 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 ); - for( i_stream = 0; i_stream < p_demux->i_streams; i_stream ++ ) +/***************************************************************************** + * 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( ;; ) { - asf_stream_t *p_stream; - asf_object_stream_properties_t *p_sp; - - p_sp = ASF_FindObject( p_demux->root.p_hdr, - &asf_object_stream_properties_guid, - i_stream ); + uint8_t *p_peek; + mtime_t i_length; + mtime_t i_time_begin = GetMoviePTS( p_sys ); + int i_result; - 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; - - 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 ); - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - if( CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_audio ) ) + if( p_demux->b_die ) { - int i_codec; - if( p_sp->p_type_specific_data ) - { - i_codec = GetWLE( p_sp->p_type_specific_data ); - } - else + break; + } + + /* 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( ASF_CmpGUID( &guid, &asf_object_header_guid ) ) { - i_codec = -1; + 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" ); + return 0; + } + continue; } + } - p_stream->i_cat = AUDIO_ES; - msg_Dbg( p_input, - "adding new audio stream(codec:0x%x,ID:%d)", - i_codec, - p_sp->i_stream_number ); - switch( i_codec ) + /* Read and demux a packet */ + if( ( i_result = DemuxPacket( p_demux ) ) <= 0 ) + { + 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 ) { - 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( 'u','n','d','f' ); + break; } - if( p_sp->i_type_specific_data_length > 0 ) + } + } + + /* Set the PCR */ + p_sys->i_time = GetMoviePTS( p_sys ); + if( p_sys->i_time >= 0 ) + { + es_out_Control( p_demux->out, ES_OUT_SET_PCR, 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 ); +} + +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( demux_t *p_demux, int i_query, va_list args ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + int64_t *pi64; + int i; + vlc_meta_t **pp_meta; + + switch( i_query ) + { + case DEMUX_SET_TIME: + return VLC_EGENERIC; + + case DEMUX_GET_LENGTH: + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_length; + return VLC_SUCCESS; + + case DEMUX_GET_META: + pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** ); + *pp_meta = vlc_meta_Duplicate( p_sys->meta ); + return VLC_SUCCESS; + + case DEMUX_SET_POSITION: + p_sys->i_time = -1; + for( i = 0; i < 128 ; i++ ) { - p_stream->p_es->p_demux_data = - malloc( p_sp->i_type_specific_data_length ); - memcpy( p_stream->p_es->p_demux_data, - p_sp->p_type_specific_data, - p_sp->i_type_specific_data_length ); + asf_track_t *tk = p_sys->track[i]; + if( tk ) tk->i_time = -1; } - - } - else - if( CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_video ) ) + + default: + return demux2_vaControlHelper( p_demux->s, + p_sys->i_data_begin, p_sys->i_data_end, + p_sys->i_bitrate, p_sys->p_fp->i_min_data_packet_size, + i_query, args ); + } +} + +/***************************************************************************** + * + *****************************************************************************/ +static mtime_t GetMoviePTS( demux_sys_t *p_sys ) +{ + mtime_t i_time; + int i; + + i_time = -1; + for( i = 0; i < 128 ; i++ ) + { + asf_track_t *tk = p_sys->track[i]; + + if( tk && tk->p_es && tk->i_time > 0) { - p_stream->i_cat = VIDEO_ES; - msg_Dbg( p_input, - "adding new video stream(ID:%d)", - p_sp->i_stream_number ); - if( p_sp->p_type_specific_data ) + if( i_time < 0 ) { - p_stream->p_es->i_fourcc = - GetDWLE( p_sp->p_type_specific_data + 27 ); + i_time = tk->i_time; } else { - p_stream->p_es->i_fourcc = - VLC_FOURCC( 'u','n','d','f' ); + i_time = __MIN( i_time, tk->i_time ); } - if( p_sp->i_type_specific_data_length > 11 ) - { - p_stream->p_es->p_demux_data = - malloc( p_sp->i_type_specific_data_length - 11); - memcpy( p_stream->p_es->p_demux_data, - p_sp->p_type_specific_data + 11, - p_sp->i_type_specific_data_length - 11 ); - } - } - 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' ); - } - p_stream->p_es->i_cat = p_stream->i_cat; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - input_SelectES( p_input, p_stream->p_es ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - } - - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.p_selected_program->b_is_ok = 1; - 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; - } - - // go to first packet - ASF_SeekAbsolute( p_input, p_demux->i_data_begin ); - return( 0 ); + return i_time; } -/***************************************************************************** - * Demux: read packet and send them to decoders - *****************************************************************************/ #define GETVALUE2b( bits, var, def ) \ switch( (bits)&0x03 ) \ { \ @@ -322,361 +291,618 @@ static int Activate( vlc_object_t * p_this ) default: var = def; break;\ } -static int Demux( input_thread_t *p_input ) +static int DemuxPacket( demux_t *p_demux ) { - demux_sys_t *p_demux = p_input->p_demux_data; - int i; + demux_sys_t *p_sys = p_demux->p_sys; + int i_data_packet_min = p_sys->p_fp->i_min_data_packet_size; + uint8_t *p_peek; + int i_skip; - /* catch seek from user */ - if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT ) + int i_packet_size_left; + int i_packet_flags; + int i_packet_property; + + 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; + + + if( stream_Peek( p_demux->s, &p_peek,i_data_packet_min)i_data_begin; - - if( i_offset < 0 ) - { - i_offset = 0; - } - /* XXX work only when i_min_data_packet_size == i_max_data_packet_size */ - i_offset -= i_offset % p_demux->p_fp->i_min_data_packet_size; - ASF_SeekAbsolute( p_input, p_demux->i_data_begin + i_offset ); + msg_Warn( p_demux, "cannot peek while getting new packet, EOF ?" ); + return 0; + } + i_skip = 0; - p_demux->i_time = 0; - for( i = 0; i < 128 ; i++ ) + /* *** parse error correction if present *** */ + if( p_peek[0]&0x80 ) + { + 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 ) { -#define p_stream p_demux->stream[i] - if( p_stream ) - { - p_stream->i_time = 0; - } -#undef p_stream + goto loop_error_recovery; } + i_skip += i_error_correction_data_length; + } + else + { + msg_Warn( p_demux, "p_peek[0]&0x80 != 0x80" ); } - /* first wait for the good time to read a packet */ - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_demux->i_pcr ); + /* sanity check */ + if( i_skip + 2 >= i_data_packet_min ) + { + goto loop_error_recovery; + } - /* update pcr XXX in mpeg scale so in 90000 unit/s */ - p_demux->i_pcr = p_demux->i_time * 9 / 100; + i_packet_flags = p_peek[i_skip]; i_skip++; + i_packet_property = p_peek[i_skip]; i_skip++; - for( i = 0; i < 10; i++ ) // parse 10 packets + 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 ); + + i_packet_send_time = GetDWLE( p_peek + i_skip ); i_skip += 4; + i_packet_duration = GetWLE( p_peek + i_skip ); i_skip += 2; + +// i_packet_size_left = i_packet_length; // XXX data really read + /* FIXME I have to do that for some file, I don't known why */ + i_packet_size_left = i_data_packet_min; + + 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 + { + i_payload_count = 1; + i_payload_length_type = 0x02; // unused + } + + for( i_payload = 0; i_payload < i_payload_count ; i_payload++ ) { - int i_data_packet_min = p_demux->p_fp->i_min_data_packet_size; - u8 *p_peek; - int i_skip; - - int i_packet_size_left; - int i_packet_flags; - int i_packet_property; - - int b_packet_multiple_payload; - int i_packet_length; - int i_packet_sequence; - int i_packet_padding_length; - - u32 i_packet_send_time; - u16 i_packet_duration; - int i_payload; - int i_payload_count; - int i_payload_length_type; - - - if( input_Peek( p_input, &p_peek, i_data_packet_min ) < i_data_packet_min ) + asf_track_t *tk; + + 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; + + mtime_t i_pts; + mtime_t i_pts_delta; + + if( i_skip >= i_packet_size_left ) { - // EOF ? - msg_Err( p_input, "cannot peek while getting new packet, EOF ?" ); - return( 0 ); + /* prevent some segfault with invalid file */ + break; } - i_skip = 0; - - /* *** parse error correction if present *** */ - if( p_peek[0]&0x80 ) + + i_stream_number = p_peek[i_skip] & 0x7f; + i_skip++; + + 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 ); + + if( i_replicated_data_length > 1 ) // should be at least 8 bytes { - int i_error_correction_length_type; - int i_error_correction_data_length; - 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 ) + i_pts = (mtime_t)GetDWLE( p_peek + i_skip + 4 ) * 1000; + i_skip += i_replicated_data_length; + i_pts_delta = 0; + + i_media_object_offset = i_tmp; + + if( i_skip >= i_packet_size_left ) { - goto loop_error_recovery; + break; } + } + else if( i_replicated_data_length == 1 ) + { + /* msg_Dbg( p_demux, "found compressed payload" ); */ + + i_pts = (mtime_t)i_tmp * 1000; + i_pts_delta = (mtime_t)p_peek[i_skip] * 1000; i_skip++; - i_skip += i_error_correction_data_length; + i_media_object_offset = 0; } else { - msg_Warn( p_input, "p_peek[0]&0x80 != 0x80" ); + i_pts = (mtime_t)i_packet_send_time * 1000; + i_pts_delta = 0; + + i_media_object_offset = i_tmp; } - 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 ); - - i_packet_send_time = GetDWLE( p_peek + i_skip ); i_skip += 4; - i_packet_duration = GetWLE( p_peek + i_skip ); i_skip += 2; - -// 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; - + i_pts = __MAX( i_pts - p_sys->p_fp->i_preroll * 1000, 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++; + GETVALUE2b( i_payload_length_type, i_payload_data_length, 0 ); } else { - i_payload_count = 1; - i_payload_length_type = 0x02; // unused + i_payload_data_length = i_packet_length - + i_packet_padding_length - i_skip; } - for( i_payload = 0; i_payload < i_payload_count ; i_payload++ ) + if( i_payload_data_length < 0 || i_skip + i_payload_data_length > i_packet_size_left ) { - asf_stream_t *p_stream; - - 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; - - mtime_t i_pts; - mtime_t i_pts_delta; - - i_stream_number = p_peek[i_skip] & 0x7f; - i_skip++; - - 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 ); - - 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; - - i_media_object_offset = i_tmp; - } - else if( i_replicated_data_length == 1 ) - { - - msg_Warn( p_input, "found compressed payload" ); + break; + } +#if 0 + msg_Dbg( p_demux, + "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 - i_pts = (mtime_t)i_tmp * 1000; - i_pts_delta = (mtime_t)p_peek[i_skip] * 1000; i_skip++; + if( ( tk = p_sys->track[i_stream_number] ) == NULL ) + { + msg_Warn( p_demux, + "undeclared stream[Id 0x%x]", i_stream_number ); + i_skip += i_payload_data_length; + continue; // over payload + } - i_media_object_offset = 0; - } - else - { - i_pts = (mtime_t)i_packet_send_time * 1000; - i_pts_delta = 0; + if( !tk->p_es ) + { + i_skip += i_payload_data_length; + continue; + } - i_media_object_offset = i_tmp; - } - i_pts = __MAX( i_pts - p_demux->p_fp->i_preroll * 1000, 0 ); + 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 ) + { + block_t *p_frag; + int i_read; - if( b_packet_multiple_payload ) + // read sub payload length + if( i_replicated_data_length == 1 ) { - GETVALUE2b( i_payload_length_type, i_payload_data_length, 0 ); + i_sub_payload_data_length = p_peek[i_skip]; i_skip++; + i_payload_data_pos++; } else { - msg_Warn( p_input, "single payload" ); - i_payload_data_length = i_packet_length - i_packet_padding_length - i_skip; + i_sub_payload_data_length = i_payload_data_length; } - -#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( !( p_stream = p_demux->stream[i_stream_number] ) ) + /* FIXME I don't use i_media_object_number, sould I ? */ + if( tk->p_frame && i_media_object_offset == 0 ) { - msg_Warn( p_input, "undeclared stream[Id 0x%x]", i_stream_number ); - i_skip += i_payload_data_length; - continue; // over payload + /* send complete packet to decoder */ + block_t *p_gather = block_ChainGather( tk->p_frame ); + + es_out_Send( p_demux->out, tk->p_es, p_gather ); + + tk->p_frame = NULL; } - if( !p_stream->p_es || !p_stream->p_es->p_decoder_fifo ) + i_read = i_sub_payload_data_length + i_skip; + if( ( p_frag = stream_Block( p_demux->s, i_read ) ) == NULL ) { - i_skip += i_payload_data_length; - continue; + msg_Warn( p_demux, "cannot read data" ); + return 0; } + i_packet_size_left -= i_read; + p_frag->p_buffer += i_skip; + p_frag->i_buffer -= i_skip; - 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 ) + if( tk->p_frame == NULL ) { - data_packet_t *p_data; - int i_read; - // read sub payload length - if( i_replicated_data_length == 1 ) - { - i_sub_payload_data_length = p_peek[i_skip]; i_skip++; - i_payload_data_pos++; - } + tk->i_time = + ( (mtime_t)i_pts + i_payload * (mtime_t)i_pts_delta ); + + p_frag->i_pts = tk->i_time; + + if( tk->i_cat != VIDEO_ES ) + p_frag->i_dts = p_frag->i_pts; else { - i_sub_payload_data_length = i_payload_data_length; + p_frag->i_dts = p_frag->i_pts; + p_frag->i_pts = 0; } + } - /* 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 ) - { - input_DecodePES( p_stream->p_es->p_decoder_fifo, p_stream->p_pes ); - p_stream->p_pes = NULL; - } - } + block_ChainAppend( &tk->p_frame, p_frag ); - if( !p_stream->p_pes ) // add a new PES + i_skip = 0; + if( i_packet_size_left > 0 ) + { + if( stream_Peek( p_demux->s, &p_peek, i_packet_size_left ) + < i_packet_size_left ) { - 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 ); - - p_stream->p_pes->p_next = NULL; - p_stream->p_pes->i_nb_data = 0; - p_stream->p_pes->i_pes_size = 0; + msg_Warn( p_demux, "cannot peek, EOF ?" ); + return 0; } + } + } + } - i_read = i_sub_payload_data_length + i_skip; - if( input_SplitBuffer( p_input, &p_data, i_read ) < i_read ) - { - msg_Err( p_input, "cannot read data" ); - return( 0 ); - } - p_data->p_payload_start += i_skip; - i_packet_size_left -= i_read; + if( i_packet_size_left > 0 ) + { + if( stream_Read( p_demux->s, NULL, i_packet_size_left ) + < i_packet_size_left ) + { + msg_Warn( p_demux, "cannot skip data, EOF ?" ); + return 0; + } + } - - if( !p_stream->p_pes->p_first ) - { - p_stream->p_pes->p_first = p_stream->p_pes->p_last = p_data; - } - 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++; + return 1; + +loop_error_recovery: + msg_Warn( p_demux, "unsupported packet header" ); + if( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) + { + msg_Err( p_demux, "unsupported packet header, fatal error" ); + return -1; + } + stream_Read( p_demux->s, NULL, i_data_packet_min ); + + return 1; +} + +/***************************************************************************** + * + *****************************************************************************/ +static int DemuxInit( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + vlc_bool_t b_seekable; + int i; + + unsigned int i_stream; + asf_object_content_description_t *p_cd; + + /* 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->i_track = 0; + for( i = 0; i < 128; i++ ) + { + p_sys->track[i] = NULL; + } + p_sys->i_data_begin = -1; + p_sys->i_data_end = -1; + p_sys->meta = NULL; + + /* Now load all object ( except raw data ) */ + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable ); + if( (p_sys->p_root = ASF_ReadObjectRoot( p_demux->s, b_seekable )) == NULL ) + { + 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( p_sys->p_fp->i_min_data_packet_size != p_sys->p_fp->i_max_data_packet_size ) + { + msg_Warn( p_demux, "ASF plugin discarded (invalid file_properties object)" ); + goto error; + } + + 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 %d streams", p_sys->i_track ); + + for( i_stream = 0; i_stream < p_sys->i_track; i_stream ++ ) + { + asf_track_t *tk; + asf_object_stream_properties_t *p_sp; + vlc_bool_t b_access_selected; + + p_sp = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_stream_properties_guid, + i_stream ); + + tk = p_sys->track[p_sp->i_stream_number] = malloc( sizeof( asf_track_t ) ); + memset( tk, 0, sizeof( asf_track_t ) ); + + tk->i_time = -1; + tk->p_sp = p_sp; + tk->p_es = NULL; + tk->p_frame = NULL; - i_skip = 0; - if( i_packet_size_left > 0 ) + /* Check (in case of mms) if this track is selected (ie will receive data) */ + if( !stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, ACCESS_GET_PRIVATE_ID_STATE, + 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:%d) (by access)", + p_sp->i_stream_number ); + continue; + } + + if( ASF_CmpGUID( &p_sp->i_stream_type, &asf_object_stream_type_audio ) && + p_sp->i_type_specific_data_length >= sizeof( WAVEFORMATEX ) - 2 ) + { + es_format_t fmt; + 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 ); + 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] ); + + if( p_sp->i_type_specific_data_length > sizeof( WAVEFORMATEX ) && + i_format != WAVE_FORMAT_MPEGLAYER3 && + i_format != WAVE_FORMAT_MPEG ) + { + 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 ); + } + + tk->i_cat = AUDIO_ES; + tk->p_es = es_out_Add( p_demux->out, &fmt ); + es_format_Clean( &fmt ); + + msg_Dbg( p_demux, "added new audio stream(codec:0x%x,ID:%d)", + GetWLE( p_data ), p_sp->i_stream_number ); + } + else if( ASF_CmpGUID( &p_sp->i_stream_type, + &asf_object_stream_type_video ) && + p_sp->i_type_specific_data_length >= 11 + + sizeof( BITMAPINFOHEADER ) ) + { + es_format_t fmt; + 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] ) ); + fmt.video.i_width = GetDWLE( p_data + 4 ); + fmt.video.i_height= GetDWLE( p_data + 8 ); + + if( p_sp->i_type_specific_data_length > 11 + + sizeof( BITMAPINFOHEADER ) ) + { + 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 ); + } + + /* Look for an aspect ratio */ + if( p_sys->p_root->p_metadata ) + { + asf_object_metadata_t *p_meta = p_sys->p_root->p_metadata; + int i_aspect_x = 0, i_aspect_y = 0; + unsigned int i; + + for( i = 0; i < p_meta->i_record_entries_count; i++ ) { - if( input_Peek( p_input, &p_peek, i_packet_size_left ) < i_packet_size_left ) + if( !strcmp( p_meta->record[i].psz_name, "AspectRatioX" ) ) { - // EOF ? - msg_Warn( p_input, "cannot peek, EOF ?" ); - return( 0 ); + if( (!i_aspect_x && !p_meta->record[i].i_stream) || + p_meta->record[i].i_stream == + p_sp->i_stream_number ) + i_aspect_x = p_meta->record[i].i_val; + } + 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 ) + i_aspect_y = p_meta->record[i].i_val; } } - } + + if( i_aspect_x && i_aspect_y ) + { + fmt.video.i_aspect = i_aspect_x * + (int64_t)fmt.video.i_width * VOUT_ASPECT_FACTOR / + fmt.video.i_height / i_aspect_y; + } + } + + tk->i_cat = VIDEO_ES; + tk->p_es = es_out_Add( p_demux->out, &fmt ); + es_format_Clean( &fmt ); + + msg_Dbg( p_demux, "added new video stream(ID:%d)", + p_sp->i_stream_number ); } - - if( i_packet_size_left > 0 ) + else { - if( !ASF_SkipBytes( p_input, i_packet_size_left ) ) - { - msg_Warn( p_input, "cannot skip data, EOF ?" ); - return( 0 ); - } + tk->i_cat = UNKNOWN_ES; + msg_Dbg( p_demux, "ignoring unknown stream(ID:%d)", + p_sp->i_stream_number ); } + } + + 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; + } - continue; -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 ) + /* 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 ) + { + int64_t i_count; + int64_t i_size = stream_Size( p_demux->s ); + + if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end ) { - msg_Err( p_input, "unsupported packet header, fatal error" ); - return( -1 ); + i_size = p_sys->i_data_end; } - ASF_SkipBytes( p_input, i_data_packet_min ); - } // loop over packet - p_demux->i_time = 0; - for( i = 0; i < 128 ; i++ ) + /* 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; + + if( p_sys->i_length > 0 ) + { + p_sys->i_bitrate = 8 * i_size * (int64_t)1000000 / p_sys->i_length; + } + } + + /* Create meta information */ + p_sys->meta = vlc_meta_New(); + + if( ( p_cd = ASF_FindObject( p_sys->p_root->p_hdr, + &asf_object_content_description_guid, 0 ) ) ) { -#define p_stream p_demux->stream[i] - if( p_stream && p_stream->p_es && p_stream->p_es->p_decoder_fifo ) + if( p_cd->psz_title && *p_cd->psz_title ) + { + vlc_meta_Add( p_sys->meta, VLC_META_TITLE, p_cd->psz_title ); + } + if( p_cd->psz_author && *p_cd->psz_author ) + { + vlc_meta_Add( p_sys->meta, VLC_META_AUTHOR, p_cd->psz_author ); + } + if( p_cd->psz_copyright && *p_cd->psz_copyright ) + { + vlc_meta_Add( p_sys->meta, VLC_META_COPYRIGHT, p_cd->psz_copyright ); + } + if( p_cd->psz_description && *p_cd->psz_description ) + { + vlc_meta_Add( p_sys->meta, VLC_META_DESCRIPTION, p_cd->psz_description ); + } + if( p_cd->psz_rating && *p_cd->psz_rating ) { - p_demux->i_time = __MAX( p_demux->i_time, p_stream->i_time ); + vlc_meta_Add( p_sys->meta, VLC_META_RATING, p_cd->psz_rating ); } -#undef p_stream } - - return( 1 ); -} + 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 ); + 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( 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++; + } + } + + es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); + return VLC_SUCCESS; + +error: + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + return VLC_EGENERIC; +} /***************************************************************************** - * MP4End: frees unused data + * *****************************************************************************/ -static void Deactivate( vlc_object_t * p_this ) -{ -#define FREE( p ) \ - if( p ) { free( p ); } - - input_thread_t * p_input = (input_thread_t *)p_this; - demux_sys_t *p_demux = p_input->p_demux_data; - int i_stream; - - msg_Dbg( p_input, "Freeing all memory" ); - ASF_FreeObjectRoot( p_input, &p_demux->root ); - for( i_stream = 0; i_stream < 128; i_stream++ ) +static void DemuxEnd( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + int i; + + if( p_sys->p_root ) + { + ASF_FreeObjectRoot( p_demux->s, p_sys->p_root ); + p_sys->p_root = NULL; + } + if( p_sys->meta ) { -#define p_stream p_demux->stream[i_stream] - if( p_stream ) + vlc_meta_Delete( p_sys->meta ); + p_sys->meta = NULL; + } + + for( i = 0; i < 128; i++ ) + { + asf_track_t *tk = p_sys->track[i]; + + if( tk ) { - if( p_stream->p_pes ) + if( tk->p_frame ) + { + block_ChainRelease( tk->p_frame ); + } + if( tk->p_es ) { - input_DeletePES( p_input->p_method_data, p_stream->p_pes ); + es_out_Del( p_demux->out, tk->p_es ); } - free( p_stream ); + free( tk ); } -#undef p_stream + p_sys->track[i] = 0; } - -#undef FREE }