X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Favi%2Favi.c;h=17433a0c60b358d85180591d4f24b81f89607abc;hb=91f2877dec9e00d081facbb7b12f610a9f329357;hp=94c6f9eae22b61bfad704e47d918f368aea664cc;hpb=4d3ce49cb331cd367341659f532451bbff4b7c50;p=vlc diff --git a/modules/demux/avi/avi.c b/modules/demux/avi/avi.c index 94c6f9eae2..17433a0c60 100644 --- a/modules/demux/avi/avi.c +++ b/modules/demux/avi/avi.c @@ -1,8 +1,8 @@ /***************************************************************************** * avi.c : AVI file Stream input module for vlc ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: avi.c,v 1.38 2003/02/28 17:23:35 fenrir Exp $ + * Copyright (C) 2001-2004 VideoLAN + * $Id$ * Authors: Laurent Aimar * * This program is free software; you can redistribute it and/or modify @@ -24,1583 +24,1107 @@ * Preamble *****************************************************************************/ #include /* malloc(), free() */ -#include /* strdup() */ -#include -#include #include #include -#include "video.h" +#include "vlc_meta.h" +#include "codecs.h" #include "libavi.h" -#define __AVI_SUBTITLE__ 1 - -#ifdef __AVI_SUBTITLE__ -# include "../util/sub.h" -#endif -#include "avi.h" - -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int AVIInit ( vlc_object_t * ); -static void __AVIEnd ( vlc_object_t * ); -static int AVISeek ( input_thread_t *, mtime_t, int ); -static int AVIDemux_Seekable ( input_thread_t * ); -static int AVIDemux_UnSeekable( input_thread_t *p_input ); - -#define AVIEnd(a) __AVIEnd(VLC_OBJECT(a)) -#define FREE( p ) if( p ) { free( p ); (p) = NULL; } /***************************************************************************** * Module descriptor *****************************************************************************/ + +#define INTERLEAVE_TEXT N_("Force interleaved method" ) +#define INTERLEAVE_LONGTEXT N_( "Force interleaved method" ) + +#define INDEX_TEXT N_("Force index creation") +#define INDEX_LONGTEXT N_( \ + "Recreate a index for the AVI file so we can seek trough it more reliably." ) + +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); + vlc_module_begin(); - add_category_hint( N_("avi-demuxer"), NULL, VLC_TRUE ); - add_bool( "avi-interleaved", 0, NULL, - N_("force interleaved method"), - N_("force interleaved method"), VLC_TRUE ); - add_bool( "avi-index", 0, NULL, - N_("force index creation"), - N_("force index creation"), VLC_TRUE ); - - set_description( _("avi demuxer") ); - set_capability( "demux", 212 ); - set_callbacks( AVIInit, __AVIEnd ); + set_description( _("AVI demuxer") ); + set_capability( "demux2", 212 ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_DEMUX ); + + add_bool( "avi-interleaved", 0, NULL, + INTERLEAVE_TEXT, INTERLEAVE_LONGTEXT, VLC_TRUE ); + add_bool( "avi-index", 0, NULL, + INDEX_TEXT, INDEX_LONGTEXT, VLC_TRUE ); + + set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** - * Some useful functions to manipulate memory + * Local prototypes *****************************************************************************/ +static int Control ( demux_t *, int, va_list ); +static int Seek ( demux_t *, mtime_t, int ); +static int Demux_Seekable ( demux_t * ); +static int Demux_UnSeekable( demux_t * ); + +#define FREE( p ) if( p ) { free( p ); (p) = NULL; } +#define __ABS( x ) ( (x) < 0 ? (-(x)) : (x) ) -static uint16_t GetWLE( uint8_t *p_buff ) +typedef struct { - return (uint16_t)p_buff[0] | ( ((uint16_t)p_buff[1]) << 8 ); -} + vlc_fourcc_t i_fourcc; + off_t i_pos; + uint32_t i_size; + vlc_fourcc_t i_type; /* only for AVIFOURCC_LIST */ + + uint8_t i_peek[8]; /* first 8 bytes */ -static uint32_t GetDWLE( uint8_t *p_buff ) + unsigned int i_stream; + unsigned int i_cat; +} avi_packet_t; + + +typedef struct { - return (uint32_t)p_buff[0] | ( ((uint32_t)p_buff[1]) << 8 ) | - ( ((uint32_t)p_buff[2]) << 16 ) | ( ((uint32_t)p_buff[3]) << 24 ); -} + vlc_fourcc_t i_id; + uint32_t i_flags; + off_t i_pos; + uint32_t i_length; + uint32_t i_lengthtotal; -static uint32_t GetDWBE( uint8_t *p_buff ) +} avi_entry_t; + +typedef struct { - return (uint32_t)p_buff[3] | ( ((uint32_t)p_buff[2]) << 8 ) | - ( ((uint32_t)p_buff[1]) << 16 ) | ( ((uint32_t)p_buff[0]) << 24 ); -} -static vlc_fourcc_t GetFOURCC( byte_t *p_buff ) + vlc_bool_t b_activated; + + unsigned int i_cat; /* AUDIO_ES, VIDEO_ES */ + vlc_fourcc_t i_codec; + + int i_rate; + int i_scale; + int i_samplesize; + + es_out_id_t *p_es; + + avi_entry_t *p_index; + unsigned int i_idxnb; + unsigned int i_idxmax; + + unsigned int i_idxposc; /* numero of chunk */ + unsigned int i_idxposb; /* byte in the current chunk */ + + /* For VBR audio only */ + unsigned int i_blockno; + unsigned int i_blocksize; +} avi_track_t; + +struct demux_sys_t { - return VLC_FOURCC( p_buff[0], p_buff[1], p_buff[2], p_buff[3] ); -} + mtime_t i_time; + mtime_t i_length; + + vlc_bool_t b_seekable; + avi_chunk_t ck_root; + + vlc_bool_t b_odml; + + off_t i_movi_begin; + off_t i_movi_lastchunk_pos; /* XXX position of last valid chunk */ + + /* number of streams and information */ + unsigned int i_track; + avi_track_t **track; + + /* meta */ + vlc_meta_t *meta; +}; static inline off_t __EVEN( off_t i ) { return (i & 1) ? i + 1 : i; } -#define __ABS( x ) ( (x) < 0 ? (-(x)) : (x) ) +static mtime_t AVI_PTSToChunk( avi_track_t *, mtime_t i_pts ); +static mtime_t AVI_PTSToByte ( avi_track_t *, mtime_t i_pts ); +static mtime_t AVI_GetDPTS ( avi_track_t *, int64_t i_count ); +static mtime_t AVI_GetPTS ( avi_track_t * ); -/* read data in a pes */ -static int input_ReadInPES( input_thread_t *p_input, - pes_packet_t **pp_pes, - size_t i_size ) -{ - pes_packet_t *p_pes; - data_packet_t *p_data; +static int AVI_StreamChunkFind( demux_t *, unsigned int i_stream ); +static int AVI_StreamChunkSet ( demux_t *, + unsigned int i_stream, unsigned int i_ck ); +static int AVI_StreamBytesSet ( demux_t *, + unsigned int i_stream, off_t i_byte ); - if( !(p_pes = input_NewPES( p_input->p_method_data ) ) ) - { - pp_pes = NULL; - return -1; - } +vlc_fourcc_t AVI_FourccGetCodec( unsigned int i_cat, vlc_fourcc_t ); +static int AVI_GetKeyFlag ( vlc_fourcc_t , uint8_t * ); - *pp_pes = p_pes; +static int AVI_PacketGetHeader( demux_t *, avi_packet_t *p_pk ); +static int AVI_PacketNext ( demux_t * ); +static int AVI_PacketRead ( demux_t *, avi_packet_t *, block_t **); +static int AVI_PacketSearch ( demux_t * ); - if( !i_size ) - { - p_pes->p_first = - p_pes->p_last = - input_NewPacket( p_input->p_method_data, 0 ); - p_pes->i_nb_data = 1; - p_pes->i_pes_size = 0; - return 0; - } +static void AVI_IndexLoad ( demux_t * ); +static void AVI_IndexCreate ( demux_t * ); +static void AVI_IndexAddEntry( demux_sys_t *, int, avi_entry_t * ); - p_pes->i_nb_data = 0; - p_pes->i_pes_size = 0; +static mtime_t AVI_MovieGetLength( demux_t * ); - while( p_pes->i_pes_size < i_size ) - { - int i_read; +/***************************************************************************** + * Stream management + *****************************************************************************/ +static int AVI_TrackSeek ( demux_t *, int, mtime_t ); +static int AVI_TrackStopFinishedStreams( demux_t *); + +/* Remarks: + - For VBR mp3 stream: + count blocks by rounded-up chunksizes instead of chunks + we need full emulation of dshow avi demuxer bugs :( + fixes silly nandub-style a-v delaying in avi with vbr mp3... + (from mplayer 2002/08/02) + - to complete.... + */ - i_read = input_SplitBuffer(p_input, - &p_data, - __MIN( i_size - - p_pes->i_pes_size, 2048 ) ); - if( i_read <= 0 ) - { - /* should occur only with EOF and max allocation reached - * it safer to return an error */ - /* free pes */ - input_DeletePES( p_input->p_method_data, p_pes ); - return -1; - } +/***************************************************************************** + * Open: check file and initializes AVI structures + *****************************************************************************/ +static int Open( vlc_object_t * p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + demux_sys_t *p_sys; - if( !p_pes->p_first ) - { - p_pes->p_first = p_data; - } - else - { - p_pes->p_last->p_next = p_data; - } - p_pes->p_last = p_data; - p_pes->i_nb_data++; - p_pes->i_pes_size += i_read; - } + avi_chunk_t ck_riff; + avi_chunk_list_t *p_riff = (avi_chunk_list_t*)&ck_riff; + avi_chunk_list_t *p_hdrl, *p_movi; + avi_chunk_avih_t *p_avih; + unsigned int i_track; + unsigned int i, i_peeker; - return p_pes->i_pes_size; -} + uint8_t *p_peek; -/* Test if it seems that it's a key frame */ -static int AVI_GetKeyFlag( vlc_fourcc_t i_fourcc, uint8_t *p_byte ) -{ - switch( i_fourcc ) + /* Is it an avi file ? */ + if( stream_Peek( p_demux->s, &p_peek, 200 ) < 200 ) return VLC_EGENERIC; + + for( i_peeker = 0; i_peeker < 188; i_peeker++ ) { - case FOURCC_DIV1: - /* we have: - * startcode: 0x00000100 32bits - * framenumber ? 5bits - * piture type 0(I),1(P) 2bits - */ - if( GetDWBE( p_byte ) != 0x00000100 ) - { - /* it's not an msmpegv1 stream, strange...*/ - return AVIIF_KEYFRAME; - } - else - { - return p_byte[4] & 0x06 ? 0 : AVIIF_KEYFRAME; - } - case FOURCC_DIV2: - case FOURCC_DIV3: // wmv1 also - /* we have - * picture type 0(I),1(P) 2bits - */ - return p_byte[0] & 0xC0 ? 0 : AVIIF_KEYFRAME; - case FOURCC_mp4v: - /* we should find first occurence of 0x000001b6 (32bits) - * startcode: 0x000001b6 32bits - * piture type 0(I),1(P) 2bits - */ - if( GetDWBE( p_byte ) != 0x000001b6 ) - { - /* not true , need to find the first VOP header */ - return AVIIF_KEYFRAME; - } - else - { - return p_byte[4] & 0xC0 ? 0 : AVIIF_KEYFRAME; - } - default: - /* I can't do it, so say yes */ - return AVIIF_KEYFRAME; + if( !strncmp( &p_peek[0], "RIFF", 4 ) && + !strncmp( &p_peek[8], "AVI ", 4 ) ) break; + p_peek++; } -} - -vlc_fourcc_t AVI_FourccGetCodec( unsigned int i_cat, vlc_fourcc_t i_codec ) -{ - switch( i_cat ) + if( i_peeker == 188 ) { - case AUDIO_ES: - switch( i_codec ) - { - case WAVE_FORMAT_PCM: - return VLC_FOURCC( 'a', 'r', 'a', 'w' ); - case WAVE_FORMAT_MPEG: - case WAVE_FORMAT_MPEGLAYER3: - return VLC_FOURCC( 'm', 'p', 'g', 'a' ); - case WAVE_FORMAT_A52: - return VLC_FOURCC( 'a', '5', '2', ' ' ); - case WAVE_FORMAT_WMA1: - return VLC_FOURCC( 'w', 'm', 'a', '1' ); - case WAVE_FORMAT_WMA2: - return VLC_FOURCC( 'w', 'm', 'a', '2' ); - default: - return VLC_FOURCC( 'm', 's', - ( i_codec >> 8 )&0xff, i_codec&0xff ); - } - case VIDEO_ES: - // XXX DIV1 <- msmpeg4v1, DIV2 <- msmpeg4v2, DIV3 <- msmpeg4v3, mp4v for mpeg4 - switch( i_codec ) - { - case FOURCC_DIV1: - case FOURCC_div1: - case FOURCC_MPG4: - case FOURCC_mpg4: - return FOURCC_DIV1; - case FOURCC_DIV2: - case FOURCC_div2: - case FOURCC_MP42: - case FOURCC_mp42: - case FOURCC_MPG3: - case FOURCC_mpg3: - return FOURCC_DIV2; - case FOURCC_div3: - case FOURCC_MP43: - case FOURCC_mp43: - case FOURCC_DIV3: - case FOURCC_DIV4: - case FOURCC_div4: - case FOURCC_DIV5: - case FOURCC_div5: - case FOURCC_DIV6: - case FOURCC_div6: - case FOURCC_AP41: - case FOURCC_3IV1: - return FOURCC_DIV3; - case FOURCC_DIVX: - case FOURCC_divx: - case FOURCC_MP4S: - case FOURCC_mp4s: - case FOURCC_M4S2: - case FOURCC_m4s2: - case FOURCC_xvid: - case FOURCC_XVID: - case FOURCC_XviD: - case FOURCC_DX50: - case FOURCC_mp4v: - case FOURCC_4: - return FOURCC_mp4v; - } - default: - return VLC_FOURCC( 'u', 'n', 'd', 'f' ); + return VLC_EGENERIC; } -} -static void AVI_ParseStreamHeader( vlc_fourcc_t i_id, - int *pi_number, int *pi_type ) -{ -#define SET_PTR( p, v ) if( p ) *(p) = (v); - int c1, c2; + /* Initialize input structures. */ + p_sys = p_demux->p_sys = malloc( sizeof(demux_sys_t) ); + memset( p_sys, 0, sizeof( demux_sys_t ) ); + p_sys->i_time = 0; + p_sys->i_length = 0; + p_sys->i_movi_lastchunk_pos = 0; + p_sys->b_odml = VLC_FALSE; + p_sys->i_track = 0; + p_sys->track = NULL; + p_sys->meta = NULL; - c1 = ((uint8_t *)&i_id)[0]; - c2 = ((uint8_t *)&i_id)[1]; + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_seekable ); - if( c1 < '0' || c1 > '9' || c2 < '0' || c2 > '9' ) + p_demux->pf_control = Control; + p_demux->pf_demux = Demux_Seekable; + /* For unseekable stream, automaticaly use Demux_UnSeekable */ + if( !p_sys->b_seekable || config_GetInt( p_demux, "avi-interleaved" ) ) { - SET_PTR( pi_number, 100 ); /* > max stream number */ - SET_PTR( pi_type, UNKNOWN_ES ); + p_demux->pf_demux = Demux_UnSeekable; } - else + + if( i_peeker > 0 ) { - SET_PTR( pi_number, (c1 - '0') * 10 + (c2 - '0' ) ); - switch( VLC_TWOCC( ((uint8_t *)&i_id)[2], ((uint8_t *)&i_id)[3] ) ) - { - case AVITWOCC_wb: - SET_PTR( pi_type, AUDIO_ES ); - break; - case AVITWOCC_dc: - case AVITWOCC_db: - SET_PTR( pi_type, VIDEO_ES ); - break; - default: - SET_PTR( pi_type, UNKNOWN_ES ); - break; - } + stream_Read( p_demux->s, NULL, i_peeker ); } -#undef SET_PTR -} - -static int AVI_PacketGetHeader( input_thread_t *p_input, avi_packet_t *p_pk ) -{ - uint8_t *p_peek; - if( input_Peek( p_input, &p_peek, 16 ) < 16 ) + if( AVI_ChunkReadRoot( p_demux->s, &p_sys->ck_root ) ) { + msg_Err( p_demux, "avi module discarded (invalid file)" ); return VLC_EGENERIC; } - p_pk->i_fourcc = GetFOURCC( p_peek ); - p_pk->i_size = GetDWLE( p_peek + 4 ); - p_pk->i_pos = AVI_TellAbsolute( p_input ); - if( p_pk->i_fourcc == AVIFOURCC_LIST ) - { - p_pk->i_type = GetFOURCC( p_peek + 8 ); - } - else + + if( AVI_ChunkCount( &p_sys->ck_root, AVIFOURCC_RIFF ) > 1 ) { - p_pk->i_type = 0; - } + unsigned int i_count = + AVI_ChunkCount( &p_sys->ck_root, AVIFOURCC_RIFF ); - memcpy( p_pk->i_peek, p_peek + 8, 8 ); + msg_Warn( p_demux, "multiple riff -> OpenDML ?" ); + for( i = 1; i < i_count; i++ ) + { + avi_chunk_list_t *p_sysx; - AVI_ParseStreamHeader( p_pk->i_fourcc, &p_pk->i_stream, &p_pk->i_cat ); - return VLC_SUCCESS; -} + p_sysx = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, i ); + if( p_sysx->i_type == AVIFOURCC_AVIX ) + { + msg_Warn( p_demux, "detected OpenDML file" ); + p_sys->b_odml = VLC_TRUE; + break; + } + } + } -static int AVI_PacketNext( input_thread_t *p_input ) -{ - avi_packet_t avi_ck; + p_riff = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, 0 ); + p_hdrl = AVI_ChunkFind( p_riff, AVIFOURCC_hdrl, 0 ); + p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0 ); - if( AVI_PacketGetHeader( p_input, &avi_ck ) ) + if( !p_hdrl || !p_movi ) { - return VLC_EGENERIC; + msg_Err( p_demux, "avi module discarded (invalid file)" ); + goto error; } - if( avi_ck.i_fourcc == AVIFOURCC_LIST && avi_ck.i_type == AVIFOURCC_rec ) + + if( !( p_avih = AVI_ChunkFind( p_hdrl, AVIFOURCC_avih, 0 ) ) ) { - return AVI_SkipBytes( p_input, 12 ); + msg_Err( p_demux, "cannot find avih chunk" ); + goto error; } - else + i_track = AVI_ChunkCount( p_hdrl, AVIFOURCC_strl ); + if( p_avih->i_streams != i_track ) { - return AVI_SkipBytes( p_input, __EVEN( avi_ck.i_size ) + 8 ); + msg_Warn( p_demux, + "found %d stream but %d are declared", + i_track, p_avih->i_streams ); } -} -static int AVI_PacketRead( input_thread_t *p_input, - avi_packet_t *p_pk, - pes_packet_t **pp_pes ) -{ - size_t i_size; - vlc_bool_t b_pad; - - i_size = __EVEN( p_pk->i_size + 8 ); - b_pad = ( i_size != p_pk->i_size + 8 ); - - if( input_ReadInPES( p_input, pp_pes, i_size ) != (ssize_t)i_size ) + if( i_track == 0 ) { - return VLC_EGENERIC; + msg_Err( p_demux, "no stream defined!" ); + goto error; } - (*pp_pes)->p_first->p_payload_start += 8; - (*pp_pes)->i_pes_size -= 8; - if( b_pad ) + /* print information on streams */ + msg_Dbg( p_demux, "AVIH: %d stream, flags %s%s%s%s ", + i_track, + p_avih->i_flags&AVIF_HASINDEX?" HAS_INDEX":"", + p_avih->i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"", + p_avih->i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"", + p_avih->i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"" ); + if( ( p_sys->meta = vlc_meta_New() ) ) { - (*pp_pes)->p_last->p_payload_end--; - (*pp_pes)->i_pes_size--; + char buffer[200]; + sprintf( buffer, "%s%s%s%s", + p_avih->i_flags&AVIF_HASINDEX?" HAS_INDEX":"", + p_avih->i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"", + p_avih->i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"", + p_avih->i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"" ); + vlc_meta_Add( p_sys->meta, VLC_META_SETTING, buffer ); } - return VLC_SUCCESS; -} + /* now read info on each stream and create ES */ + for( i = 0 ; i < i_track; i++ ) + { + avi_track_t *tk = malloc( sizeof( avi_track_t ) ); + avi_chunk_list_t *p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, i ); + avi_chunk_strh_t *p_strh = AVI_ChunkFind( p_strl, AVIFOURCC_strh, 0 ); + avi_chunk_STRING_t *p_strn = AVI_ChunkFind( p_strl, AVIFOURCC_strn, 0 ); + avi_chunk_strf_auds_t *p_auds; + avi_chunk_strf_vids_t *p_vids; + es_format_t fmt; -static int AVI_PacketSearch( input_thread_t *p_input ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; + tk->b_activated = VLC_FALSE; + tk->p_index = 0; + tk->i_idxnb = 0; + tk->i_idxmax = 0; + tk->i_idxposc = 0; + tk->i_idxposb = 0; - avi_packet_t avi_pk; - for( ;; ) - { - if( AVI_SkipBytes( p_input, 1 ) ) - { - return VLC_EGENERIC; - } - AVI_PacketGetHeader( p_input, &avi_pk ); - if( avi_pk.i_stream < p_avi->i_streams && - ( avi_pk.i_cat == AUDIO_ES || avi_pk.i_cat == VIDEO_ES ) ) - { - return VLC_SUCCESS; - } - switch( avi_pk.i_fourcc ) + tk->i_blockno = 0; + tk->i_blocksize = 0; + + p_vids = (avi_chunk_strf_vids_t*)AVI_ChunkFind( p_strl, AVIFOURCC_strf, 0 ); + p_auds = (avi_chunk_strf_auds_t*)AVI_ChunkFind( p_strl, AVIFOURCC_strf, 0 ); + + if( p_strl == NULL || p_strh == NULL || p_auds == NULL || p_vids == NULL ) { - case AVIFOURCC_JUNK: - case AVIFOURCC_LIST: - case AVIFOURCC_idx1: - return VLC_SUCCESS; + msg_Warn( p_demux, "stream[%d] incomplete", i ); + continue; } - } -} + tk->i_rate = p_strh->i_rate; + tk->i_scale = p_strh->i_scale; + tk->i_samplesize = p_strh->i_samplesize; + msg_Dbg( p_demux, "stream[%d] rate:%d scale:%d samplesize:%d", + i, tk->i_rate, tk->i_scale, tk->i_samplesize ); -static void __AVI_AddEntryIndex( avi_stream_t *p_info, - AVIIndexEntry_t *p_index) -{ - if( p_info->p_index == NULL ) - { - p_info->i_idxmax = 16384; - p_info->i_idxnb = 0; - if( !( p_info->p_index = calloc( p_info->i_idxmax, - sizeof( AVIIndexEntry_t ) ) ) ) + switch( p_strh->i_type ) { - return; - } - } - if( p_info->i_idxnb >= p_info->i_idxmax ) - { - p_info->i_idxmax += 16384; - if( !( p_info->p_index = realloc( (void*)p_info->p_index, - p_info->i_idxmax * - sizeof( AVIIndexEntry_t ) ) ) ) - { - return; - } - } - /* calculate cumulate length */ - if( p_info->i_idxnb > 0 ) - { - p_index->i_lengthtotal = - p_info->p_index[p_info->i_idxnb - 1].i_length + - p_info->p_index[p_info->i_idxnb - 1].i_lengthtotal; - } - else - { - p_index->i_lengthtotal = 0; - } - - p_info->p_index[p_info->i_idxnb] = *p_index; - p_info->i_idxnb++; - -} - -static void AVI_IndexAddEntry( demux_sys_t *p_avi, - int i_stream, - AVIIndexEntry_t *p_index) -{ - __AVI_AddEntryIndex( p_avi->pp_info[i_stream], - p_index ); - if( p_avi->i_movi_lastchunk_pos < p_index->i_pos ) - { - p_avi->i_movi_lastchunk_pos = p_index->i_pos; - } -} - -static void AVI_IndexLoad( input_thread_t *p_input ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; - - avi_chunk_list_t *p_riff; - avi_chunk_list_t *p_movi; - avi_chunk_idx1_t *p_idx1; + case( AVIFOURCC_auds ): + tk->i_cat = AUDIO_ES; + tk->i_codec = AVI_FourccGetCodec( AUDIO_ES, + p_auds->p_wf->wFormatTag ); + if( ( tk->i_blocksize = p_auds->p_wf->nBlockAlign ) == 0 ) + { + if( p_auds->p_wf->wFormatTag == 1 ) + { + tk->i_blocksize = p_auds->p_wf->nChannels * (p_auds->p_wf->wBitsPerSample/8); + } + else + { + tk->i_blocksize = 1; + } + } + es_format_Init( &fmt, AUDIO_ES, tk->i_codec ); + + fmt.audio.i_channels = p_auds->p_wf->nChannels; + fmt.audio.i_rate = p_auds->p_wf->nSamplesPerSec; + fmt.i_bitrate = p_auds->p_wf->nAvgBytesPerSec*8; + fmt.audio.i_blockalign = p_auds->p_wf->nBlockAlign; + fmt.audio.i_bitspersample = p_auds->p_wf->wBitsPerSample; + fmt.i_extra = __MIN( p_auds->p_wf->cbSize, + p_auds->i_chunk_size - sizeof(WAVEFORMATEX) ); + fmt.p_extra = &p_auds->p_wf[1]; + msg_Dbg( p_demux, "stream[%d] audio(0x%x) %d channels %dHz %dbits", + i, p_auds->p_wf->wFormatTag, p_auds->p_wf->nChannels, + p_auds->p_wf->nSamplesPerSec, p_auds->p_wf->wBitsPerSample); + break; - unsigned int i_stream; - unsigned int i_index; - off_t i_offset; + case( AVIFOURCC_vids ): + tk->i_cat = VIDEO_ES; + tk->i_codec = AVI_FourccGetCodec( VIDEO_ES, + p_vids->p_bih->biCompression ); + if( p_vids->p_bih->biCompression == 0x00 ) + { + switch( p_vids->p_bih->biBitCount ) + { + case 32: + tk->i_codec = VLC_FOURCC('R','V','3','2'); + break; + case 24: + tk->i_codec = VLC_FOURCC('R','V','2','4'); + break; + case 16: + /* tk->i_codec = VLC_FOURCC('R','V','1','6');*/ + /* break;*/ + case 15: + tk->i_codec = VLC_FOURCC('R','V','1','5'); + break; + case 9: + tk->i_codec = VLC_FOURCC( 'Y', 'V', 'U', '9' ); /* <- TODO check that */ + break; + } + es_format_Init( &fmt, VIDEO_ES, tk->i_codec ); - p_riff = (avi_chunk_list_t*)AVI_ChunkFind( &p_avi->ck_root, - AVIFOURCC_RIFF, 0); + if( p_vids->p_bih->biBitCount == 24 ) + { + /* This is in BGR format */ + fmt.video.i_bmask = 0x00ff0000; + fmt.video.i_gmask = 0x0000ff00; + fmt.video.i_rmask = 0x000000ff; + } + } + else + { + es_format_Init( &fmt, VIDEO_ES, p_vids->p_bih->biCompression ); + if( tk->i_codec == FOURCC_mp4v && + !strncasecmp( (char*)&p_strh->i_handler, "XVID", 4 ) ) + { + fmt.i_codec = VLC_FOURCC( 'X', 'V', 'I', 'D' ); + } + } + tk->i_samplesize = 0; + fmt.video.i_width = p_vids->p_bih->biWidth; + fmt.video.i_height = p_vids->p_bih->biHeight; + fmt.video.i_bits_per_pixel = p_vids->p_bih->biBitCount; + fmt.i_extra = + __MIN( p_vids->p_bih->biSize - sizeof( BITMAPINFOHEADER ), + p_vids->i_chunk_size - sizeof(BITMAPINFOHEADER) ); + fmt.p_extra = &p_vids->p_bih[1]; + msg_Dbg( p_demux, "stream[%d] video(%4.4s) %dx%d %dbpp %ffps", + i, + (char*)&p_vids->p_bih->biCompression, + p_vids->p_bih->biWidth, + p_vids->p_bih->biHeight, + p_vids->p_bih->biBitCount, + (float)tk->i_rate/(float)tk->i_scale ); - p_idx1 = (avi_chunk_idx1_t*)AVI_ChunkFind( p_riff, AVIFOURCC_idx1, 0); - p_movi = (avi_chunk_list_t*)AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0); + if( p_vids->p_bih->biCompression == 0x00 ) + { + /* RGB DIB are coded from bottom to top */ + fmt.video.i_height = + (unsigned int)(-(int)p_vids->p_bih->biHeight); + } + break; - if( !p_idx1 ) - { - msg_Warn( p_input, "cannot find idx1 chunk, no index defined" ); - return; - } - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) - { - p_avi->pp_info[i_stream]->i_idxnb = 0; - p_avi->pp_info[i_stream]->i_idxmax = 0; - p_avi->pp_info[i_stream]->p_index = NULL; - } - /* *** calculate offset *** */ - if( p_idx1->i_entry_count > 0 && - p_idx1->entry[0].i_pos < p_movi->i_chunk_pos ) - { - i_offset = p_movi->i_chunk_pos + 8; + case( AVIFOURCC_txts): + tk->i_cat = SPU_ES; + tk->i_codec = VLC_FOURCC( 's', 'u', 'b', 't' ); + msg_Dbg( p_demux, "stream[%d] subtitles", i ); + es_format_Init( &fmt, SPU_ES, tk->i_codec ); + break; + + case( AVIFOURCC_mids): + msg_Dbg( p_demux, "stream[%d] midi is UNSUPPORTED", i ); + default: + msg_Warn( p_demux, "stream[%d] unknown type", i ); + free( tk ); + continue; + } + if( p_strn ) + { + fmt.psz_description = strdup( p_strn->p_str ); + } + tk->p_es = es_out_Add( p_demux->out, &fmt ); + TAB_APPEND( p_sys->i_track, p_sys->track, tk ); } - else + + if( p_sys->i_track <= 0 ) { - i_offset = 0; + msg_Err( p_demux, "no valid track" ); + goto error; } - for( i_index = 0; i_index < p_idx1->i_entry_count; i_index++ ) + if( config_GetInt( p_demux, "avi-index" ) ) { - unsigned int i_cat; - - AVI_ParseStreamHeader( p_idx1->entry[i_index].i_fourcc, - &i_stream, - &i_cat ); - if( i_stream < p_avi->i_streams && - i_cat == p_avi->pp_info[i_stream]->i_cat ) + if( p_sys->b_seekable ) { - AVIIndexEntry_t index; - index.i_id = p_idx1->entry[i_index].i_fourcc; - index.i_flags = - p_idx1->entry[i_index].i_flags&(~AVIIF_FIXKEYFRAME); - index.i_pos = p_idx1->entry[i_index].i_pos + i_offset; - index.i_length = p_idx1->entry[i_index].i_length; - AVI_IndexAddEntry( p_avi, i_stream, &index ); + AVI_IndexCreate( p_demux ); + } + else + { + msg_Warn( p_demux, "cannot create index (unseekable stream)" ); + AVI_IndexLoad( p_demux ); } } - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) - { - msg_Dbg( p_input, - "stream[%d] creating %d index entries", - i_stream, - p_avi->pp_info[i_stream]->i_idxnb ); - } - -} - -static void AVI_IndexCreate( input_thread_t *p_input ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; - - avi_chunk_list_t *p_riff; - avi_chunk_list_t *p_movi; - - unsigned int i_stream; - off_t i_movi_end; - - p_riff = (avi_chunk_list_t*)AVI_ChunkFind( &p_avi->ck_root, - AVIFOURCC_RIFF, 0); - p_movi = (avi_chunk_list_t*)AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0); - - if( !p_movi ) + else { - msg_Err( p_input, "cannot find p_movi" ); - return; + AVI_IndexLoad( p_demux ); } - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + /* *** movie length in sec *** */ + p_sys->i_length = AVI_MovieGetLength( p_demux ); + if( p_sys->i_length < (mtime_t)p_avih->i_totalframes * + (mtime_t)p_avih->i_microsecperframe / + (mtime_t)1000000 ) { - p_avi->pp_info[i_stream]->i_idxnb = 0; - p_avi->pp_info[i_stream]->i_idxmax = 0; - p_avi->pp_info[i_stream]->p_index = NULL; + msg_Warn( p_demux, "broken or missing index, 'seek' will be axproximative or will have strange behavour" ); } - i_movi_end = __MIN( (off_t)(p_movi->i_chunk_pos + p_movi->i_chunk_size), - p_input->stream.p_selected_area->i_size ); - - AVI_SeekAbsolute( p_input, p_movi->i_chunk_pos + 12); - msg_Warn( p_input, "creating index from LIST-movi, will take time !" ); - for( ;; ) + /* fix some BeOS MediaKit generated file */ + for( i = 0 ; i < p_sys->i_track; i++ ) { - avi_packet_t pk; + avi_track_t *tk = p_sys->track[i]; + avi_chunk_list_t *p_strl; + avi_chunk_strh_t *p_strh; + avi_chunk_strf_auds_t *p_auds; - if( AVI_PacketGetHeader( p_input, &pk ) ) + if( tk->i_cat != AUDIO_ES ) { - break; + continue; } - if( pk.i_stream < p_avi->i_streams && - pk.i_cat == p_avi->pp_info[pk.i_stream]->i_cat ) + if( tk->i_idxnb < 1 || + tk->i_scale != 1 || + tk->i_samplesize != 0 ) { - AVIIndexEntry_t index; - index.i_id = pk.i_fourcc; - index.i_flags = - AVI_GetKeyFlag(p_avi->pp_info[pk.i_stream]->i_codec, pk.i_peek); - index.i_pos = pk.i_pos; - index.i_length = pk.i_size; - AVI_IndexAddEntry( p_avi, pk.i_stream, &index ); + continue; } - else + p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, i ); + p_strh = AVI_ChunkFind( p_strl, AVIFOURCC_strh, 0 ); + p_auds = AVI_ChunkFind( p_strl, AVIFOURCC_strf, 0 ); + + if( p_auds->p_wf->wFormatTag != WAVE_FORMAT_PCM && + (unsigned int)tk->i_rate == p_auds->p_wf->nSamplesPerSec ) { - switch( pk.i_fourcc ) + int64_t i_track_length = + tk->p_index[tk->i_idxnb-1].i_length + + tk->p_index[tk->i_idxnb-1].i_lengthtotal; + mtime_t i_length = (mtime_t)p_avih->i_totalframes * + (mtime_t)p_avih->i_microsecperframe; + + if( i_length == 0 ) { - case AVIFOURCC_idx1: - goto print_stat; - case AVIFOURCC_rec: - case AVIFOURCC_JUNK: - break; - default: - msg_Warn( p_input, "need resync, probably broken avi" ); - if( AVI_PacketSearch( p_input ) ) - { - msg_Warn( p_input, "lost sync, abord index creation" ); - goto print_stat; - } + msg_Warn( p_demux, "track[%d] cannot be fixed (BeOS MediaKit generated)", i ); + continue; } - } - if( pk.i_pos + pk.i_size >= i_movi_end || - AVI_PacketNext( p_input ) ) - { - break; + tk->i_samplesize = 1; + tk->i_rate = i_track_length * (int64_t)1000000/ i_length; + msg_Warn( p_demux, "track[%d] fixed with rate=%d scale=%d (BeOS MediaKit generated)", i, tk->i_rate, tk->i_scale ); } } -print_stat: - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + if( p_sys->b_seekable ) { - msg_Dbg( p_input, - "stream[%d] creating %d index entries", - i_stream, - p_avi->pp_info[i_stream]->i_idxnb ); + /* we have read all chunk so go back to movi */ + stream_Seek( p_demux->s, p_movi->i_chunk_pos ); } -} + /* Skip movi header */ + stream_Read( p_demux->s, NULL, 12 ); + + p_sys->i_movi_begin = p_movi->i_chunk_pos; + return VLC_SUCCESS; +error: + if( p_sys->meta ) + { + vlc_meta_Delete( p_sys->meta ); + } + AVI_ChunkFreeRoot( p_demux->s, &p_sys->ck_root ); + free( p_sys ); + return VLC_EGENERIC; +} /***************************************************************************** - * Stream management + * Close: frees unused data *****************************************************************************/ -static vlc_bool_t AVI_StreamStart ( input_thread_t *, demux_sys_t *, int ); -static int AVI_StreamSeek ( input_thread_t *, demux_sys_t *, int, mtime_t ); -static void AVI_StreamStop ( input_thread_t *, demux_sys_t *, int ); -static int AVI_StreamStopFinishedStreams( input_thread_t *, demux_sys_t * ); - -static vlc_bool_t AVI_StreamStart( input_thread_t *p_input, - demux_sys_t *p_avi, int i_stream ) +static void Close ( vlc_object_t * p_this ) { -#define p_stream p_avi->pp_info[i_stream] - if( !p_stream->p_es ) - { - msg_Warn( p_input, "stream[%d] unselectable", i_stream ); - return VLC_FALSE; - } - if( p_stream->b_activated ) - { - msg_Warn( p_input, "stream[%d] already selected", i_stream ); - return VLC_TRUE; - } + demux_t * p_demux = (demux_t *)p_this; + unsigned int i; + demux_sys_t *p_sys = p_demux->p_sys ; - if( !p_stream->p_es->p_decoder_fifo ) - { - vlc_mutex_lock( &p_input->stream.stream_lock ); - input_SelectES( p_input, p_stream->p_es ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - } - p_stream->b_activated = p_stream->p_es->p_decoder_fifo ? VLC_TRUE - : VLC_FALSE; - if( p_stream->b_activated && p_avi->b_seekable) + for( i = 0; i < p_sys->i_track; i++ ) { - AVI_StreamSeek( p_input, p_avi, i_stream, p_avi->i_time ); + if( p_sys->track[i] ) + { + FREE( p_sys->track[i]->p_index ); + free( p_sys->track[i] ); + } } + FREE( p_sys->track ); + AVI_ChunkFreeRoot( p_demux->s, &p_sys->ck_root ); + vlc_meta_Delete( p_sys->meta ); - return p_stream->b_activated; -#undef p_stream + free( p_sys ); } -static void AVI_StreamStop( input_thread_t *p_input, - demux_sys_t *p_avi, int i_stream ) +/***************************************************************************** + * Demux_Seekable: reads and demuxes data packets for stream seekable + ***************************************************************************** + * AVIDemux: reads and demuxes data packets + ***************************************************************************** + * Returns -1 in case of error, 0 in case of EOF, 1 otherwise + *****************************************************************************/ +typedef struct { -#define p_stream p_avi->pp_info[i_stream] - - if( !p_stream->b_activated ) - { - msg_Warn( p_input, "stream[%d] already unselected", i_stream ); - return; - } + vlc_bool_t b_ok; - if( p_stream->p_es->p_decoder_fifo ) - { - vlc_mutex_lock( &p_input->stream.stream_lock ); - input_UnselectES( p_input, p_stream->p_es ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - } + int i_toread; + off_t i_posf; /* where we will read : + if i_idxposb == 0 : begining of chunk (+8 to acces data) + else : point on data directly */ +} avi_track_toread_t; - p_stream->b_activated = VLC_FALSE; +static int Demux_Seekable( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; -#undef p_stream -} + unsigned int i_track_count = 0; + unsigned int i_track; + vlc_bool_t b_stream; + /* cannot be more than 100 stream (dcXX or wbXX) */ + avi_track_toread_t toread[100]; -static int AVI_StreamStopFinishedStreams( input_thread_t *p_input, - demux_sys_t *p_avi ) -{ - unsigned int i_stream; - int b_end; - for( i_stream = 0,b_end = VLC_TRUE; - i_stream < p_avi->i_streams; i_stream++ ) + /* detect new selected/unselected streams */ + for( i_track = 0; i_track < p_sys->i_track; i_track++ ) { -#define p_stream p_avi->pp_info[i_stream] - if( p_stream->i_idxposc >= p_stream->i_idxnb ) + avi_track_t *tk = p_sys->track[i_track]; + vlc_bool_t b; + + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); + if( b && !tk->b_activated ) { - AVI_StreamStop( p_input, p_avi, i_stream ); + if( p_sys->b_seekable) + { + AVI_TrackSeek( p_demux, i_track, p_sys->i_time ); + } + tk->b_activated = VLC_TRUE; } - else + else if( !b && tk->b_activated ) { - b_end = VLC_FALSE; + tk->b_activated = VLC_FALSE; + } + if( b ) + { + i_track_count++; } -#undef p_stream } - return( b_end ); -} -/**************************************************************************** - * AVI_MovieGetLength give max streams length in second - ****************************************************************************/ -static mtime_t AVI_MovieGetLength( input_thread_t *p_input, demux_sys_t *p_avi ) -{ - unsigned int i_stream; - mtime_t i_maxlength; - i_maxlength = 0; - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + if( i_track_count <= 0 ) { -#define p_stream p_avi->pp_info[i_stream] - mtime_t i_length; - /* fix length for each stream */ - if( p_stream->i_idxnb < 1 || !p_stream->p_index ) + int64_t i_length = p_sys->i_length * (mtime_t)1000000; + + p_sys->i_time += 25*1000; /* read 25ms */ + if( i_length > 0 ) { - continue; + if( p_sys->i_time >= i_length ) + return 0; + return 1; } + msg_Warn( p_demux, "no track selected, exiting..." ); + return 0; + } + + /* wait for the good time */ + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_time + 1 ); + p_sys->i_time += 25*1000; /* read 25ms */ - if( p_stream->i_samplesize ) + /* init toread */ + for( i_track = 0; i_track < p_sys->i_track; i_track++ ) + { + avi_track_t *tk = p_sys->track[i_track]; + mtime_t i_dpts; + + toread[i_track].b_ok = tk->b_activated; + if( tk->i_idxposc < tk->i_idxnb ) { - i_length = - (mtime_t)( p_stream->p_index[p_stream->i_idxnb-1].i_lengthtotal + - p_stream->p_index[p_stream->i_idxnb-1].i_length ) * - (mtime_t)p_stream->i_scale / - (mtime_t)p_stream->i_rate / - (mtime_t)p_stream->i_samplesize; + toread[i_track].i_posf = tk->p_index[tk->i_idxposc].i_pos; + if( tk->i_idxposb > 0 ) + { + toread[i_track].i_posf += 8 + tk->i_idxposb; + } } else { - i_length = (mtime_t)p_stream->i_idxnb * - (mtime_t)p_stream->i_scale / - (mtime_t)p_stream->i_rate; + toread[i_track].i_posf = -1; } - msg_Dbg( p_input, - "stream[%d] length:"I64Fd" (based on index)", - i_stream, - i_length ); - i_maxlength = __MAX( i_maxlength, i_length ); -#undef p_stream - } + i_dpts = p_sys->i_time - AVI_GetPTS( tk ); - return i_maxlength; -} + if( tk->i_samplesize ) + { + toread[i_track].i_toread = AVI_PTSToByte( tk, __ABS( i_dpts ) ); + } + else + { + toread[i_track].i_toread = AVI_PTSToChunk( tk, __ABS( i_dpts ) ); + } -/***************************************************************************** - * AVIEnd: frees unused data - *****************************************************************************/ -static void __AVIEnd ( vlc_object_t * p_this ) -{ - input_thread_t * p_input = (input_thread_t *)p_this; - unsigned int i; - demux_sys_t *p_avi = p_input->p_demux_data ; + if( i_dpts < 0 ) + { + toread[i_track].i_toread *= -1; + } + } + + b_stream = VLC_FALSE; - if( p_avi->pp_info ) + for( ;; ) { - for( i = 0; i < p_avi->i_streams; i++ ) + avi_track_t *tk; + vlc_bool_t b_done; + block_t *p_frame; + off_t i_pos; + unsigned int i; + size_t i_size; + + /* search for first chunk to be read */ + for( i = 0, b_done = VLC_TRUE, i_pos = -1; i < p_sys->i_track; i++ ) { - if( p_avi->pp_info[i] ) + if( !toread[i].b_ok || + AVI_GetDPTS( p_sys->track[i], + toread[i].i_toread ) <= -25 * 1000 ) + { + continue; + } + + if( toread[i].i_toread > 0 ) + { + b_done = VLC_FALSE; /* not yet finished */ + } + if( toread[i].i_posf > 0 ) { - if( p_avi->pp_info[i]->p_index ) + if( i_pos == -1 || i_pos > toread[i_track].i_posf ) { - free( p_avi->pp_info[i]->p_index ); + i_track = i; + i_pos = toread[i].i_posf; } - free( p_avi->pp_info[i] ); } } - free( p_avi->pp_info ); - } -#ifdef __AVI_SUBTITLE__ - if( p_avi->p_sub ) - { - subtitle_Close( p_avi->p_sub ); - p_avi->p_sub = NULL; - } -#endif - AVI_ChunkFreeRoot( p_input, &p_avi->ck_root ); - - FREE( p_input->p_demux_data ); -} - -/***************************************************************************** - * AVIInit: check file and initializes AVI structures - *****************************************************************************/ -static int AVIInit( vlc_object_t * p_this ) -{ - input_thread_t * p_input = (input_thread_t *)p_this; - avi_chunk_t ck_riff; - avi_chunk_list_t *p_riff = (avi_chunk_list_t*)&ck_riff; - avi_chunk_list_t *p_hdrl, *p_movi; -#if 0 - avi_chunk_list_t *p_INFO; - avi_chunk_strz_t *p_name; -#endif - avi_chunk_avih_t *p_avih; - demux_sys_t *p_avi; - es_descriptor_t *p_es = NULL; /* avoid warning */ - unsigned int i; -#ifdef __AVI_SUBTITLE__ - mtime_t i_microsecperframe = 0; // for some subtitle format -#endif - - vlc_bool_t b_stream_audio, b_stream_video; - p_input->pf_demux = AVIDemux_Seekable; - if( AVI_TestFile( p_input ) ) - { - msg_Warn( p_input, "avi module discarded (invalid headr)" ); - return VLC_EGENERIC; - } - - /* Initialize access plug-in structures. */ - if( p_input->i_mtu == 0 ) - { - /* Improve speed. */ - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE; - } - - if( !( p_input->p_demux_data = - p_avi = malloc( sizeof(demux_sys_t) ) ) ) - { - msg_Err( p_input, "out of memory" ); - return VLC_ENOMEM; - } - memset( p_avi, 0, sizeof( demux_sys_t ) ); - p_avi->i_time = 0; - p_avi->i_pcr = 0; - p_avi->b_seekable = ( ( p_input->stream.b_seekable ) - &&( p_input->stream.i_method == INPUT_METHOD_FILE ) ); - p_avi->i_movi_lastchunk_pos = 0; - - /* *** for unseekable stream, automaticaly use AVIDemux_interleaved *** */ - if( !p_avi->b_seekable || config_GetInt( p_input, "avi-interleaved" ) ) - { - p_input->pf_demux = AVIDemux_UnSeekable; - } + if( b_done ) + { + return( 1 ); + } - if( AVI_ChunkReadRoot( p_input, &p_avi->ck_root, p_avi->b_seekable ) ) - { - msg_Err( p_input, "avi module discarded (invalid file)" ); - return VLC_EGENERIC; - } - AVI_ChunkDumpDebug( p_input, &p_avi->ck_root ); + if( i_pos == -1 ) + { + int i_loop_count = 0; + /* no valid index, we will parse directly the stream + * in case we fail we will disable all finished stream */ + if( p_sys->i_movi_lastchunk_pos >= p_sys->i_movi_begin + 12 ) + { + stream_Seek( p_demux->s, p_sys->i_movi_lastchunk_pos ); + if( AVI_PacketNext( p_demux ) ) + { + return( AVI_TrackStopFinishedStreams( p_demux ) ? 0 : 1 ); + } + } + else + { + stream_Seek( p_demux->s, p_sys->i_movi_begin + 12 ); + } - p_riff = (avi_chunk_list_t*)AVI_ChunkFind( &p_avi->ck_root, - AVIFOURCC_RIFF, 0 ); - p_hdrl = (avi_chunk_list_t*)AVI_ChunkFind( p_riff, - AVIFOURCC_hdrl, 0 ); - p_movi = (avi_chunk_list_t*)AVI_ChunkFind( p_riff, - AVIFOURCC_movi, 0 ); -#if 0 - p_INFO = (avi_chunk_list_t*)AVI_ChunkFind( p_riff, - AVIFOURCC_INFO, 0 ); - p_name = (avi_chunk_strz_t*)AVI_ChunkFind( p_INFO, - AVIFOURCC_INAM, 0 ); - if( p_name ) - { + for( ;; ) + { + avi_packet_t avi_pk; - } -#endif + if( AVI_PacketGetHeader( p_demux, &avi_pk ) ) + { + msg_Warn( p_demux, + "cannot get packet header, track disabled" ); + return( AVI_TrackStopFinishedStreams( p_demux ) ? 0 : 1 ); + } + if( avi_pk.i_stream >= p_sys->i_track || + ( avi_pk.i_cat != AUDIO_ES && avi_pk.i_cat != VIDEO_ES ) ) + { + if( AVI_PacketNext( p_demux ) ) + { + msg_Warn( p_demux, + "cannot skip packet, track disabled" ); + return( AVI_TrackStopFinishedStreams( p_demux ) ? 0 : 1 ); + } - if( !p_hdrl || !p_movi ) - { - msg_Err( p_input, "avi module discarded (invalid file)" ); - return VLC_EGENERIC; - } + /* Prevents from eating all the CPU with broken files. + * This value should be low enough so that it doesn't + * affect the reading speed too much. */ + if( !(++i_loop_count % 1024) ) + { + if( p_demux->b_die ) return -1; + msleep( 10000 ); - if( !( p_avih = (avi_chunk_avih_t*)AVI_ChunkFind( p_hdrl, - AVIFOURCC_avih, 0 ) ) ) - { - msg_Err( p_input, "cannot find avih chunk" ); - return VLC_EGENERIC; - } - p_avi->i_streams = AVI_ChunkCount( p_hdrl, AVIFOURCC_strl ); - if( p_avih->i_streams != p_avi->i_streams ) - { - msg_Warn( p_input, - "found %d stream but %d are declared", - p_avi->i_streams, - p_avih->i_streams ); - } - if( p_avi->i_streams == 0 ) - { - AVIEnd( p_input ); - msg_Err( p_input, "no stream defined!" ); - return VLC_EGENERIC; - } + if( !(i_loop_count % (1024 * 10)) ) + msg_Warn( p_demux, + "don't seem to find any data..." ); + } + continue; + } + else + { + /* add this chunk to the index */ + avi_entry_t index; - /* 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 ); - AVIEnd( p_input ); - msg_Err( p_input, "cannot init stream" ); - return VLC_EGENERIC; - } - if( input_AddProgram( p_input, 0, 0) == NULL ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - AVIEnd( p_input ); - msg_Err( p_input, "cannot add program" ); - return VLC_EGENERIC; - } - p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + index.i_id = avi_pk.i_fourcc; + index.i_flags = + AVI_GetKeyFlag(p_sys->track[avi_pk.i_stream]->i_codec, + avi_pk.i_peek); + index.i_pos = avi_pk.i_pos; + index.i_length = avi_pk.i_size; + AVI_IndexAddEntry( p_sys, avi_pk.i_stream, &index ); - /* print informations on streams */ - msg_Dbg( p_input, "AVIH: %d stream, flags %s%s%s%s ", - p_avi->i_streams, - p_avih->i_flags&AVIF_HASINDEX?" HAS_INDEX":"", - p_avih->i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"", - p_avih->i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"", - p_avih->i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"" ); - { - input_info_category_t *p_cat = input_InfoCategory( p_input, "Avi" ); - input_AddInfo( p_cat, "Number of streams", "%d", p_avi->i_streams ); - input_AddInfo( p_cat, "Flags", "%s%s%s%s", - p_avih->i_flags&AVIF_HASINDEX?" HAS_INDEX":"", - p_avih->i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"", - p_avih->i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"", - p_avih->i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"" ); - } + i_track = avi_pk.i_stream; + tk = p_sys->track[i_track]; + /* do we will read this data ? */ + if( AVI_GetDPTS( tk, toread[i_track].i_toread ) > -25*1000 ) + { + break; + } + else + { + if( AVI_PacketNext( p_demux ) ) + { + msg_Warn( p_demux, + "cannot skip packet, track disabled" ); + return( AVI_TrackStopFinishedStreams( p_demux ) ? 0 : 1 ); + } + } + } + } - /* now read info on each stream and create ES */ - p_avi->pp_info = calloc( p_avi->i_streams, - sizeof( avi_stream_t* ) ); - memset( p_avi->pp_info, - 0, - sizeof( avi_stream_t* ) * p_avi->i_streams ); - - for( i = 0 ; i < p_avi->i_streams; i++ ) - { - avi_chunk_list_t *p_avi_strl; - avi_chunk_strh_t *p_avi_strh; - avi_chunk_strf_auds_t *p_avi_strf_auds; - avi_chunk_strf_vids_t *p_avi_strf_vids; - int i_init_size; - void *p_init_data; -#define p_info p_avi->pp_info[i] - p_info = malloc( sizeof(avi_stream_t ) ); - memset( p_info, 0, sizeof( avi_stream_t ) ); - - p_avi_strl = (avi_chunk_list_t*)AVI_ChunkFind( p_hdrl, - AVIFOURCC_strl, i ); - p_avi_strh = (avi_chunk_strh_t*)AVI_ChunkFind( p_avi_strl, - AVIFOURCC_strh, 0 ); - p_avi_strf_auds = (avi_chunk_strf_auds_t*) - p_avi_strf_vids = (avi_chunk_strf_vids_t*) - AVI_ChunkFind( p_avi_strl, AVIFOURCC_strf, 0 ); - - if( !p_avi_strl || !p_avi_strh || - ( !p_avi_strf_auds && !p_avi_strf_vids ) ) - { - msg_Warn( p_input, "stream[%d] incomlete", i ); - continue; + } + else + { + stream_Seek( p_demux->s, i_pos ); } - /* *** Init p_info *** */ - p_info->i_rate = p_avi_strh->i_rate; - p_info->i_scale = p_avi_strh->i_scale; - p_info->i_samplesize = p_avi_strh->i_samplesize; - msg_Dbg( p_input, "stream[%d] rate:%d scale:%d samplesize:%d", - i, - p_info->i_rate, p_info->i_scale, p_info->i_samplesize ); - switch( p_avi_strh->i_type ) + /* Set the track to use */ + tk = p_sys->track[i_track]; + + /* read thoses data */ + if( tk->i_samplesize ) { - case( AVIFOURCC_auds ): - p_info->i_cat = AUDIO_ES; - p_info->i_fourcc = - AVI_FourccGetCodec( AUDIO_ES, - p_avi_strf_auds->p_wf->wFormatTag ); - p_info->i_codec = p_info->i_fourcc; - i_init_size = p_avi_strf_auds->i_chunk_size; - p_init_data = p_avi_strf_auds->p_wf; - msg_Dbg( p_input, "stream[%d] audio(0x%x) %d channels %dHz %dbits", - i, - p_avi_strf_auds->p_wf->wFormatTag, - p_avi_strf_auds->p_wf->nChannels, - p_avi_strf_auds->p_wf->nSamplesPerSec, - p_avi_strf_auds->p_wf->wBitsPerSample ); - { - char hepp[sizeof("Stream") + 10]; - input_info_category_t *p_cat; - sprintf(hepp, "Stream %d", i); - p_cat = input_InfoCategory( p_input, hepp); - input_AddInfo( p_cat, "Type", "audio(0x%x)", - p_avi_strf_auds->p_wf->wFormatTag ); - input_AddInfo( p_cat, "Codec", "%4.4s", - (const char*)&(p_info->i_codec) ); - input_AddInfo( p_cat, "Channels", "%d", - p_avi_strf_auds->p_wf->nChannels ); - input_AddInfo( p_cat, "Samplerate", "%d", - p_avi_strf_auds->p_wf->nSamplesPerSec ); - input_AddInfo( p_cat, "Bits Per Sample", "%d", - p_avi_strf_auds->p_wf->wBitsPerSample ); - } - break; + unsigned int i_toread; - case( AVIFOURCC_vids ): - p_info->i_cat = VIDEO_ES; - /* XXX quick hack for playing ffmpeg video, I don't know - who is doing something wrong */ - p_info->i_samplesize = 0; - p_info->i_fourcc = p_avi_strf_vids->p_bih->biCompression; - p_info->i_codec = - AVI_FourccGetCodec( VIDEO_ES, p_info->i_fourcc ); - i_init_size = p_avi_strf_vids->i_chunk_size; - p_init_data = p_avi_strf_vids->p_bih; - msg_Dbg( p_input, "stream[%d] video(%4.4s) %dx%d %dbpp %ffps", - i, - (char*)&p_avi_strf_vids->p_bih->biCompression, - p_avi_strf_vids->p_bih->biWidth, - p_avi_strf_vids->p_bih->biHeight, - p_avi_strf_vids->p_bih->biBitCount, - (float)p_info->i_rate / - (float)p_info->i_scale ); - { - char hepp[sizeof("Stream") + 10]; - input_info_category_t *p_cat; - sprintf(hepp, "stream %d", i); - p_cat = input_InfoCategory( p_input, hepp); - input_AddInfo( p_cat, "Type", "video" ); - input_AddInfo( p_cat, "Codec", "%4.4s", - (const char*)&(p_info->i_codec) ); - input_AddInfo( p_cat, "Resolution", "%dx%d", - p_avi_strf_vids->p_bih->biWidth, - p_avi_strf_vids->p_bih->biHeight ); - input_AddInfo( p_cat, "Frame Rate", "%f", - (float)p_info->i_rate / - (float)p_info->i_scale ); - input_AddInfo( p_cat, "Bits Per Pixel", "%d", - p_avi_strf_vids->p_bih->biBitCount ); - } -#ifdef __AVI_SUBTITLE__ - if( i_microsecperframe == 0 ) + if( ( i_toread = toread[i_track].i_toread ) <= 0 ) + { + if( tk->i_samplesize > 1 ) { - i_microsecperframe = (mtime_t)1000000 * - (mtime_t)p_info->i_scale / - (mtime_t)p_info->i_rate; + i_toread = tk->i_samplesize; } -#endif - break; - default: - msg_Warn( p_input, "stream[%d] unknown type", i ); - p_info->i_cat = UNKNOWN_ES; - i_init_size = 0; - p_init_data = NULL; + else { - char psz_cat[32]; /* We'll clip i just in case */ - input_info_category_t *p_cat; - sprintf( psz_cat, "stream %d", __MIN( i, 100000 ) ); - p_cat = input_InfoCategory( p_input, psz_cat ); - input_AddInfo( p_cat, "Type", "unknown" ); + i_toread = AVI_PTSToByte( tk, 20 * 1000 ); + i_toread = __MAX( i_toread, 100 ); } - break; + } + i_size = __MIN( tk->p_index[tk->i_idxposc].i_length - + tk->i_idxposb, + i_toread ); } - p_info->b_activated = VLC_FALSE; - /* add one ES */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_info->p_es = - p_es = input_AddES( p_input, - p_input->stream.p_selected_program, 1+i, - 0 ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - p_es->i_stream_id =i; /* XXX: i don't use it */ - p_es->i_fourcc = p_info->i_fourcc; - p_es->i_cat = p_info->i_cat; - if( p_es->i_cat == AUDIO_ES ) + else { - p_es->p_waveformatex = malloc( i_init_size ); - memcpy( p_es->p_waveformatex, p_init_data, i_init_size ); + i_size = tk->p_index[tk->i_idxposc].i_length; } - else if( p_es->i_cat == VIDEO_ES ) + + if( tk->i_idxposb == 0 ) { - p_es->p_bitmapinfoheader = malloc( i_init_size ); - memcpy( p_es->p_bitmapinfoheader, p_init_data, i_init_size ); + i_size += 8; /* need to read and skip header */ } -#undef p_info - } - -#ifdef __AVI_SUBTITLE__ - if( ( p_avi->p_sub = subtitle_New( p_input, NULL, i_microsecperframe ) ) ) - { - subtitle_Select( p_avi->p_sub ); - } -#endif - if( config_GetInt( p_input, "avi-index" ) ) - { - if( p_avi->b_seekable ) + if( ( p_frame = stream_Block( p_demux->s, __EVEN( i_size ) ) )==NULL ) { - AVI_IndexCreate( p_input ); + msg_Warn( p_demux, "failled reading data" ); + tk->b_activated = VLC_FALSE; + toread[i_track].b_ok = VLC_FALSE; + continue; } - else + if( i_size % 2 ) /* read was padded on word boundary */ { - msg_Warn( p_input, "cannot create index (unseekable stream)" ); - AVI_IndexLoad( p_input ); + p_frame->i_buffer--; } - } - else - { - AVI_IndexLoad( p_input ); - } - - /* *** movie length in sec *** */ - p_avi->i_length = AVI_MovieGetLength( p_input, p_avi ); - if( p_avi->i_length < (mtime_t)p_avih->i_totalframes * - (mtime_t)p_avih->i_microsecperframe / - (mtime_t)1000000 ) - { - msg_Warn( p_input, "broken or missing index, 'seek' will be axproximative or will have strange behavour" ); - } - - /* fix some BeOS MediaKit generated file */ - for( i = 0 ; i < p_avi->i_streams; i++ ) - { - avi_chunk_list_t *p_avi_strl; - avi_chunk_strh_t *p_avi_strh; - avi_chunk_strf_auds_t *p_avi_strf_auds; -#define p_stream p_avi->pp_info[i] - - if( p_stream->i_cat != AUDIO_ES ) + /* skip header */ + if( tk->i_idxposb == 0 ) { - continue; + p_frame->p_buffer += 8; + p_frame->i_buffer -= 8; } - if( p_stream->i_idxnb < 1 || - p_stream->i_scale != 1 || - p_stream->i_samplesize != 0 ) + p_frame->i_pts = AVI_GetPTS( tk ) + 1; + if( tk->p_index[tk->i_idxposc].i_flags&AVIIF_KEYFRAME ) { - continue; + p_frame->i_flags = BLOCK_FLAG_TYPE_I; } - p_avi_strl = (avi_chunk_list_t*)AVI_ChunkFind( p_hdrl, - AVIFOURCC_strl, i ); - p_avi_strh = (avi_chunk_strh_t*)AVI_ChunkFind( p_avi_strl, - AVIFOURCC_strh, 0 ); - p_avi_strf_auds = - (avi_chunk_strf_auds_t*)AVI_ChunkFind( p_avi_strl, - AVIFOURCC_strf, 0 ); - - if( p_avi_strf_auds->p_wf->wFormatTag != WAVE_FORMAT_PCM && - (unsigned int)p_stream->i_rate == p_avi_strf_auds->p_wf->nSamplesPerSec ) + else { - int64_t i_track_length = - p_stream->p_index[p_stream->i_idxnb-1].i_length + - p_stream->p_index[p_stream->i_idxnb-1].i_lengthtotal; - mtime_t i_length = (mtime_t)p_avih->i_totalframes * - (mtime_t)p_avih->i_microsecperframe; + p_frame->i_flags = BLOCK_FLAG_TYPE_PB; + } - if( i_length == 0 ) + /* read data */ + if( tk->i_samplesize ) + { + if( tk->i_idxposb == 0 ) { - msg_Warn( p_input, "track[%d] cannot be fixed (BeOS MediaKit generated)", i ); - continue; + i_size -= 8; + } + toread[i_track].i_toread -= i_size; + tk->i_idxposb += i_size; + if( tk->i_idxposb >= + tk->p_index[tk->i_idxposc].i_length ) + { + tk->i_idxposb = 0; + tk->i_idxposc++; } - p_stream->i_samplesize = 1; - p_stream->i_rate = i_track_length * (int64_t)1000000/ i_length; - msg_Warn( p_input, "track[%d] fixed with rate=%d scale=%d (BeOS MediaKit generated)", i, p_stream->i_rate, p_stream->i_scale ); } -#undef p_stream - } - - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( p_avi->i_length ) - { - p_input->stream.i_mux_rate = - p_input->stream.p_selected_area->i_size / 50 / p_avi->i_length; - } - else - { - p_input->stream.i_mux_rate = 0; - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); + else + { + int i_length = tk->p_index[tk->i_idxposc].i_length; - b_stream_audio = VLC_FALSE; - b_stream_video = VLC_FALSE; + tk->i_idxposc++; + if( tk->i_cat == AUDIO_ES ) + { + tk->i_blockno += tk->i_blocksize > 0 ? ( i_length + tk->i_blocksize - 1 ) / tk->i_blocksize : 1; + } + toread[i_track].i_toread--; + } - for( i = 0; i < p_avi->i_streams; i++ ) - { -#define p_info p_avi->pp_info[i] - switch( p_info->p_es->i_cat ) + if( tk->i_idxposc < tk->i_idxnb) { - case( VIDEO_ES ): - - if( !b_stream_video ) - { - b_stream_video = AVI_StreamStart( p_input, p_avi, i ); - } - break; + toread[i_track].i_posf = + tk->p_index[tk->i_idxposc].i_pos; + if( tk->i_idxposb > 0 ) + { + toread[i_track].i_posf += 8 + tk->i_idxposb; + } - case( AUDIO_ES ): - if( !b_stream_audio ) - { - b_stream_audio = AVI_StreamStart( p_input, p_avi, i ); - } - break; - default: - break; } -#undef p_info - } + else + { + toread[i_track].i_posf = -1; + } - if( !b_stream_video ) - { - msg_Warn( p_input, "no video stream found" ); - } - if( !b_stream_audio ) - { - msg_Warn( p_input, "no audio stream found!" ); - } + b_stream = VLC_TRUE; /* at least one read succeed */ - 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 ); + if( tk->i_cat != VIDEO_ES ) + p_frame->i_dts = p_frame->i_pts; + else + { + p_frame->i_dts = p_frame->i_pts; + p_frame->i_pts = 0; + } - if( p_avi->b_seekable ) - { - AVI_ChunkGoto( p_input, p_movi ); - } - else - { - // already at begining of p_movi + //p_pes->i_rate = p_demux->stream.control.i_rate; + es_out_Send( p_demux->out, tk->p_es, p_frame ); } - AVI_SkipBytes( p_input, 12 ); // enter in p_movi - - p_avi->i_movi_begin = p_movi->i_chunk_pos; - return VLC_SUCCESS; } - - /***************************************************************************** - * Function to convert pts to chunk or byte + * Demux_UnSeekable: reads and demuxes data packets for unseekable file + ***************************************************************************** + * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ - -static inline mtime_t AVI_PTSToChunk( avi_stream_t *p_info, - mtime_t i_pts ) -{ - return (mtime_t)((int64_t)i_pts * - (int64_t)p_info->i_rate / - (int64_t)p_info->i_scale / - (int64_t)1000000 ); -} -static inline mtime_t AVI_PTSToByte( avi_stream_t *p_info, - mtime_t i_pts ) +static int Demux_UnSeekable( demux_t *p_demux ) { - return (mtime_t)((int64_t)i_pts * - (int64_t)p_info->i_rate / - (int64_t)p_info->i_scale / - (int64_t)1000000 * - (int64_t)p_info->i_samplesize ); -} + demux_sys_t *p_sys = p_demux->p_sys; + avi_track_t *p_stream_master = NULL; + unsigned int i_stream; + unsigned int i_packet; -static mtime_t AVI_GetDPTS( avi_stream_t *p_stream, int i_count ) -{ - if( p_stream->i_samplesize ) - { - return (mtime_t)( (int64_t)1000000 * - (int64_t)i_count * - (int64_t)p_stream->i_scale / - (int64_t)p_stream->i_rate / - (int64_t)p_stream->i_samplesize ); - } - else - { - return (mtime_t)( (int64_t)1000000 * - (int64_t)i_count * - (int64_t)p_stream->i_scale / - (int64_t)p_stream->i_rate); - } + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_time + 1 ); -} + /* *** find master stream for data packet skipping algo *** */ + /* *** -> first video, if any, or first audio ES *** */ + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + avi_track_t *tk = p_sys->track[i_stream]; + vlc_bool_t b; -static mtime_t AVI_GetPTS( avi_stream_t *p_info ) -{ + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); - if( p_info->i_samplesize ) - { - /* we need a valid entry we will emulate one */ - int64_t i_len; - if( p_info->i_idxposc == p_info->i_idxnb ) + if( b && tk->i_cat == VIDEO_ES ) { - if( p_info->i_idxposc ) - { - /* use the last entry */ - i_len = p_info->p_index[p_info->i_idxnb - 1].i_lengthtotal - + p_info->p_index[p_info->i_idxnb - 1].i_length - + p_info->i_idxposb; /* should be 0 */ - } - else - { - i_len = p_info->i_idxposb; - /* no valid entry use only offset*/ - } + p_stream_master = tk; } - else + else if( b ) { - i_len = p_info->p_index[p_info->i_idxposc].i_lengthtotal - + p_info->i_idxposb; + p_stream_master = tk; } - return (mtime_t)( (int64_t)1000000 * - (int64_t)i_len * - (int64_t)p_info->i_scale / - (int64_t)p_info->i_rate / - (int64_t)p_info->i_samplesize ); } - else + + if( !p_stream_master ) { - /* even if p_info->i_idxposc isn't valid, there isn't any problem */ - return (mtime_t)( (int64_t)1000000 * - (int64_t)(p_info->i_idxposc ) * - (int64_t)p_info->i_scale / - (int64_t)p_info->i_rate); + msg_Warn( p_demux, "no more stream selected" ); + return( 0 ); } -} -static int AVI_StreamChunkFind( input_thread_t *p_input, - unsigned int i_stream ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; - avi_packet_t avi_pk; - - /* find first chunk of i_stream that isn't in index */ + p_sys->i_time = AVI_GetPTS( p_stream_master ); - if( p_avi->i_movi_lastchunk_pos >= p_avi->i_movi_begin ) - { - AVI_SeekAbsolute( p_input, p_avi->i_movi_lastchunk_pos ); - if( AVI_PacketNext( p_input ) ) - { - return VLC_EGENERIC; - } - } - else + for( i_packet = 0; i_packet < 10; i_packet++) { - AVI_SeekAbsolute( p_input, p_avi->i_movi_begin ); - } +#define p_stream p_sys->track[avi_pk.i_stream] - for( ;; ) - { + avi_packet_t avi_pk; - if( AVI_PacketGetHeader( p_input, &avi_pk ) ) + if( AVI_PacketGetHeader( p_demux, &avi_pk ) ) { - msg_Warn( p_input, "cannot get packet header" ); - return VLC_EGENERIC; + return( 0 ); } - if( avi_pk.i_stream >= p_avi->i_streams || + + if( avi_pk.i_stream >= p_sys->i_track || ( avi_pk.i_cat != AUDIO_ES && avi_pk.i_cat != VIDEO_ES ) ) { + /* we haven't found an audio or video packet: + * - we have seek, found first next packet + * - others packets could be found, skip them + */ switch( avi_pk.i_fourcc ) { + case AVIFOURCC_JUNK: case AVIFOURCC_LIST: - AVI_SkipBytes( p_input, 12 ); - break; + case AVIFOURCC_RIFF: + return( !AVI_PacketNext( p_demux ) ? 1 : 0 ); + case AVIFOURCC_idx1: + if( p_sys->b_odml ) + { + return( !AVI_PacketNext( p_demux ) ? 1 : 0 ); + } + return( 0 ); /* eof */ default: - if( AVI_PacketNext( p_input ) ) + msg_Warn( p_demux, + "seems to have lost position, resync" ); + if( AVI_PacketSearch( p_demux ) ) { - return VLC_EGENERIC; + msg_Err( p_demux, "resync failed" ); + return( -1 ); } - break; } } else { - /* add this chunk to the index */ - AVIIndexEntry_t index; - - index.i_id = avi_pk.i_fourcc; - index.i_flags = - AVI_GetKeyFlag(p_avi->pp_info[avi_pk.i_stream]->i_codec, - avi_pk.i_peek); - index.i_pos = avi_pk.i_pos; - index.i_length = avi_pk.i_size; - AVI_IndexAddEntry( p_avi, avi_pk.i_stream, &index ); - - if( avi_pk.i_stream == i_stream ) - { - return VLC_SUCCESS; - } - - if( AVI_PacketNext( p_input ) ) - { - return VLC_EGENERIC; - } - } - } -} - - -/* be sure that i_ck will be a valid index entry */ -static int AVI_SetStreamChunk( input_thread_t *p_input, - unsigned int i_stream, - unsigned int i_ck ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; - avi_stream_t *p_stream = p_avi->pp_info[i_stream]; - - p_stream->i_idxposc = i_ck; - p_stream->i_idxposb = 0; - - if( i_ck >= p_stream->i_idxnb ) - { - p_stream->i_idxposc = p_stream->i_idxnb - 1; - do - { - p_stream->i_idxposc++; - if( AVI_StreamChunkFind( p_input, i_stream ) ) - { - return VLC_EGENERIC; - } - - } while( p_stream->i_idxposc < i_ck ); - } - - return VLC_SUCCESS; -} - - -/* XXX FIXME up to now, we assume that all chunk are one after one */ -static int AVI_SetStreamBytes( input_thread_t *p_input, - unsigned int i_stream, - off_t i_byte ) -{ - demux_sys_t *p_avi = p_input->p_demux_data; - avi_stream_t *p_stream = p_avi->pp_info[i_stream]; - - if( ( p_stream->i_idxnb > 0 ) - &&( i_byte < p_stream->p_index[p_stream->i_idxnb - 1].i_lengthtotal + - p_stream->p_index[p_stream->i_idxnb - 1].i_length ) ) - { - /* index is valid to find the ck */ - /* uses dichototmie to be fast enougth */ - int i_idxposc = __MIN( p_stream->i_idxposc, p_stream->i_idxnb - 1 ); - int i_idxmax = p_stream->i_idxnb; - int i_idxmin = 0; - for( ;; ) - { - if( p_stream->p_index[i_idxposc].i_lengthtotal > i_byte ) - { - i_idxmax = i_idxposc ; - i_idxposc = ( i_idxmin + i_idxposc ) / 2 ; - } - else + /* check for time */ + if( __ABS( AVI_GetPTS( p_stream ) - + AVI_GetPTS( p_stream_master ) )< 600*1000 ) { - if( p_stream->p_index[i_idxposc].i_lengthtotal + - p_stream->p_index[i_idxposc].i_length <= i_byte) + /* load it and send to decoder */ + block_t *p_frame; + if( AVI_PacketRead( p_demux, &avi_pk, &p_frame ) || p_frame == NULL ) { - i_idxmin = i_idxposc ; - i_idxposc = (i_idxmax + i_idxposc ) / 2 ; + return( -1 ); } + p_frame->i_pts = AVI_GetPTS( p_stream ) + 1; + + if( avi_pk.i_cat != VIDEO_ES ) + p_frame->i_dts = p_frame->i_pts; else { - p_stream->i_idxposc = i_idxposc; - p_stream->i_idxposb = i_byte - - p_stream->p_index[i_idxposc].i_lengthtotal; - return VLC_SUCCESS; + p_frame->i_dts = p_frame->i_pts; + p_frame->i_pts = 0; } - } - } - } - else - { - p_stream->i_idxposc = p_stream->i_idxnb - 1; - p_stream->i_idxposb = 0; - do - { - p_stream->i_idxposc++; - if( AVI_StreamChunkFind( p_input, i_stream ) ) - { - return VLC_EGENERIC; + //p_pes->i_rate = p_demux->stream.control.i_rate; + es_out_Send( p_demux->out, p_stream->p_es, p_frame ); } - - } while( p_stream->p_index[p_stream->i_idxposc].i_lengthtotal + - p_stream->p_index[p_stream->i_idxposc].i_length <= i_byte ); - - p_stream->i_idxposb = i_byte - - p_stream->p_index[p_stream->i_idxposc].i_lengthtotal; - return VLC_SUCCESS; - } -} - -static int AVI_StreamSeek( input_thread_t *p_input, - demux_sys_t *p_avi, - int i_stream, - mtime_t i_date ) -{ -#define p_stream p_avi->pp_info[i_stream] - mtime_t i_oldpts; - - i_oldpts = AVI_GetPTS( p_stream ); - - if( !p_stream->i_samplesize ) - { - if( AVI_SetStreamChunk( p_input, - i_stream, - AVI_PTSToChunk( p_stream, i_date ) ) ) - { - return VLC_EGENERIC; - } - - /* search key frame */ - msg_Dbg( p_input, - "old:"I64Fd" %s new "I64Fd, - i_oldpts, - i_oldpts > i_date ? ">" : "<", - i_date ); - - if( i_date < i_oldpts ) - { - while( p_stream->i_idxposc > 0 && - !( p_stream->p_index[p_stream->i_idxposc].i_flags & - AVIIF_KEYFRAME ) ) + else { - if( AVI_SetStreamChunk( p_input, - i_stream, - p_stream->i_idxposc - 1 ) ) + if( AVI_PacketNext( p_demux ) ) { - return VLC_EGENERIC; + return( 0 ); } } - } - else - { - while( p_stream->i_idxposc < p_stream->i_idxnb && - !( p_stream->p_index[p_stream->i_idxposc].i_flags & - AVIIF_KEYFRAME ) ) + + /* *** update stream time position *** */ + if( p_stream->i_samplesize ) { - if( AVI_SetStreamChunk( p_input, - i_stream, - p_stream->i_idxposc + 1 ) ) + p_stream->i_idxposb += avi_pk.i_size; + } + else + { + if( p_stream->i_cat == AUDIO_ES ) { - return VLC_EGENERIC; + p_stream->i_blockno += p_stream->i_blocksize > 0 ? ( avi_pk.i_size + p_stream->i_blocksize - 1 ) / p_stream->i_blocksize : 1; } + p_stream->i_idxposc++; } + } - } - else - { - if( AVI_SetStreamBytes( p_input, - i_stream, - AVI_PTSToByte( p_stream, i_date ) ) ) - { - return VLC_EGENERIC; - } - } - return VLC_SUCCESS; #undef p_stream + } + + return( 1 ); } /***************************************************************************** - * AVISeek: goto to i_date or i_percent - ***************************************************************************** - * Returns -1 in case of error, 0 in case of EOF, 1 otherwise + * Seek: goto to i_date or i_percent *****************************************************************************/ -static int AVISeek ( input_thread_t *p_input, - mtime_t i_date, int i_percent ) +static int Seek( demux_t *p_demux, mtime_t i_date, int i_percent ) { - demux_sys_t *p_avi = p_input->p_demux_data; + demux_sys_t *p_sys = p_demux->p_sys; unsigned int i_stream; - msg_Dbg( p_input, - "seek requested: "I64Fd" secondes %d%%", - i_date / 1000000, - i_percent ); + msg_Dbg( p_demux, "seek requested: "I64Fd" secondes %d%%", + i_date / 1000000, i_percent ); - if( p_avi->b_seekable ) + if( p_sys->b_seekable ) { - if( !p_avi->i_length ) + if( !p_sys->i_length ) { - avi_stream_t *p_stream; - uint64_t i_pos; + avi_track_t *p_stream; + int64_t i_pos; /* use i_percent to create a true i_date */ - msg_Warn( p_input, - "mmh, seeking without index at %d%%" + msg_Warn( p_demux, "mmh, seeking without index at %d%%" " work only for interleaved file", i_percent ); - if( i_percent >= 100 ) { - msg_Warn( p_input, "cannot seek so far !" ); - return( -1 ); + msg_Warn( p_demux, "cannot seek so far !" ); + return VLC_EGENERIC; } i_percent = __MAX( i_percent, 0 ); /* try to find chunk that is at i_percent or the file */ - i_pos = __MAX( i_percent * - p_input->stream.p_selected_area->i_size / 100, - p_avi->i_movi_begin ); + i_pos = __MAX( i_percent * stream_Size( p_demux->s ) / 100, + p_sys->i_movi_begin ); /* search first selected stream */ for( i_stream = 0, p_stream = NULL; - i_stream < p_avi->i_streams; i_stream++ ) + i_stream < p_sys->i_track; i_stream++ ) { - p_stream = p_avi->pp_info[i_stream]; + p_stream = p_sys->track[i_stream]; if( p_stream->b_activated ) { break; @@ -1608,658 +1132,1205 @@ static int AVISeek ( input_thread_t *p_input, } if( !p_stream || !p_stream->b_activated ) { - msg_Warn( p_input, "cannot find any selected stream" ); - return( -1 ); + msg_Warn( p_demux, "cannot find any selected stream" ); + return VLC_EGENERIC; } - /* be sure that the index exit */ - if( AVI_SetStreamChunk( p_input, - i_stream, - 0 ) ) + /* be sure that the index exist */ + if( AVI_StreamChunkSet( p_demux, i_stream, 0 ) ) { - msg_Warn( p_input, "cannot seek" ); - return( -1 ); + msg_Warn( p_demux, "cannot seek" ); + return VLC_EGENERIC; } while( i_pos >= p_stream->p_index[p_stream->i_idxposc].i_pos + p_stream->p_index[p_stream->i_idxposc].i_length + 8 ) { /* search after i_idxposc */ - if( AVI_SetStreamChunk( p_input, + if( AVI_StreamChunkSet( p_demux, i_stream, p_stream->i_idxposc + 1 ) ) { - msg_Warn( p_input, "cannot seek" ); - return( -1 ); + msg_Warn( p_demux, "cannot seek" ); + return VLC_EGENERIC; } } + i_date = AVI_GetPTS( p_stream ); /* TODO better support for i_samplesize != 0 */ - msg_Dbg( p_input, "estimate date "I64Fd, i_date ); + msg_Dbg( p_demux, "estimate date "I64Fd, i_date ); } -#define p_stream p_avi->pp_info[i_stream] - p_avi->i_time = 0; +#define p_stream p_sys->track[i_stream] + p_sys->i_time = 0; /* seek for chunk based streams */ - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) { if( p_stream->b_activated && !p_stream->i_samplesize ) -// if( p_stream->b_activated ) +/* if( p_stream->b_activated ) */ { - AVI_StreamSeek( p_input, p_avi, i_stream, i_date ); - p_avi->i_time = __MAX( AVI_GetPTS( p_stream ), - p_avi->i_time ); + AVI_TrackSeek( p_demux, i_stream, i_date ); + p_sys->i_time = __MAX( AVI_GetPTS( p_stream ), + p_sys->i_time ); } } #if 1 - if( p_avi->i_time ) + if( p_sys->i_time ) { - i_date = p_avi->i_time; + i_date = p_sys->i_time; } /* seek for bytes based streams */ - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) { if( p_stream->b_activated && p_stream->i_samplesize ) { - AVI_StreamSeek( p_input, p_avi, i_stream, i_date ); -// p_avi->i_time = __MAX( AVI_GetPTS( p_stream ), p_avi->i_time ); + AVI_TrackSeek( p_demux, i_stream, i_date ); +/* p_sys->i_time = __MAX( AVI_GetPTS( p_stream ), p_sys->i_time );*/ } } - msg_Dbg( p_input, "seek: "I64Fd" secondes", p_avi->i_time /1000000 ); + msg_Dbg( p_demux, "seek: "I64Fd" seconds", p_sys->i_time /1000000 ); /* set true movie time */ #endif - if( !p_avi->i_time ) + if( !p_sys->i_time ) { - p_avi->i_time = i_date; + p_sys->i_time = i_date; } #undef p_stream - return( 1 ); + return VLC_SUCCESS; } else { - msg_Err( p_input, "shouldn't yet be executed" ); - return( -1 ); + msg_Err( p_demux, "shouldn't yet be executed" ); + return VLC_EGENERIC; } } -#if 0 -static pes_packet_t *PES_split( input_thread_t *p_input, avi_stream_t *p_stream, pes_packet_t *p_pes ) -{ - pes_packet_t *p_pes2; - data_packet_t *p_data; - int i_nb_data; - - if( p_pes->i_nb_data < 2 ) - { - return( NULL ); - } - p_pes2 = input_NewPES( p_input->p_method_data ); - p_pes2->i_pts = p_pes->i_pts; - p_pes2->i_dts = p_pes->i_dts; - p_pes2->i_nb_data = p_pes->i_nb_data/2; - p_pes2->i_pes_size = 0; - for( i_nb_data = 0, p_data = p_pes->p_first; - i_nb_data < p_pes2->i_nb_data; - i_nb_data++, p_data = p_data->p_next ) - { - p_pes2->i_pes_size += - p_data->p_payload_end - p_data->p_payload_start; - p_pes2->p_last = p_data; - } - p_pes2->p_first = p_pes->p_first; - p_pes2->p_last->p_next = NULL; - - p_pes->p_first = p_data; - p_pes->i_pes_size -= p_pes2->i_pes_size; - p_pes->i_nb_data -= p_pes2->i_nb_data; -// p_pes->i_pts += AVI_GetDPTS( p_stream, p_pes2->i_pes_size ); -// p_pes->i_dts += AVI_GetDPTS( p_stream, p_pes2->i_pes_size ); - p_pes->i_pts = 0; - p_pes->i_dts = 0; - return( p_pes2 ); -} -#endif - /***************************************************************************** - * AVIDemux_Seekable: reads and demuxes data packets for stream seekable - ***************************************************************************** - * AVIDemux: reads and demuxes data packets + * Control: ***************************************************************************** - * Returns -1 in case of error, 0 in case of EOF, 1 otherwise + * *****************************************************************************/ -typedef struct avi_stream_toread_s +static double ControlGetPosition( demux_t *p_demux ) { - vlc_bool_t b_ok; + demux_sys_t *p_sys = p_demux->p_sys; - int i_toread; + if( p_sys->i_length > 0 ) + { + return (double)p_sys->i_time / (double)( p_sys->i_length * (mtime_t)1000000 ); + } + else if( stream_Size( p_demux->s ) > 0 ) + { + unsigned int i; + int64_t i_tmp; + int64_t i64 = 0; - off_t i_posf; // where we will read : - // if i_idxposb == 0 : begining of chunk (+8 to acces data) - // else : point on data directly -} avi_stream_toread_t; + /* search the more advanced selected es */ + for( i = 0; i < p_sys->i_track; i++ ) + { + avi_track_t *tk = p_sys->track[i]; + if( tk->b_activated && tk->i_idxposc < tk->i_idxnb ) + { + i_tmp = tk->p_index[tk->i_idxposc].i_pos + + tk->p_index[tk->i_idxposc].i_length + 8; + if( i_tmp > i64 ) + { + i64 = i_tmp; + } + } + } + return (double)i64 / (double)stream_Size( p_demux->s ); + } + return 0.0; +} -static int AVIDemux_Seekable( input_thread_t *p_input ) +static int Control( demux_t *p_demux, int i_query, va_list args ) { - unsigned int i_stream_count; - unsigned int i_stream; - vlc_bool_t b_stream; - vlc_bool_t b_play_audio; - vlc_bool_t b_video; /* is there some video track selected */ - // cannot be more than 100 stream (dcXX or wbXX) - avi_stream_toread_t toread[100]; - - demux_sys_t *p_avi = p_input->p_demux_data; + demux_sys_t *p_sys = p_demux->p_sys; + int i; + double f, *pf; + int64_t i64, *pi64; + vlc_meta_t **pp_meta; - - /* detect new selected/unselected streams */ - for( i_stream = 0,i_stream_count= 0, b_video = VLC_FALSE; - i_stream < p_avi->i_streams; i_stream++ ) + switch( i_query ) { -#define p_stream p_avi->pp_info[i_stream] - if( p_stream->p_es ) - { - if( p_stream->p_es->p_decoder_fifo && - !p_stream->b_activated ) + case DEMUX_GET_POSITION: + pf = (double*)va_arg( args, double * ); + *pf = ControlGetPosition( p_demux ); + return VLC_SUCCESS; + case DEMUX_SET_POSITION: + f = (double)va_arg( args, double ); + if( p_sys->b_seekable ) { - AVI_StreamStart( p_input, p_avi, i_stream ); + i64 = (mtime_t)(1000000.0 * p_sys->i_length * f ); + return Seek( p_demux, i64, (int)(f * 100) ); } else - if( !p_stream->p_es->p_decoder_fifo && - p_stream->b_activated ) { - AVI_StreamStop( p_input, p_avi, i_stream ); + int64_t i_pos = stream_Size( p_demux->s ) * f; + return stream_Seek( p_demux->s, i_pos ); } - } - if( p_stream->b_activated ) + + case DEMUX_GET_TIME: + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_time; + return VLC_SUCCESS; + + case DEMUX_SET_TIME: { - i_stream_count++; - if( p_stream->i_cat == VIDEO_ES ) + int i_percent = 0; + + i64 = (int64_t)va_arg( args, int64_t ); + if( p_sys->i_length > 0 ) + { + i_percent = 100 * i64 / (p_sys->i_length*1000000); + } + else if( p_sys->i_time > 0 ) { - b_video = VLC_TRUE; + i_percent = (int)( 100.0 * ControlGetPosition( p_demux ) * + (double)i64 / (double)p_sys->i_time ); } + return Seek( p_demux, i64, i_percent ); } -#undef p_stream - } + case DEMUX_GET_LENGTH: + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_length * (mtime_t)1000000; + return VLC_SUCCESS; - if( i_stream_count <= 0 ) - { - msg_Warn( p_input, "no track selected, exiting..." ); - return( 0 ); + case DEMUX_GET_FPS: + pf = (double*)va_arg( args, double * ); + *pf = 0.0; + for( i = 0; i < (int)p_sys->i_track; i++ ) + { + avi_track_t *tk = p_sys->track[i]; + if( tk->i_cat == VIDEO_ES && tk->i_scale > 0) + { + *pf = (float)tk->i_rate / (float)tk->i_scale; + break; + } + } + 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; + + default: + return VLC_EGENERIC; } +} - if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT ) - { - mtime_t i_date; - int i_percent; - /* first wait for empty buffer, arbitrary time FIXME */ - //msleep( DEFAULT_PTS_DELAY ); +/***************************************************************************** + * Function to convert pts to chunk or byte + *****************************************************************************/ - i_date = (mtime_t)1000000 * - (mtime_t)p_avi->i_length * - (mtime_t)AVI_TellAbsolute( p_input ) / - (mtime_t)p_input->stream.p_selected_area->i_size; - i_percent = 100 * AVI_TellAbsolute( p_input ) / - p_input->stream.p_selected_area->i_size; +static mtime_t AVI_PTSToChunk( avi_track_t *tk, mtime_t i_pts ) +{ + return (mtime_t)((int64_t)i_pts * + (int64_t)tk->i_rate / + (int64_t)tk->i_scale / + (int64_t)1000000 ); +} +static mtime_t AVI_PTSToByte( avi_track_t *tk, mtime_t i_pts ) +{ + return (mtime_t)((int64_t)i_pts * + (int64_t)tk->i_rate / + (int64_t)tk->i_scale / + (int64_t)1000000 * + (int64_t)tk->i_samplesize ); +} -// input_ClockInit( p_input->stream.p_selected_program ); - AVISeek( p_input, i_date, i_percent); +static mtime_t AVI_GetDPTS( avi_track_t *tk, int64_t i_count ) +{ + mtime_t i_dpts; -#ifdef __AVI_SUBTITLE__ - if( p_avi->p_sub ) - { - subtitle_Seek( p_avi->p_sub, p_avi->i_time ); - } -#endif - } - - - /* wait for the good time */ - - p_avi->i_pcr = p_avi->i_time * 9 / 100; - - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_avi->i_pcr ); + i_dpts = (mtime_t)( (int64_t)1000000 * + (int64_t)i_count * + (int64_t)tk->i_scale / + (int64_t)tk->i_rate ); + if( tk->i_samplesize ) + { + return i_dpts / tk->i_samplesize; + } + return i_dpts; +} - p_avi->i_time += 25*1000; /* read 25ms */ +static mtime_t AVI_GetPTS( avi_track_t *tk ) +{ + if( tk->i_samplesize ) + { + int64_t i_count = 0; -#ifdef __AVI_SUBTITLE__ - if( p_avi->p_sub ) + /* we need a valid entry we will emulate one */ + if( tk->i_idxposc == tk->i_idxnb ) + { + if( tk->i_idxposc ) + { + /* use the last entry */ + i_count = tk->p_index[tk->i_idxnb - 1].i_lengthtotal + + tk->p_index[tk->i_idxnb - 1].i_length; + } + } + else + { + i_count = tk->p_index[tk->i_idxposc].i_lengthtotal; + } + return AVI_GetDPTS( tk, i_count + tk->i_idxposb ); + } + else { - subtitle_Demux( p_avi->p_sub, p_avi->i_time ); + if( tk->i_cat == AUDIO_ES ) + { + return AVI_GetDPTS( tk, tk->i_blockno ); + } + else + { + return AVI_GetDPTS( tk, tk->i_idxposc ); + } } -#endif +} + +static int AVI_StreamChunkFind( demux_t *p_demux, unsigned int i_stream ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_packet_t avi_pk; + int i_loop_count = 0; - /* *** send audio data to decoder if rate == DEFAULT_RATE or no video *** */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( p_input->stream.control.i_rate == DEFAULT_RATE || !b_video ) + /* find first chunk of i_stream that isn't in index */ + + if( p_sys->i_movi_lastchunk_pos >= p_sys->i_movi_begin + 12 ) { - b_play_audio = VLC_TRUE; + stream_Seek( p_demux->s, p_sys->i_movi_lastchunk_pos ); + if( AVI_PacketNext( p_demux ) ) + { + return VLC_EGENERIC; + } } else { - b_play_audio = VLC_FALSE; + stream_Seek( p_demux->s, p_sys->i_movi_begin + 12 ); } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - /* init toread */ - for( i_stream = 0; i_stream < p_avi->i_streams; i_stream++ ) + for( ;; ) { -#define p_stream p_avi->pp_info[i_stream] - mtime_t i_dpts; + if( p_demux->b_die ) return VLC_EGENERIC; - toread[i_stream].b_ok = p_stream->b_activated; - if( p_stream->i_idxposc < p_stream->i_idxnb ) + if( AVI_PacketGetHeader( p_demux, &avi_pk ) ) { - toread[i_stream].i_posf = - p_stream->p_index[p_stream->i_idxposc].i_pos; - if( p_stream->i_idxposb > 0 ) - { - toread[i_stream].i_posf += 8 + p_stream->i_idxposb; - } + msg_Warn( p_demux, "cannot get packet header" ); + return VLC_EGENERIC; } - else + if( avi_pk.i_stream >= p_sys->i_track || + ( avi_pk.i_cat != AUDIO_ES && avi_pk.i_cat != VIDEO_ES ) ) { - toread[i_stream].i_posf = -1; - } + if( AVI_PacketNext( p_demux ) ) + { + return VLC_EGENERIC; + } - i_dpts = p_avi->i_time - AVI_GetPTS( p_stream ); + /* Prevents from eating all the CPU with broken files. + * This value should be low enough so that it doesn't + * affect the reading speed too much. */ + if( !(++i_loop_count % 1024) ) + { + if( p_demux->b_die ) return VLC_EGENERIC; + msleep( 10000 ); - if( p_stream->i_samplesize ) - { - toread[i_stream].i_toread = AVI_PTSToByte( p_stream, - __ABS( i_dpts ) ); + if( !(i_loop_count % (1024 * 10)) ) + msg_Warn( p_demux, "don't seem to find any data..." ); + } } else { - toread[i_stream].i_toread = AVI_PTSToChunk( p_stream, - __ABS( i_dpts ) ); - } + /* add this chunk to the index */ + avi_entry_t index; - if( i_dpts < 0 ) - { - toread[i_stream].i_toread *= -1; + index.i_id = avi_pk.i_fourcc; + index.i_flags = + AVI_GetKeyFlag(p_sys->track[avi_pk.i_stream]->i_codec, + avi_pk.i_peek); + index.i_pos = avi_pk.i_pos; + index.i_length = avi_pk.i_size; + AVI_IndexAddEntry( p_sys, avi_pk.i_stream, &index ); + + if( avi_pk.i_stream == i_stream ) + { + return VLC_SUCCESS; + } + + if( AVI_PacketNext( p_demux ) ) + { + return VLC_EGENERIC; + } } -#undef p_stream } +} - b_stream = VLC_FALSE; - for( ;; ) - { -#define p_stream p_avi->pp_info[i_stream] - vlc_bool_t b_done; - pes_packet_t *p_pes; - off_t i_pos; - unsigned int i; - size_t i_size; +/* be sure that i_ck will be a valid index entry */ +static int AVI_StreamChunkSet( demux_t *p_demux, unsigned int i_stream, + unsigned int i_ck ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_track_t *p_stream = p_sys->track[i_stream]; - /* search for first chunk to be read */ - for( i = 0, b_done = VLC_TRUE, i_pos = -1; i < p_avi->i_streams; i++ ) + p_stream->i_idxposc = i_ck; + p_stream->i_idxposb = 0; + + if( i_ck >= p_stream->i_idxnb ) + { + p_stream->i_idxposc = p_stream->i_idxnb - 1; + do { - if( !toread[i].b_ok || - AVI_GetDPTS( p_avi->pp_info[i], - toread[i].i_toread ) <= -25 * 1000 ) + p_stream->i_idxposc++; + if( AVI_StreamChunkFind( p_demux, i_stream ) ) { - continue; + return VLC_EGENERIC; } - if( toread[i].i_toread > 0 ) + } while( p_stream->i_idxposc < i_ck ); + } + + return VLC_SUCCESS; +} + + +/* XXX FIXME up to now, we assume that all chunk are one after one */ +static int AVI_StreamBytesSet( demux_t *p_demux, + unsigned int i_stream, + off_t i_byte ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_track_t *p_stream = p_sys->track[i_stream]; + + if( ( p_stream->i_idxnb > 0 ) + &&( i_byte < p_stream->p_index[p_stream->i_idxnb - 1].i_lengthtotal + + p_stream->p_index[p_stream->i_idxnb - 1].i_length ) ) + { + /* index is valid to find the ck */ + /* uses dichototmie to be fast enougth */ + int i_idxposc = __MIN( p_stream->i_idxposc, p_stream->i_idxnb - 1 ); + int i_idxmax = p_stream->i_idxnb; + int i_idxmin = 0; + for( ;; ) + { + if( p_stream->p_index[i_idxposc].i_lengthtotal > i_byte ) { - b_done = VLC_FALSE; // not yet finished + i_idxmax = i_idxposc ; + i_idxposc = ( i_idxmin + i_idxposc ) / 2 ; } - - if( toread[i].i_posf > 0 ) + else { - if( i_pos == -1 || i_pos > toread[i_stream].i_posf ) + if( p_stream->p_index[i_idxposc].i_lengthtotal + + p_stream->p_index[i_idxposc].i_length <= i_byte) { - i_stream = i; - i_pos = toread[i].i_posf; + i_idxmin = i_idxposc ; + i_idxposc = (i_idxmax + i_idxposc ) / 2 ; + } + else + { + p_stream->i_idxposc = i_idxposc; + p_stream->i_idxposb = i_byte - + p_stream->p_index[i_idxposc].i_lengthtotal; + return VLC_SUCCESS; } } } - if( b_done ) + } + else + { + p_stream->i_idxposc = p_stream->i_idxnb - 1; + p_stream->i_idxposb = 0; + do { -// return( b_stream ? 1 : 0 ); - return( 1 ); + p_stream->i_idxposc++; + if( AVI_StreamChunkFind( p_demux, i_stream ) ) + { + return VLC_EGENERIC; + } + + } while( p_stream->p_index[p_stream->i_idxposc].i_lengthtotal + + p_stream->p_index[p_stream->i_idxposc].i_length <= i_byte ); + + p_stream->i_idxposb = i_byte - + p_stream->p_index[p_stream->i_idxposc].i_lengthtotal; + return VLC_SUCCESS; + } +} + +static int AVI_TrackSeek( demux_t *p_demux, + int i_stream, + mtime_t i_date ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_track_t *tk = p_sys->track[i_stream]; + +#define p_stream p_sys->track[i_stream] + mtime_t i_oldpts; + + i_oldpts = AVI_GetPTS( p_stream ); + + if( !p_stream->i_samplesize ) + { + if( AVI_StreamChunkSet( p_demux, + i_stream, + AVI_PTSToChunk( p_stream, i_date ) ) ) + { + return VLC_EGENERIC; } - if( i_pos == -1 ) + if( p_stream->i_cat == AUDIO_ES ) { - /* no valid index, we will parse directly the stream - * in case we fail we will disable all finished stream */ - if( p_avi->i_movi_lastchunk_pos >= p_avi->i_movi_begin ) + unsigned int i; + tk->i_blockno = 0; + for( i = 0; i < tk->i_idxposc; i++ ) { - AVI_SeekAbsolute( p_input, p_avi->i_movi_lastchunk_pos ); - if( AVI_PacketNext( p_input ) ) + if( tk->i_blocksize > 0 ) { - return( AVI_StreamStopFinishedStreams( p_input, p_avi ) ? 0 : 1 ); + tk->i_blockno += ( tk->p_index[i].i_length + tk->i_blocksize - 1 ) / tk->i_blocksize; + } + else + { + tk->i_blockno++; } } - else - { - AVI_SeekAbsolute( p_input, p_avi->i_movi_begin ); - } + } - for( ;; ) - { - avi_packet_t avi_pk; + msg_Dbg( p_demux, + "old:"I64Fd" %s new "I64Fd, + i_oldpts, + i_oldpts > i_date ? ">" : "<", + i_date ); - if( AVI_PacketGetHeader( p_input, &avi_pk ) ) + if( p_stream->i_cat == VIDEO_ES ) + { + /* search key frame */ + if( i_date < i_oldpts ) + { + while( p_stream->i_idxposc > 0 && + !( p_stream->p_index[p_stream->i_idxposc].i_flags & + AVIIF_KEYFRAME ) ) { - msg_Warn( p_input, - "cannot get packet header, track disabled" ); - return( AVI_StreamStopFinishedStreams( p_input, p_avi ) ? 0 : 1 ); + if( AVI_StreamChunkSet( p_demux, + i_stream, + p_stream->i_idxposc - 1 ) ) + { + return VLC_EGENERIC; + } } - if( avi_pk.i_stream >= p_avi->i_streams || - ( avi_pk.i_cat != AUDIO_ES && avi_pk.i_cat != VIDEO_ES ) ) + } + else + { + while( p_stream->i_idxposc < p_stream->i_idxnb && + !( p_stream->p_index[p_stream->i_idxposc].i_flags & + AVIIF_KEYFRAME ) ) { - switch( avi_pk.i_fourcc ) + if( AVI_StreamChunkSet( p_demux, + i_stream, + p_stream->i_idxposc + 1 ) ) { - case AVIFOURCC_LIST: - AVI_SkipBytes( p_input, 12 ); - break; - default: - if( AVI_PacketNext( p_input ) ) - { - msg_Warn( p_input, - "cannot skip packet, track disabled" ); - return( AVI_StreamStopFinishedStreams( p_input, p_avi ) ? 0 : 1 ); - } - break; + return VLC_EGENERIC; } - continue; } - else - { - /* add this chunk to the index */ - AVIIndexEntry_t index; + } + } + } + else + { + if( AVI_StreamBytesSet( p_demux, + i_stream, + AVI_PTSToByte( p_stream, i_date ) ) ) + { + return VLC_EGENERIC; + } + } + return VLC_SUCCESS; +#undef p_stream +} - index.i_id = avi_pk.i_fourcc; - index.i_flags = - AVI_GetKeyFlag(p_avi->pp_info[avi_pk.i_stream]->i_codec, - avi_pk.i_peek); - index.i_pos = avi_pk.i_pos; - index.i_length = avi_pk.i_size; - AVI_IndexAddEntry( p_avi, avi_pk.i_stream, &index ); +/**************************************************************************** + * Return VLC_TRUE if it's a key frame + ****************************************************************************/ +static int AVI_GetKeyFlag( vlc_fourcc_t i_fourcc, uint8_t *p_byte ) +{ + switch( i_fourcc ) + { + case FOURCC_DIV1: + /* we have: + * startcode: 0x00000100 32bits + * framenumber ? 5bits + * piture type 0(I),1(P) 2bits + */ + if( GetDWBE( p_byte ) != 0x00000100 ) + { + /* it's not an msmpegv1 stream, strange...*/ + return AVIIF_KEYFRAME; + } + return p_byte[4] & 0x06 ? 0 : AVIIF_KEYFRAME; + + case FOURCC_DIV2: + case FOURCC_DIV3: /* wmv1 also */ + /* we have + * picture type 0(I),1(P) 2bits + */ + return p_byte[0] & 0xC0 ? 0 : AVIIF_KEYFRAME; + case FOURCC_mp4v: + /* we should find first occurrence of 0x000001b6 (32bits) + * startcode: 0x000001b6 32bits + * piture type 0(I),1(P) 2bits + */ + if( GetDWBE( p_byte ) != 0x000001b6 ) + { + /* not true , need to find the first VOP header */ + return AVIIF_KEYFRAME; + } + return p_byte[4] & 0xC0 ? 0 : AVIIF_KEYFRAME; + + default: + /* I can't do it, so say yes */ + return AVIIF_KEYFRAME; + } +} + +vlc_fourcc_t AVI_FourccGetCodec( unsigned int i_cat, vlc_fourcc_t i_codec ) +{ + switch( i_cat ) + { + case AUDIO_ES: + wf_tag_to_fourcc( i_codec, &i_codec, NULL ); + return i_codec; + + case VIDEO_ES: + /* XXX DIV1 <- msmpeg4v1, DIV2 <- msmpeg4v2, DIV3 <- msmpeg4v3, mp4v for mpeg4 */ + switch( i_codec ) + { + case FOURCC_1: + return VLC_FOURCC('m','r','l','e'); + case FOURCC_DIV1: + case FOURCC_div1: + case FOURCC_MPG4: + case FOURCC_mpg4: + return FOURCC_DIV1; + case FOURCC_DIV2: + case FOURCC_div2: + case FOURCC_MP42: + case FOURCC_mp42: + case FOURCC_MPG3: + case FOURCC_mpg3: + return FOURCC_DIV2; + case FOURCC_div3: + case FOURCC_MP43: + case FOURCC_mp43: + case FOURCC_DIV3: + case FOURCC_DIV4: + case FOURCC_div4: + case FOURCC_DIV5: + case FOURCC_div5: + case FOURCC_DIV6: + case FOURCC_div6: + case FOURCC_AP41: + case FOURCC_3IV1: + case FOURCC_3iv1: + case FOURCC_3IVD: + case FOURCC_3ivd: + case FOURCC_3VID: + case FOURCC_3vid: + return FOURCC_DIV3; + case FOURCC_DIVX: + case FOURCC_divx: + case FOURCC_MP4S: + case FOURCC_mp4s: + case FOURCC_M4S2: + case FOURCC_m4s2: + case FOURCC_xvid: + case FOURCC_XVID: + case FOURCC_XviD: + case FOURCC_DX50: + case FOURCC_dx50: + case FOURCC_mp4v: + case FOURCC_4: + case FOURCC_3IV2: + case FOURCC_3iv2: + return FOURCC_mp4v; + } + default: + return VLC_FOURCC( 'u', 'n', 'd', 'f' ); + } +} + +/**************************************************************************** + * + ****************************************************************************/ +static void AVI_ParseStreamHeader( vlc_fourcc_t i_id, + int *pi_number, int *pi_type ) +{ +#define SET_PTR( p, v ) if( p ) *(p) = (v); + int c1, c2; + + c1 = ((uint8_t *)&i_id)[0]; + c2 = ((uint8_t *)&i_id)[1]; + + if( c1 < '0' || c1 > '9' || c2 < '0' || c2 > '9' ) + { + SET_PTR( pi_number, 100 ); /* > max stream number */ + SET_PTR( pi_type, UNKNOWN_ES ); + } + else + { + SET_PTR( pi_number, (c1 - '0') * 10 + (c2 - '0' ) ); + switch( VLC_TWOCC( ((uint8_t *)&i_id)[2], ((uint8_t *)&i_id)[3] ) ) + { + case AVITWOCC_wb: + SET_PTR( pi_type, AUDIO_ES ); + break; + case AVITWOCC_dc: + case AVITWOCC_db: + SET_PTR( pi_type, VIDEO_ES ); + break; + default: + SET_PTR( pi_type, UNKNOWN_ES ); + break; + } + } +#undef SET_PTR +} + +/**************************************************************************** + * + ****************************************************************************/ +static int AVI_PacketGetHeader( demux_t *p_demux, avi_packet_t *p_pk ) +{ + uint8_t *p_peek; + + if( stream_Peek( p_demux->s, &p_peek, 16 ) < 16 ) + { + return VLC_EGENERIC; + } + p_pk->i_fourcc = VLC_FOURCC( p_peek[0], p_peek[1], p_peek[2], p_peek[3] ); + p_pk->i_size = GetDWLE( p_peek + 4 ); + p_pk->i_pos = stream_Tell( p_demux->s ); + if( p_pk->i_fourcc == AVIFOURCC_LIST || p_pk->i_fourcc == AVIFOURCC_RIFF ) + { + p_pk->i_type = VLC_FOURCC( p_peek[8], p_peek[9], + p_peek[10], p_peek[11] ); + } + else + { + p_pk->i_type = 0; + } + + memcpy( p_pk->i_peek, p_peek + 8, 8 ); + + AVI_ParseStreamHeader( p_pk->i_fourcc, &p_pk->i_stream, &p_pk->i_cat ); + return VLC_SUCCESS; +} + +static int AVI_PacketNext( demux_t *p_demux ) +{ + avi_packet_t avi_ck; + int i_skip = 0; + + if( AVI_PacketGetHeader( p_demux, &avi_ck ) ) + { + return VLC_EGENERIC; + } + + if( avi_ck.i_fourcc == AVIFOURCC_LIST && + ( avi_ck.i_type == AVIFOURCC_rec || avi_ck.i_type == AVIFOURCC_movi ) ) + { + i_skip = 12; + } + else if( avi_ck.i_fourcc == AVIFOURCC_RIFF && + avi_ck.i_type == AVIFOURCC_AVIX ) + { + i_skip = 24; + } + else + { + i_skip = __EVEN( avi_ck.i_size ) + 8; + } + + if( stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) + { + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} +static int AVI_PacketRead( demux_t *p_demux, + avi_packet_t *p_pk, + block_t **pp_frame ) +{ + size_t i_size; + + i_size = __EVEN( p_pk->i_size + 8 ); + + if( ( *pp_frame = stream_Block( p_demux->s, i_size ) ) == NULL ) + { + return VLC_EGENERIC; + } + (*pp_frame)->p_buffer += 8; + (*pp_frame)->i_buffer -= 8; + + if( i_size != p_pk->i_size + 8 ) + { + (*pp_frame)->i_buffer--; + } + + return VLC_SUCCESS; +} + +static int AVI_PacketSearch( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_packet_t avi_pk; + int i_count = 0; + + for( ;; ) + { + if( stream_Read( p_demux->s, NULL, 1 ) != 1 ) + { + return VLC_EGENERIC; + } + AVI_PacketGetHeader( p_demux, &avi_pk ); + if( avi_pk.i_stream < p_sys->i_track && + ( avi_pk.i_cat == AUDIO_ES || avi_pk.i_cat == VIDEO_ES ) ) + { + return VLC_SUCCESS; + } + switch( avi_pk.i_fourcc ) + { + case AVIFOURCC_JUNK: + case AVIFOURCC_LIST: + case AVIFOURCC_RIFF: + case AVIFOURCC_idx1: + return VLC_SUCCESS; + } + + /* Prevents from eating all the CPU with broken files. + * This value should be low enough so that it doesn't affect the + * reading speed too much (not that we care much anyway because + * this code is called only on broken files). */ + if( !(++i_count % 1024) ) + { + if( p_demux->b_die ) return VLC_EGENERIC; + + msleep( 10000 ); + if( !(i_count % (1024 * 10)) ) + msg_Warn( p_demux, "trying to resync..." ); + } + } +} + +/**************************************************************************** + * Index stuff. + ****************************************************************************/ +static void AVI_IndexAddEntry( demux_sys_t *p_sys, + int i_stream, + avi_entry_t *p_index) +{ + avi_track_t *tk = p_sys->track[i_stream]; + + /* Update i_movi_lastchunk_pos */ + if( p_sys->i_movi_lastchunk_pos < p_index->i_pos ) + { + p_sys->i_movi_lastchunk_pos = p_index->i_pos; + } + + /* add the entry */ + if( tk->i_idxnb >= tk->i_idxmax ) + { + tk->i_idxmax += 16384; + tk->p_index = realloc( tk->p_index, + tk->i_idxmax * sizeof( avi_entry_t ) ); + if( tk->p_index == NULL ) + { + return; + } + } + /* calculate cumulate length */ + if( tk->i_idxnb > 0 ) + { + p_index->i_lengthtotal = + tk->p_index[tk->i_idxnb - 1].i_length + + tk->p_index[tk->i_idxnb - 1].i_lengthtotal; + } + else + { + p_index->i_lengthtotal = 0; + } + + tk->p_index[tk->i_idxnb++] = *p_index; +} + +static int AVI_IndexLoad_idx1( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + avi_chunk_list_t *p_riff; + avi_chunk_list_t *p_movi; + avi_chunk_idx1_t *p_idx1; + + unsigned int i_stream; + unsigned int i_index; + off_t i_offset; + unsigned int i; + + vlc_bool_t b_keyset[100]; - i_stream = avi_pk.i_stream; - /* do we will read this data ? */ - if( AVI_GetDPTS( p_stream, - toread[i_stream].i_toread ) > -25 * 1000 ) - { - break; - } - else - { - if( AVI_PacketNext( p_input ) ) - { - msg_Warn( p_input, - "cannot skip packet, track disabled" ); - return( AVI_StreamStopFinishedStreams( p_input, p_avi ) ? 0 : 1 ); - } - } - } - } + p_riff = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, 0); + p_idx1 = AVI_ChunkFind( p_riff, AVIFOURCC_idx1, 0); + p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0); - } - else + if( !p_idx1 ) + { + msg_Warn( p_demux, "cannot find idx1 chunk, no index defined" ); + return VLC_EGENERIC; + } + + /* *** calculate offset *** */ + /* Well, avi is __SHIT__ so test more than one entry + * (needed for some avi files) */ + i_offset = 0; + for( i = 0; i < __MIN( p_idx1->i_entry_count, 10 ); i++ ) + { + if( p_idx1->entry[i].i_pos < p_movi->i_chunk_pos ) { - AVI_SeekAbsolute( p_input, i_pos ); + i_offset = p_movi->i_chunk_pos + 8; + break; } + } - /* read thoses data */ - if( p_stream->i_samplesize ) - { - unsigned int i_toread; + /* Reset b_keyset */ + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + b_keyset[i_stream] = VLC_FALSE; - if( ( i_toread = toread[i_stream].i_toread ) <= 0 ) - { - if( p_stream->i_samplesize > 1 ) - { - i_toread = p_stream->i_samplesize; - } - else - { - i_toread = __MAX( AVI_PTSToByte( p_stream, 20 * 1000 ), 100 ); - } - } - i_size = __MIN( p_stream->p_index[p_stream->i_idxposc].i_length - - p_stream->i_idxposb, - i_toread ); - } - else - { - i_size = p_stream->p_index[p_stream->i_idxposc].i_length; - } + for( i_index = 0; i_index < p_idx1->i_entry_count; i_index++ ) + { + unsigned int i_cat; - if( p_stream->i_idxposb == 0 ) + AVI_ParseStreamHeader( p_idx1->entry[i_index].i_fourcc, + &i_stream, + &i_cat ); + if( i_stream < p_sys->i_track && + i_cat == p_sys->track[i_stream]->i_cat ) { - i_size += 8; // need to read and skip header + avi_entry_t index; + index.i_id = p_idx1->entry[i_index].i_fourcc; + index.i_flags = + p_idx1->entry[i_index].i_flags&(~AVIIF_FIXKEYFRAME); + index.i_pos = p_idx1->entry[i_index].i_pos + i_offset; + index.i_length = p_idx1->entry[i_index].i_length; + AVI_IndexAddEntry( p_sys, i_stream, &index ); + + if( index.i_flags&AVIIF_KEYFRAME ) + b_keyset[i_stream] = VLC_TRUE; } + } - if( input_ReadInPES( p_input, &p_pes, __EVEN( i_size ) ) < 0 ) + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + if( !b_keyset[i_stream] ) { - msg_Warn( p_input, "failled reading data" ); - AVI_StreamStop( p_input, p_avi, i_stream ); - toread[i_stream].b_ok = VLC_FALSE; - continue; + avi_track_t *tk = p_sys->track[i_stream]; + + msg_Dbg( p_demux, "no key frame set for track %d", i_stream ); + for( i_index = 0; i_index < tk->i_idxnb; i_index++ ) + tk->p_index[i_index].i_flags |= AVIIF_KEYFRAME; } + } + return VLC_SUCCESS; +} + +static void __Parse_indx( demux_t *p_demux, + int i_stream, + avi_chunk_indx_t *p_indx ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + avi_entry_t index; + int32_t i; - if( i_size % 2 ) // read was padded on word boundary + msg_Dbg( p_demux, "loading subindex(0x%x) %d entries", p_indx->i_indextype, p_indx->i_entriesinuse ); + if( p_indx->i_indexsubtype == 0 ) + { + for( i = 0; i < p_indx->i_entriesinuse; i++ ) { - p_pes->p_last->p_payload_end--; - p_pes->i_pes_size--; + index.i_id = p_indx->i_id; + index.i_flags = p_indx->idx.std[i].i_size & 0x80000000 ? 0 : AVIIF_KEYFRAME; + index.i_pos = p_indx->i_baseoffset + p_indx->idx.std[i].i_offset - 8; + index.i_length = p_indx->idx.std[i].i_size&0x7fffffff; + + AVI_IndexAddEntry( p_sys, i_stream, &index ); } - // skip header - if( p_stream->i_idxposb == 0 ) + } + else if( p_indx->i_indexsubtype == AVI_INDEX_2FIELD ) + { + for( i = 0; i < p_indx->i_entriesinuse; i++ ) { - p_pes->p_first->p_payload_start += 8; - p_pes->i_pes_size -= 8; + index.i_id = p_indx->i_id; + index.i_flags = p_indx->idx.field[i].i_size & 0x80000000 ? 0 : AVIIF_KEYFRAME; + index.i_pos = p_indx->i_baseoffset + p_indx->idx.field[i].i_offset - 8; + index.i_length = p_indx->idx.field[i].i_size; + + AVI_IndexAddEntry( p_sys, i_stream, &index ); } + } + else + { + msg_Warn( p_demux, "unknown subtype index(0x%x)", p_indx->i_indexsubtype ); + } +} - p_pes->i_pts = AVI_GetPTS( p_stream ); +static void AVI_IndexLoad_indx( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + unsigned int i_stream; + int32_t i; - /* read data */ - if( p_stream->i_samplesize ) + avi_chunk_list_t *p_riff; + avi_chunk_list_t *p_hdrl; + + p_riff = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, 0); + p_hdrl = AVI_ChunkFind( p_riff, AVIFOURCC_hdrl, 0 ); + + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + avi_chunk_list_t *p_strl; + avi_chunk_indx_t *p_indx; + +#define p_stream p_sys->track[i_stream] + p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, i_stream ); + p_indx = AVI_ChunkFind( p_strl, AVIFOURCC_indx, 0 ); + + if( !p_indx ) { - if( p_stream->i_idxposb == 0 ) - { - i_size -= 8; - } - toread[i_stream].i_toread -= i_size; - p_stream->i_idxposb += i_size; - if( p_stream->i_idxposb >= - p_stream->p_index[p_stream->i_idxposc].i_length ) - { - p_stream->i_idxposb = 0; - p_stream->i_idxposc++; - } + msg_Warn( p_demux, "cannot find indx (misdetect/broken OpenDML file?)" ); + continue; } - else + + if( p_indx->i_indextype == AVI_INDEX_OF_CHUNKS ) { - toread[i_stream].i_toread--; - p_stream->i_idxposc++; + __Parse_indx( p_demux, i_stream, p_indx ); } - - if( p_stream->i_idxposc < p_stream->i_idxnb) + else if( p_indx->i_indextype == AVI_INDEX_OF_INDEXES ) { - toread[i_stream].i_posf = - p_stream->p_index[p_stream->i_idxposc].i_pos; - if( p_stream->i_idxposb > 0 ) + avi_chunk_t ck_sub; + for( i = 0; i < p_indx->i_entriesinuse; i++ ) { - toread[i_stream].i_posf += 8 + p_stream->i_idxposb; + if( stream_Seek( p_demux->s, p_indx->idx.super[i].i_offset )|| + AVI_ChunkRead( p_demux->s, &ck_sub, NULL ) ) + { + break; + } + __Parse_indx( p_demux, i_stream, &ck_sub.indx ); } - } else { - toread[i_stream].i_posf = -1; + msg_Warn( p_demux, "unknown type index(0x%x)", p_indx->i_indextype ); } +#undef p_stream + } +} + +static void AVI_IndexLoad( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + unsigned int i_stream; - b_stream = VLC_TRUE; // at least one read succeed + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + p_sys->track[i_stream]->i_idxnb = 0; + p_sys->track[i_stream]->i_idxmax = 0; + p_sys->track[i_stream]->p_index = NULL; + } - if( p_stream->p_es && p_stream->p_es->p_decoder_fifo && - ( b_play_audio || p_stream->i_cat != AUDIO_ES ) ) - { - p_pes->i_dts = - p_pes->i_pts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - p_pes->i_pts * 9/100); + if( p_sys->b_odml ) + { + AVI_IndexLoad_indx( p_demux ); + } + else if( AVI_IndexLoad_idx1( p_demux ) ) + { + /* try indx if idx1 failed as some "normal" file have indx too */ + AVI_IndexLoad_indx( p_demux ); + } - input_DecodePES( p_stream->p_es->p_decoder_fifo, p_pes ); - } - else - { - input_DeletePES( p_input->p_method_data, p_pes ); - } + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + msg_Dbg( p_demux, "stream[%d] created %d index entries", + i_stream, p_sys->track[i_stream]->i_idxnb ); } } - -/***************************************************************************** - * AVIDemux_UnSeekable: reads and demuxes data packets for unseekable file - ***************************************************************************** - * Returns -1 in case of error, 0 in case of EOF, 1 otherwise - *****************************************************************************/ -static int AVIDemux_UnSeekable( input_thread_t *p_input ) +static void AVI_IndexCreate( demux_t *p_demux ) { - demux_sys_t *p_avi = p_input->p_demux_data; - avi_stream_t *p_stream_master; - vlc_bool_t b_audio; + demux_sys_t *p_sys = p_demux->p_sys; + + avi_chunk_list_t *p_riff; + avi_chunk_list_t *p_movi; + unsigned int i_stream; - unsigned int i_packet; + off_t i_movi_end; - /* *** send audio data to decoder only if rate == DEFAULT_RATE *** */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - b_audio = p_input->stream.control.i_rate == DEFAULT_RATE; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + p_riff = AVI_ChunkFind( &p_sys->ck_root, AVIFOURCC_RIFF, 0); + p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0); - input_ClockManageRef( p_input, - p_input->stream.p_selected_program, - p_avi->i_pcr ); - /* *** find master stream for data packet skipping algo *** */ - /* *** -> first video, if any, or first audio ES *** */ - for( i_stream = 0, p_stream_master = NULL; - i_stream < p_avi->i_streams; i_stream++ ) + if( !p_movi ) { -#define p_stream p_avi->pp_info[i_stream] - if( p_stream->p_es && - p_stream->p_es->p_decoder_fifo ) - { - if( p_stream->i_cat == VIDEO_ES ) - { - p_stream_master = p_stream; - break; - } - if( p_stream->i_cat == AUDIO_ES && !p_stream_master ) - { - p_stream_master = p_stream; - } - } -#undef p_stream + msg_Err( p_demux, "cannot find p_movi" ); + return; } - if( !p_stream_master ) + + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) { - msg_Warn( p_input, "no more stream selected" ); - return( 0 ); + p_sys->track[i_stream]->i_idxnb = 0; + p_sys->track[i_stream]->i_idxmax = 0; + p_sys->track[i_stream]->p_index = NULL; } + i_movi_end = __MIN( (off_t)(p_movi->i_chunk_pos + p_movi->i_chunk_size), + stream_Size( p_demux->s ) ); - p_avi->i_pcr = AVI_GetPTS( p_stream_master ) * 9 / 100; - - for( i_packet = 0; i_packet < 10; i_packet++) + stream_Seek( p_demux->s, p_movi->i_chunk_pos + 12 ); + msg_Warn( p_demux, "creating index from LIST-movi, will take time !" ); + for( ;; ) { -#define p_stream p_avi->pp_info[avi_pk.i_stream] - - avi_packet_t avi_pk; + avi_packet_t pk; - if( AVI_PacketGetHeader( p_input, &avi_pk ) ) + if( p_demux->b_die ) { - return( 0 ); + return; } -// AVI_ParseStreamHeader( avi_pk.i_fourcc, &i_stream, &i_cat ); - if( avi_pk.i_stream >= p_avi->i_streams || - ( avi_pk.i_cat != AUDIO_ES && avi_pk.i_cat != VIDEO_ES ) ) + if( AVI_PacketGetHeader( p_demux, &pk ) ) { - /* we haven't found an audio or video packet: - * - we have seek, found first next packet - * - others packets could be found, skip them - */ - switch( avi_pk.i_fourcc ) - { - case AVIFOURCC_JUNK: - case AVIFOURCC_LIST: - return( !AVI_PacketNext( p_input ) ? 1 : 0 ); - case AVIFOURCC_idx1: - return( 0 ); // eof - default: - msg_Warn( p_input, - "seems to have lost position, resync" ); - if( AVI_PacketSearch( p_input ) ) - { - msg_Err( p_input, "resync failed" ); - return( -1 ); - } - } + break; + } + if( pk.i_stream < p_sys->i_track && + pk.i_cat == p_sys->track[pk.i_stream]->i_cat ) + { + avi_entry_t index; + index.i_id = pk.i_fourcc; + index.i_flags = + AVI_GetKeyFlag(p_sys->track[pk.i_stream]->i_codec, pk.i_peek); + index.i_pos = pk.i_pos; + index.i_length = pk.i_size; + AVI_IndexAddEntry( p_sys, pk.i_stream, &index ); } else { - /* do will send this packet to decoder ? */ - if( ( !b_audio && avi_pk.i_cat == AUDIO_ES )|| - !p_stream->p_es || - !p_stream->p_es->p_decoder_fifo ) - { - if( AVI_PacketNext( p_input ) ) - { - return( 0 ); - } - } - else + switch( pk.i_fourcc ) { - /* it's a selected stream, check for time */ - if( __ABS( AVI_GetPTS( p_stream ) - - AVI_GetPTS( p_stream_master ) )< 600*1000 ) - { - /* load it and send to decoder */ - pes_packet_t *p_pes; - if( AVI_PacketRead( p_input, &avi_pk, &p_pes ) || !p_pes) + case AVIFOURCC_idx1: + if( p_sys->b_odml ) { - return( -1 ); + avi_chunk_list_t *p_sysx; + p_sysx = AVI_ChunkFind( &p_sys->ck_root, + AVIFOURCC_RIFF, 1 ); + + msg_Dbg( p_demux, "looking for new RIFF chunk" ); + if( stream_Seek( p_demux->s, p_sysx->i_chunk_pos + 24)) + { + goto print_stat; + } + break; } - p_pes->i_dts = - p_pes->i_pts = - input_ClockGetTS( p_input, - p_input->stream.p_selected_program, - AVI_GetPTS( p_stream ) * 9/100); - input_DecodePES( p_stream->p_es->p_decoder_fifo, p_pes ); - } - else - { - if( AVI_PacketNext( p_input ) ) + goto print_stat; + case AVIFOURCC_RIFF: + msg_Dbg( p_demux, "new RIFF chunk found" ); + case AVIFOURCC_rec: + case AVIFOURCC_JUNK: + break; + default: + msg_Warn( p_demux, "need resync, probably broken avi" ); + if( AVI_PacketSearch( p_demux ) ) { - return( 0 ); + msg_Warn( p_demux, "lost sync, abord index creation" ); + goto print_stat; } - } } + } - /* *** update stream time position *** */ - if( p_stream->i_samplesize ) - { - p_stream->i_idxposb += avi_pk.i_size; - } - else - { - p_stream->i_idxposc++; - } + if( ( !p_sys->b_odml && pk.i_pos + pk.i_size >= i_movi_end ) || + AVI_PacketNext( p_demux ) ) + { + break; + } + } + +print_stat: + for( i_stream = 0; i_stream < p_sys->i_track; i_stream++ ) + { + msg_Dbg( p_demux, + "stream[%d] creating %d index entries", + i_stream, + p_sys->track[i_stream]->i_idxnb ); + } +} + +/***************************************************************************** + * Stream management + *****************************************************************************/ +static int AVI_TrackStopFinishedStreams( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + unsigned int i; + int b_end = VLC_TRUE; + + for( i = 0; i < p_sys->i_track; i++ ) + { + avi_track_t *tk = p_sys->track[i]; + if( tk->i_idxposc >= tk->i_idxnb ) + { + tk->b_activated = VLC_FALSE; + es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, tk->p_es, VLC_FALSE ); + } + else + { + b_end = VLC_FALSE; + } + } + return( b_end ); +} + +/**************************************************************************** + * AVI_MovieGetLength give max streams length in second + ****************************************************************************/ +static mtime_t AVI_MovieGetLength( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + mtime_t i_maxlength = 0; + unsigned int i; + + for( i = 0; i < p_sys->i_track; i++ ) + { + avi_track_t *tk = p_sys->track[i]; + mtime_t i_length; + + /* fix length for each stream */ + if( tk->i_idxnb < 1 || !tk->p_index ) + { + continue; + } + if( tk->i_samplesize ) + { + i_length = AVI_GetDPTS( tk, + tk->p_index[tk->i_idxnb-1].i_lengthtotal + + tk->p_index[tk->i_idxnb-1].i_length ); + } + else + { + i_length = AVI_GetDPTS( tk, tk->i_idxnb ); } + i_length /= (mtime_t)1000000; /* in seconds */ -#undef p_stream + msg_Dbg( p_demux, + "stream[%d] length:"I64Fd" (based on index)", + i, + i_length ); + i_maxlength = __MAX( i_maxlength, i_length ); } - return( 1 ); + return i_maxlength; } +