* avi.c : AVI file Stream input module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
- * $Id: avi.c,v 1.2 2002/04/25 03:01:03 fenrir Exp $
+ * $Id: avi.c,v 1.19 2002/05/18 17:47:46 sam Exp $
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
#include "input_ext-dec.h"
#include "input_ext-plugins.h"
+#include "video.h"
+
/*****************************************************************************
* Constants
*****************************************************************************/
/*****************************************************************************
* Definition of structures and libraries for this plugins
*****************************************************************************/
-#include "fourcc.h"
#include "libLE.c"
#include "libioRIFF.c"
#include "avi.h"
p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data ;
if( p_avi_demux->p_riff != NULL )
- RIFF_DeleteChunk( p_input->p_demux_data, p_avi_demux->p_riff );
+ RIFF_DeleteChunk( p_input, p_avi_demux->p_riff );
if( p_avi_demux->p_hdrl != NULL )
- RIFF_DeleteChunk( p_input->p_demux_data, p_avi_demux->p_hdrl );
+ RIFF_DeleteChunk( p_input, p_avi_demux->p_hdrl );
if( p_avi_demux->p_movi != NULL )
- RIFF_DeleteChunk( p_input->p_demux_data, p_avi_demux->p_movi );
+ RIFF_DeleteChunk( p_input, p_avi_demux->p_movi );
if( p_avi_demux->p_idx1 != NULL )
- RIFF_DeleteChunk( p_input->p_demux_data, p_avi_demux->p_idx1 );
+ RIFF_DeleteChunk( p_input, p_avi_demux->p_idx1 );
if( p_avi_demux->pp_info != NULL )
{
for( i = 0; i < p_avi_demux->i_streams; i++ )
{
if( p_avi_demux->pp_info[i] != NULL )
{
-#define p_info p_avi_demux->pp_info[i]
-/* don't uses RIFF_DeleteChunk -> it will segfault here ( probably because of
- data_packey already unallocated ? */
- if( p_info->p_strl != NULL )
- {
- free( p_info->p_strl );
- }
- if( p_info->p_strh != NULL )
- {
- free( p_info->p_strh );
- }
-
- if( p_info->p_strf != NULL )
- {
- free( p_info->p_strf );
- }
- if( p_info->p_strd != NULL )
+ if( p_avi_demux->pp_info[i]->p_index != NULL )
{
- free( p_info->p_strd );
+ free( p_avi_demux->pp_info[i]->p_index );
}
- if( p_info->p_index != NULL )
- {
- free( p_info->p_index );
- }
- free( p_info );
-#undef p_info
+ free( p_avi_demux->pp_info[i] );
}
}
free( p_avi_demux->pp_info );
return( 0 );
}
+static inline int __AVIGetESTypeFromTwoCC( u16 i_type )
+{
+ switch( i_type )
+ {
+ case( TWOCC_wb ):
+ return( AUDIO_ES );
+ case( TWOCC_dc ):
+ case( TWOCC_db ):
+ return( VIDEO_ES );
+ }
+ return( UNKNOWN_ES );
+}
+
+
static int __AVI_ParseStreamHeader( u32 i_id, int *i_number, u16 *i_type )
{
int c1,c2,c3,c4;
return( -1 );
}
*i_number = (c1 - '0') * 10 + (c2 - '0' );
- *i_type = ( c3 << 8) + c4;
+ *i_type = ( c4 << 8) + c3;
return( 0 );
}
-static int __AVI_HeaderMoviValid( u32 i_header )
-{
- switch( i_header&0xFFFF0000 )
- {
- case( TWOCC_wb ):
- case( TWOCC_db ):
- case( TWOCC_dc ):
- case( TWOCC_pc ):
- return( 1 );
- break;
- }
- switch( i_header )
- {
- case( FOURCC_LIST ):
- case( FOURCC_JUNK ):
- return( 1 );
- break;
- }
- return( 0 );
-}
-
static void __AVI_AddEntryIndex( AVIStreamInfo_t *p_info,
AVIIndexEntry_t *p_index)
{
+ AVIIndexEntry_t *p_tmp;
if( p_info->p_index == NULL )
{
- p_info->i_idxmax = 4096;
+ p_info->i_idxmax = 16384;
p_info->i_idxnb = 0;
p_info->p_index = calloc( p_info->i_idxmax,
sizeof( AVIIndexEntry_t ) );
+ if( p_info->p_index == NULL ) {return;}
}
if( p_info->i_idxnb >= p_info->i_idxmax )
{
- p_info->i_idxmax += 4096;
- p_info->p_index = realloc( (void*)p_info->p_index,
- p_info->i_idxmax *
- sizeof( AVIIndexEntry_t ) );
+ p_info->i_idxmax += 16384;
+ p_tmp = realloc( (void*)p_info->p_index,
+ p_info->i_idxmax *
+ sizeof( AVIIndexEntry_t ) );
+ if( p_tmp == NULL )
+ {
+ p_info->i_idxmax -= 16384;
+ return;
+ }
+ p_info->p_index = p_tmp;
}
/* calculate cumulate length */
if( p_info->i_idxnb > 0 )
{
- p_index->i_lengthtotal = p_index->i_length +
- p_info->p_index[p_info->i_idxnb-1].i_lengthtotal;
+ 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
{
static void __AVI_GetIndex( input_thread_t *p_input )
{
- demux_data_avi_file_t *p_avi_demux;
AVIIndexEntry_t index;
byte_t *p_buff;
riffchunk_t *p_idx1;
int i;
int i_number;
u16 i_type;
-
- p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data ;
+ int i_totalentry = 0;
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data ;
if( RIFF_FindAndGotoDataChunk( p_input,
p_avi_demux->p_riff,
intf_WarnMsg( 1, "input init: loading index" );
for(;;)
{
- if( (i_read = input_Peek( p_input, &p_buff, 16*1024 )) < 16 )
+ i_read = __MIN( 16*1024, p_idx1->i_size - i_totalentry *16);
+ if( ((i_read = input_Peek( p_input, &p_buff, i_read )) < 16 )
+ ||( i_totalentry *16 >= p_idx1->i_size ) )
{
- for( i = 0, i_read = 0; i < p_avi_demux->i_streams; i++ )
- {
- i_read += p_avi_demux->pp_info[i]->i_idxnb;
- }
- intf_WarnMsg( 1,"input info: read %d idx chunk", i_read );
+ intf_WarnMsg( 1,"input info: read %d idx entries", i_totalentry );
return;
}
i_read /= 16 ;
- /* TODO try to verify if we are beyond end of p_idx1 */
for( i = 0; i < i_read; i++ )
{
byte_t *p_peek = p_buff + i * 16;
+ i_totalentry++;
index.i_id = __GetDoubleWordLittleEndianFromBuff( p_peek );
index.i_flags = __GetDoubleWordLittleEndianFromBuff( p_peek+4);
- index.i_offset = __GetDoubleWordLittleEndianFromBuff( p_peek+8);
+ index.i_pos = __GetDoubleWordLittleEndianFromBuff( p_peek+8);
index.i_length = __GetDoubleWordLittleEndianFromBuff(p_peek+12);
- if( (__AVI_ParseStreamHeader( index.i_id, &i_number, &i_type ) != 0)
- ||(i_number > p_avi_demux->i_streams))
+ if( (__AVI_ParseStreamHeader( index.i_id, &i_number, &i_type ) == 0)
+ &&(i_number < p_avi_demux->i_streams )
+ &&(p_avi_demux->pp_info[i_number]->i_cat ==
+ __AVIGetESTypeFromTwoCC( i_type )))
{
- continue;
+ __AVI_AddEntryIndex( p_avi_demux->pp_info[i_number],
+ &index );
}
- __AVI_AddEntryIndex( p_avi_demux->pp_info[i_number],
- &index );
}
__RIFF_SkipBytes( p_input, 16 * i_read );
}
}
static int __AVI_SeekToChunk( input_thread_t *p_input, AVIStreamInfo_t *p_info )
{
- demux_data_avi_file_t *p_avi_demux;
- p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data;
-
- if( (p_info->p_index != NULL)&&(p_info->i_idxpos < p_info->i_idxnb) )
+ if( (p_info->p_index )&&(p_info->i_idxposc < p_info->i_idxnb) )
{
- /* perfect */
- off_t i_pos;
- i_pos = (off_t)p_info->p_index[p_info->i_idxpos].i_offset +
- p_info->i_idxoffset;
-
- p_input->pf_seek( p_input, i_pos );
+ p_input->pf_seek( p_input,
+ (off_t)p_info->p_index[p_info->i_idxposc].i_pos);
input_AccessReinit( p_input );
return( 0 );
}
- /* index are no longer valid */
- if( p_info->p_index != NULL )
- {
- return( -1 );
- }
- /* no index */
+ /* no index can't arrive but ...*/
+ intf_WarnMsg( 1, "input error: can't seek");
return( -1 );
}
/* XXX call after get p_movi */
-static int __AVI_GetIndexOffset( input_thread_t *p_input )
+static void __AVI_UpdateIndexOffset( input_thread_t *p_input )
{
- riffchunk_t *p_chunk;
- demux_data_avi_file_t *p_avi_demux;
- int i;
-
- p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data;
- for( i = 0; i < p_avi_demux->i_streams; i++ )
+ int i_stream;
+ int b_start = 1;/* if index pos is based on start of file or not (p_movi) */
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data;
+
+/* FIXME some work to do :
+ * test in the ile if it's true, if not do a RIFF_Find...
+*/
+#define p_info p_avi_demux->pp_info[i_stream]
+ for( i_stream = 0; i_stream < p_avi_demux->i_streams; i_stream++ )
+ {
+ if( ( p_info->p_index )
+ && ( p_info->p_index[0].i_pos < p_avi_demux->p_movi->i_pos + 8 ))
+ {
+ b_start = 0;
+ break;
+ }
+ }
+ if( !b_start )
{
-#define p_info p_avi_demux->pp_info[i]
- if( p_info->p_index == NULL ) {continue;}
- p_info->i_idxoffset = 0;
- __AVI_SeekToChunk( p_input, p_info );
- p_chunk = RIFF_ReadChunk( p_input );
- if( (p_chunk == NULL)||(p_chunk->i_id != p_info->p_index[0].i_id) )
+ for( i_stream = 0; i_stream < p_avi_demux->i_streams; i_stream++ )
{
- p_info->i_idxoffset = p_avi_demux->p_movi->i_pos + 8;
- __AVI_SeekToChunk( p_input, p_info );
- p_chunk = RIFF_ReadChunk( p_input );
- if( (p_chunk == NULL)||(p_chunk->i_id != p_info->p_index[0].i_id) )
+ int i;
+ if( p_info->p_index )
{
- intf_WarnMsg( 1, "input demux: can't find offset for stream %d",
- i);
- continue; /* TODO: search manually from p_movi */
+ for( i = 0; i < p_info->i_idxnb; i++ )
+ {
+ p_info->p_index[i].i_pos +=
+ p_avi_demux->p_movi->i_pos + 8;
+ }
}
}
-#undef p_info
}
- return( 0 );
+#undef p_info
}
+
static int __AVI_AudioGetType( u32 i_type )
{
switch( i_type )
{
switch( i_type )
{
- case( FOURCC_DIV3 ):
- case( FOURCC_div3 ):
- case( FOURCC_DIV4 ):
- case( FOURCC_div4 ):
- case( FOURCC_DIV5 ):
- case( FOURCC_div5 ):
- case( FOURCC_DIV6 ):
- case( FOURCC_div6 ):
- case( FOURCC_3IV1 ):
- case( FOURCC_AP41 ):
- case( FOURCC_MP43 ):
- case( FOURCC_mp43 ):
- return( MSMPEG4_VIDEO_ES );
-
- case( FOURCC_DIVX ):
- case( FOURCC_divx ):
- case( FOURCC_DX50 ):
- case( FOURCC_MP4S ):
- case( FOURCC_MPG4 ):
- case( FOURCC_mpg4 ):
- case( FOURCC_mp4v ):
+/* FIXME FIXME : what are the correct tag for msmpeg4 v1 */
+ case( FOURCC_MPG4 ):
+ case( FOURCC_mpg4 ):
+ case( FOURCC_DIV2 ):
+ case( FOURCC_div2 ):
+ case( FOURCC_MP42 ):
+ case( FOURCC_mp42 ):
+ return( MSMPEG4v2_VIDEO_ES );
+
+ case( FOURCC_MPG3 ):
+ case( FOURCC_mpg3 ):
+ 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( MSMPEG4v3_VIDEO_ES );
+
+
+ case( FOURCC_DIVX ):
+ case( FOURCC_divx ):
+ case( FOURCC_DIV1 ):
+ case( FOURCC_div1 ):
+ 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( MPEG4_VIDEO_ES );
default:
return( 0 );
}
}
-/**************************************************************************/
-/* Tention: bcp de test à ajouter mais aussi beaucoup de MEMOIRE a DESALLOUER pas fait */
+/*****************************************************************************
+ * AVIInit: check file and initializes AVI structures
+ *****************************************************************************/
static int AVIInit( input_thread_t *p_input )
{
riffchunk_t *p_riff,*p_hdrl,*p_movi;
riffchunk_t *p_avih;
- riffchunk_t *p_strl,*p_strh,*p_strf/* ,*p_strd */;
-
+ riffchunk_t *p_strl,*p_strh,*p_strf;
demux_data_avi_file_t *p_avi_demux;
es_descriptor_t *p_es = NULL; /* for not warning */
- es_descriptor_t *p_es_video = NULL;
- es_descriptor_t *p_es_audio = NULL;
int i;
-
- p_avi_demux = malloc( sizeof(demux_data_avi_file_t) );
- memset( p_avi_demux, 0, sizeof( demux_data_avi_file_t ) );
- p_input->p_demux_data = p_avi_demux;
- /* FIXME FIXME Je sais pas trop a quoi ca sert juste copié de ESInit */
+ /* we need to seek to be able to readcorrectly */
+ if( !p_input->stream.b_seekable )
+ {
+ intf_WarnMsg( 2,"input: RIFF-AVI plug-in discarded (no seekable)" );
+ return( -1 );
+ }
+ p_input->p_demux_data =
+ p_avi_demux = malloc( sizeof(demux_data_avi_file_t) );
+ if( p_avi_demux == NULL )
+ {
+ intf_ErrMsg( "input error: not enough memory" );
+ return( -1 );
+ }
+ memset( p_avi_demux, 0, sizeof( demux_data_avi_file_t ) );
+ p_avi_demux->i_rate = DEFAULT_RATE;
+ /* FIXME I don't know what it's do, copied from ESInit */
/* Initialize access plug-in structures. */
if( p_input->i_mtu == 0 )
{
if( RIFF_TestFileHeader( p_input, &p_riff, FOURCC_AVI ) != 0 )
{
__AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input: RIFF-AVI plug-in discarded (avi_file)" );
+ intf_WarnMsg( 2,"input: RIFF-AVI plug-in discarded (avi_file)" );
return( -1 );
}
p_avi_demux->p_riff = p_riff;
intf_ErrMsg( "input error: cannot look for subchunk (avi_file)" );
return ( -1 );
}
- /* ds LIST-hdrl cherche avih */
+ /* in LIST-hdrl search avih */
if( RIFF_FindAndLoadChunk( p_input, p_hdrl,
&p_avih, FOURCC_avih ) != 0 )
{
RIFF_DeleteChunk( p_input, p_avih );
if( p_avi_demux->avih.i_streams == 0 )
- /* aucun flux defini, peut etre essayer de trouver ss connaitre */
- /* le nombre serait pas mal, a voir */
+ /* no stream found, perhaps it would be cool to find it */
{
__AVIFreeDemuxData( p_input );
intf_ErrMsg( "input error: no defined stream !" );
return( -1 );
}
-
- /* On creer les tableau pr les flux */
+
+ /* 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 );
+ __AVIFreeDemuxData( p_input );
+ intf_ErrMsg( "input error: cannot init stream" );
+ return( -1 );
+ }
+ if( input_AddProgram( p_input, 0, 0) == NULL )
+ {
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ __AVIFreeDemuxData( p_input );
+ intf_ErrMsg( "input error: cannot add program" );
+ return( -1 );
+ }
+ p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
+ p_input->stream.p_new_program = p_input->stream.pp_programs[0] ;
+ p_input->stream.i_mux_rate = p_avi_demux->avih.i_maxbytespersec / 50;
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+ /* now read info on each stream and create ES */
p_avi_demux->i_streams = p_avi_demux->avih.i_streams;
p_avi_demux->pp_info = calloc( p_avi_demux->i_streams,
sizeof( AVIStreamInfo_t* ) );
memset( p_avi_demux->pp_info, 0,
sizeof( AVIStreamInfo_t* ) * p_avi_demux->i_streams );
-
+
for( i = 0 ; i < p_avi_demux->i_streams; i++ )
{
- p_avi_demux->pp_info[i] = malloc( sizeof(AVIStreamInfo_t ) );
- memset( p_avi_demux->pp_info[i], 0, sizeof( AVIStreamInfo_t ) );
+#define p_info p_avi_demux->pp_info[i]
+ p_info = malloc( sizeof(AVIStreamInfo_t ) );
+ memset( p_info, 0, sizeof( AVIStreamInfo_t ) );
- /* pour chaque flux on cherche ses infos */
- if( RIFF_FindListChunk(p_input,
+ if( ( RIFF_FindListChunk(p_input,
&p_strl,p_hdrl, FOURCC_strl) != 0 )
+ ||( RIFF_DescendChunk(p_input) != 0 ))
{
__AVIFreeDemuxData( p_input );
intf_ErrMsg( "input error: cannot find \"LIST-strl\" (avi_file)" );
return( -1 );
}
- p_avi_demux->pp_info[i]->p_strl = p_strl;
- if( RIFF_DescendChunk(p_input) != 0 )
- {
- __AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input error: cannot look for subchunk (avi_file)" );
- return ( -1 );
- }
- /* ds LIST-strl cherche strh */
+ /* in LIST-strl search strh */
if( RIFF_FindAndLoadChunk( p_input, p_hdrl,
&p_strh, FOURCC_strh ) != 0 )
{
+ RIFF_DeleteChunk( p_input, p_strl );
__AVIFreeDemuxData( p_input );
intf_ErrMsg( "input error: cannot find \"strh\" (avi_file)" );
return( -1 );
}
- p_avi_demux->pp_info[i]->p_strh = p_strh;
-
- /* ds LIST-strl cherche strf */
+ __AVI_Parse_Header( &p_info->header,
+ p_strh->p_data->p_payload_start);
+ RIFF_DeleteChunk( p_input, p_strh );
+
+ /* in LIST-strl search strf */
if( RIFF_FindAndLoadChunk( p_input, p_hdrl,
&p_strf, FOURCC_strf ) != 0 )
{
+ RIFF_DeleteChunk( p_input, p_strl );
__AVIFreeDemuxData( p_input );
intf_ErrMsg( "input error: cannot find \"strf\" (avi_file)" );
return( -1 );
}
- p_avi_demux->pp_info[i]->p_strf = p_strf;
- /* FIXME faudrait cherche et charger strd */
- /* mais a priori pas vraiment utile pr divx */
-
- if( RIFF_AscendChunk(p_input, p_strl) != 0 )
+ /* we don't get strd, it's useless for divx,opendivx,mepgaudio */
+ if( RIFF_AscendChunk(p_input, p_strl) != 0 )
{
+ RIFF_DeleteChunk( p_input, p_strf );
+ RIFF_DeleteChunk( p_input, p_strl );
__AVIFreeDemuxData( p_input );
intf_ErrMsg( "input error: cannot go out (\"strl\") (avi_file)" );
return( -1 );
}
-
- }
-
-
- if( RIFF_AscendChunk(p_input, p_hdrl) != 0)
- {
- __AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input error: cannot go out (\"hdrl\") (avi_file)" );
- return( -1 );
- }
-
- intf_Msg( "input init: AVIH: %d stream, flags %s%s%s%s%s%s ",
- p_avi_demux->i_streams,
- p_avi_demux->avih.i_flags&AVIF_HASINDEX?" HAS_INDEX":"",
- p_avi_demux->avih.i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"",
- p_avi_demux->avih.i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"",
- p_avi_demux->avih.i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"",
- p_avi_demux->avih.i_flags&AVIF_WASCAPTUREFILE?" CAPTUREFILE":"",
- p_avi_demux->avih.i_flags&AVIF_COPYRIGHTED?" COPYRIGHTED":"" );
-
- /* go to movi chunk */
- if( RIFF_FindListChunk(p_input ,&p_movi,p_riff, FOURCC_movi) != 0 )
- {
- intf_ErrMsg( "input error: cannot find \"LIST-movi\" (avi_file)" );
- __AVIFreeDemuxData( p_input );
- return( -1 );
- }
- p_avi_demux->p_movi = p_movi;
-
- /* get index */
- if( (p_input->stream.b_seekable)
- &&((p_avi_demux->avih.i_flags&AVIF_HASINDEX) != 0) )
- {
- __AVI_GetIndex( p_input );
- /* try to get i_idxoffset with first stream*/
- __AVI_GetIndexOffset( p_input );
- RIFF_GoToChunk( p_input, p_avi_demux->p_movi );
- }
- else
- {
- intf_WarnMsg( 1, "input init: cannot get index" );
- }
-
- if( RIFF_DescendChunk( p_input ) != 0 )
- {
- __AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input error: cannot go in (\"movi\") (avi_file)" );
- return( -1 );
- }
- /* TODO: check for index and read it if possible( seekable )*/
-
- /** We have now finished with reading the file **/
- /** we make the last initialisation **/
-
- if( input_InitStream( p_input, 0 ) == -1)
- {
- __AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input error: cannot init stream" );
- return( -1 );
- }
- if( input_AddProgram( p_input, 0, 0) == NULL )
- {
- __AVIFreeDemuxData( p_input );
- intf_ErrMsg( "input error: cannot add program" );
- return( -1 );
- }
- p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
- p_input->stream.p_new_program = p_input->stream.pp_programs[0] ;
-
- vlc_mutex_lock( &p_input->stream.stream_lock );
- for( i = 0; i < p_avi_demux->i_streams; i++ )
- {
-#define p_info p_avi_demux->pp_info[i]
- __AVI_Parse_Header( &p_info->header,
- p_info->p_strh->p_data->p_payload_start);
+ /* add one ES */
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ p_es = input_AddES( p_input,
+ p_input->stream.p_selected_program, 1+i,
+ p_strf->i_size );
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ p_es->i_stream_id =i; /* XXX: i don't use it */
+
switch( p_info->header.i_type )
{
case( FOURCC_auds ):
- /* pour l'id j'ai mis 12+i pr audio et 42+i pour video */
- /* et le numero du flux(ici i) dans i_stream_id */
+ p_es->i_cat = AUDIO_ES;
avi_ParseWaveFormatEx( &p_info->audio_format,
- p_info->p_strf->p_data->p_payload_start );
- p_es = input_AddES( p_input,
- p_input->stream.p_selected_program, 12+i,
- p_info->p_strf->i_size );
+ p_strf->p_data->p_payload_start );
p_es->b_audio = 1;
p_es->i_type =
__AVI_AudioGetType( p_info->audio_format.i_formattag );
- p_es->i_stream_id =i; /* FIXME */
if( p_es->i_type == 0 )
{
intf_ErrMsg( "input error: stream(%d,0x%x) not supported",
p_info->audio_format.i_formattag );
p_es->i_cat = UNKNOWN_ES;
}
- else
- {
- if( p_es_audio == NULL ) {p_es_audio = p_es;}
- p_es->i_cat = AUDIO_ES;
- }
break;
case( FOURCC_vids ):
+ p_es->i_cat = VIDEO_ES;
avi_ParseBitMapInfoHeader( &p_info->video_format,
- p_info->p_strf->p_data->p_payload_start );
-
- p_es = input_AddES( p_input,
- p_input->stream.p_selected_program, 42+i,
- p_info->p_strf->i_size );
+ p_strf->p_data->p_payload_start );
p_es->b_audio = 0;
p_es->i_type =
__AVI_VideoGetType( p_info->video_format.i_compression );
- p_es->i_stream_id =i; /* FIXME */
if( p_es->i_type == 0 )
{
intf_ErrMsg( "input error: stream(%d,%4.4s) not supported",
(char*)&p_info->video_format.i_compression);
p_es->i_cat = UNKNOWN_ES;
}
- else
- {
- if( p_es_video == NULL ) {p_es_video = p_es;}
- p_es->i_cat = VIDEO_ES;
- }
break;
default:
- p_es = input_AddES( p_input,
- p_input->stream.p_selected_program, 12,
- p_info->p_strf->i_size );
intf_ErrMsg( "input error: unknown stream(%d) type",
i );
p_es->i_cat = UNKNOWN_ES;
p_info->i_cat = p_es->i_cat;
/* We copy strf for decoder in p_es->p_demux_data */
memcpy( p_es->p_demux_data,
- p_info->p_strf->p_data->p_payload_start,
- p_info->p_strf->i_size );
- /* print informations on stream */
- switch( p_es->i_cat )
+ p_strf->p_data->p_payload_start,
+ p_strf->i_size );
+ RIFF_DeleteChunk( p_input, p_strf );
+ RIFF_DeleteChunk( p_input, p_strl );
+#undef p_info
+ }
+
+
+
+ /* go out of p_hdrl */
+ if( RIFF_AscendChunk(p_input, p_hdrl) != 0)
+ {
+ __AVIFreeDemuxData( p_input );
+ intf_ErrMsg( "input error: cannot go out (\"hdrl\") (avi_file)" );
+ return( -1 );
+ }
+
+ /* go to movi chunk to get it*/
+ if( RIFF_FindListChunk(p_input ,&p_movi,p_riff, FOURCC_movi) != 0 )
+ {
+ intf_ErrMsg( "input error: cannot find \"LIST-movi\" (avi_file)" );
+ __AVIFreeDemuxData( p_input );
+ return( -1 );
+ }
+ p_avi_demux->p_movi = p_movi;
+
+ /* get index XXX need to have p_movi */
+ if( (p_avi_demux->avih.i_flags&AVIF_HASINDEX) != 0 )
+ {
+ /* get index */
+ __AVI_GetIndex( p_input );
+ /* try to get i_idxoffset for each stream */
+ __AVI_UpdateIndexOffset( p_input );
+ }
+ else
+ {
+ intf_WarnMsg( 1, "input init: no index !" );
+ }
+
+
+ /* we verify that each stream have at least one entry or create it */
+ for( i = 0; i < p_avi_demux->i_streams ; i++ )
+ {
+ AVIIndexEntry_t index;
+ riffchunk_t *p_chunk;
+#define p_info p_avi_demux->pp_info[i]
+ if( p_info->p_index == NULL )
+ {
+ RIFF_GoToChunk( p_input, p_avi_demux->p_movi );
+ if( RIFF_DescendChunk(p_input) != 0 ) { continue; }
+ p_chunk = NULL;
+ switch( p_info->i_cat )
+ {
+ case( AUDIO_ES ):
+ if( RIFF_FindChunk( p_input,
+ MAKEFOURCC('0'+i/10, '0'+i%10,'w','b' ),
+ p_movi ) == 0)
+ {
+ p_chunk = RIFF_ReadChunk( p_input );
+ }
+ break;
+
+ case( VIDEO_ES ):
+ if( (RIFF_FindChunk( p_input,
+ MAKEFOURCC('0'+i/10, '0'+i%10,'d','c' ),
+ p_movi ) == 0) )
+ {
+ p_chunk = RIFF_ReadChunk( p_input );
+ }
+ else
+ {
+ RIFF_GoToChunk( p_input, p_avi_demux->p_movi );
+ if( RIFF_DescendChunk(p_input) != 0 ) { continue; }
+ if( (RIFF_FindChunk( p_input,
+ MAKEFOURCC('0'+i/10, '0'+i%10,'d','b' ),
+ p_movi ) == 0) )
+ {
+ p_chunk = RIFF_ReadChunk( p_input );
+ }
+ }
+ break;
+ }
+ if( p_chunk != NULL )
+ {
+ index.i_id = p_chunk->i_id;
+ index.i_flags = AVIIF_KEYFRAME;
+ index.i_pos = p_chunk->i_pos;
+ index.i_length = p_chunk->i_size;
+ __AVI_AddEntryIndex( p_info, &index );
+ intf_WarnMsg( 3, "input init: add index entry (%4.4s) (%d)",
+ (char*)&p_chunk->i_id,
+ i);
+
+ }
+ }
+#undef p_info
+ }
+
+ /* to make sure to go the begining because unless demux will see a seek */
+ RIFF_GoToChunk( p_input, p_avi_demux->p_movi );
+ if( RIFF_DescendChunk( p_input ) != 0 )
+ {
+ __AVIFreeDemuxData( p_input );
+ intf_ErrMsg( "input error: cannot go in (\"movi\") (avi_file)" );
+ return( -1 );
+ }
+
+ /* print informations on streams */
+ intf_Msg( "input init: AVIH: %d stream, flags %s%s%s%s%s%s ",
+ p_avi_demux->i_streams,
+ p_avi_demux->avih.i_flags&AVIF_HASINDEX?" HAS_INDEX":"",
+ p_avi_demux->avih.i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"",
+ p_avi_demux->avih.i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"",
+ p_avi_demux->avih.i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"",
+ p_avi_demux->avih.i_flags&AVIF_WASCAPTUREFILE?" CAPTUREFILE":"",
+ p_avi_demux->avih.i_flags&AVIF_COPYRIGHTED?" COPYRIGHTED":"" );
+
+ for( i = 0; i < p_avi_demux->i_streams; i++ )
+ {
+#define p_info p_avi_demux->pp_info[i]
+ switch( p_info->p_es->i_cat )
{
case( VIDEO_ES ):
intf_Msg("input init: video(%4.4s) %dx%d %dbpp %ffps (size %d)",
(float)p_info->header.i_rate /
(float)p_info->header.i_scale,
p_info->header.i_samplesize );
+ if( (p_avi_demux->p_info_video == NULL) )
+ {
+ p_avi_demux->p_info_video = p_info;
+ }
break;
+
case( AUDIO_ES ):
intf_Msg( "input init: audio(0x%x) %d channels %dHz %dbits %ffps (size %d)",
p_info->audio_format.i_formattag,
(float)p_info->header.i_rate /
(float)p_info->header.i_scale,
p_info->header.i_samplesize );
+ if( (p_avi_demux->p_info_audio == NULL) )
+ {
+ p_avi_demux->p_info_audio = p_info;
+ }
break;
+ case( UNKNOWN_ES ):
+ intf_Msg( "input init: unhanled stream %d", i );
}
-
#undef p_info
}
/* we select the first audio and video ES */
- if( p_es_video != NULL )
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ if( p_avi_demux->p_info_video != NULL )
{
- input_SelectES( p_input, p_es_video );
+ input_SelectES( p_input, p_avi_demux->p_info_video->p_es );
+ /* it seems that it's useless to select es because there are selected
+ * by the interface but i'm not sure of that */
}
else
{
- intf_ErrMsg( "input error: no video stream found !" );
- vlc_mutex_unlock( &p_input->stream.stream_lock );
- return( -1 );
+ intf_Msg( "input error: no video stream found !" );
}
- if( p_es_audio != NULL )
+ if( p_avi_demux->p_info_audio != NULL )
{
- input_SelectES( p_input, p_es_audio );
+ input_SelectES( p_input, p_avi_demux->p_info_audio->p_es );
}
else
{
intf_Msg( "input init: no audio stream found !" );
}
-
- /* p_input->stream.p_selected_area->i_tell = 0; */
- p_input->stream.i_mux_rate = p_avi_demux->avih.i_maxbytespersec / 50;
p_input->stream.p_selected_program->b_is_ok = 1;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return( 0 );
}
+
+/*****************************************************************************
+ * AVIEnd: frees unused data
+ *****************************************************************************/
static void AVIEnd( input_thread_t *p_input )
{
__AVIFreeDemuxData( p_input );
}
-static mtime_t __AVI_GetPTS( AVIStreamInfo_t *p_info )
+static mtime_t AVI_GetPTS( AVIStreamInfo_t *p_info )
{
- /* XXX you need to had p_info->i_date to have correct pts */
- /* p_info->p_index[p_info->i_idxpos] need to be valid !! */
- mtime_t i_pts;
-
+ /* p_info->p_index[p_info->i_idxposc] need to be valid !! */
/* be careful to *1000000 before round ! */
if( p_info->header.i_samplesize != 0 )
{
- i_pts = (mtime_t)( (double)1000000.0 *
- (double)p_info->p_index[p_info->i_idxpos].i_lengthtotal *
+ return( (mtime_t)( (double)1000000.0 *
+ (double)(p_info->p_index[p_info->i_idxposc].i_lengthtotal +
+ p_info->i_idxposb )*
(double)p_info->header.i_scale /
(double)p_info->header.i_rate /
- (double)p_info->header.i_samplesize );
+ (double)p_info->header.i_samplesize ) );
}
else
{
- i_pts = (mtime_t)( (double)1000000.0 *
- (double)p_info->i_idxpos *
+ return( (mtime_t)( (double)1000000.0 *
+ (double)(p_info->i_idxposc ) *
(double)p_info->header.i_scale /
- (double)p_info->header.i_rate);
+ (double)p_info->header.i_rate) );
}
- return( i_pts );
}
-static void __AVI_NextIndexEntry( input_thread_t *p_input,
+static int __AVI_NextIndexEntry( input_thread_t *p_input,
AVIStreamInfo_t *p_info )
{
- p_info->i_idxpos++;
- if( p_info->i_idxpos >= p_info->i_idxnb )
- {
- /* we need to verify if we reach end of file
- or if index is broken and search manually */
- intf_WarnMsg( 1, "input demux: out of index" );
- }
-}
+ AVIIndexEntry_t index;
+ riffchunk_t *p_chunk;
+ AVIStreamInfo_t *p_info_tmp;
+ int i;
+ int i_idxposc;
+ int b_inc = 0;
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data;
-static int __AVI_ReAlign( input_thread_t *p_input,
- AVIStreamInfo_t *p_info )
-{
- u32 u32_pos;
- off_t i_pos;
-
- __RIFF_TellPos( p_input, &u32_pos );
- i_pos = (off_t)u32_pos - (off_t)p_info->i_idxoffset;
-
- /* TODO verifier si on est dans p_movi */
-
- if( p_info->p_index[p_info->i_idxnb-1].i_offset <= i_pos )
+ p_info->i_idxposc++;
+
+ if( p_info->i_idxposc < p_info->i_idxnb )
{
- p_info->i_idxpos = p_info->i_idxnb-1;
- return( 0 );
+ return( 0 );
}
+ if( p_info->i_idxposc > p_info->i_idxnb )
+ {
+ return( -1 );
+ }
+ p_info->i_idxposc--;
- if( i_pos <= p_info->p_index[0].i_offset )
+ /* create entry on the fly */
+ /* search for the more advance stream and parse from it for all streams*/
+ p_info_tmp = p_info;
+ for( i = 0; i < p_avi_demux->i_streams; i++ )
{
- p_info->i_idxpos = 0;
- return( 0 );
+#define p_info_i p_avi_demux->pp_info[i]
+ if( ( p_info_i->p_index )
+ && ( p_info_i->p_index[p_info_i->i_idxnb - 1].i_pos >
+ p_info_tmp->p_index[p_info_tmp->i_idxnb - 1].i_pos ) )
+ {
+ p_info_tmp = p_info_i;
+ }
+#undef p_info_i
}
- /* if we have seek in the current chunk then do nothing
- __AVI_SeekToChunk will correct */
- if( (p_info->p_index[p_info->i_idxpos].i_offset <= i_pos)
- && ( i_pos < p_info->p_index[p_info->i_idxpos].i_offset +
- p_info->p_index[p_info->i_idxpos].i_length ) )
+
+ /* go to last defined entry */
+ i_idxposc = p_info_tmp->i_idxposc; /* save p_info_tmp->i_idxposc */
+ p_info_tmp->i_idxposc = p_info_tmp->i_idxnb - 1;
+ __AVI_SeekToChunk( p_input, p_info_tmp );
+ p_info_tmp->i_idxposc = i_idxposc;
+
+ if( RIFF_NextChunk( p_input, p_avi_demux->p_movi ) != 0 )
{
- return( 0 );
+ p_info->i_idxposc++;
+ return( -1 );
}
- if( i_pos >= p_info->p_index[p_info->i_idxpos].i_offset )
+ /* save idxpos of p_info */
+ /* now parse for all stream and stop when reach next chunk for p_info */
+ for( i = 0; (i < 15)||(!b_inc); i++)
{
- /* search for a chunk after i_idxpos */
- while( (p_info->p_index[p_info->i_idxpos].i_offset < i_pos)
- &&( p_info->i_idxpos < p_info->i_idxnb - 1 ) )
+ int i_number;
+ u16 i_type;
+ if( (p_chunk = RIFF_ReadChunk( p_input )) == NULL )
+ {
+ p_info->i_idxposc++;
+ return( b_inc == 1 ? 0 : -1 );
+ }
+
+ index.i_id = p_chunk->i_id;
+ index.i_flags = AVIIF_KEYFRAME;
+ index.i_pos = p_chunk->i_pos;
+ index.i_length = p_chunk->i_size;
+ RIFF_DeleteChunk( p_input, p_chunk );
+#define p_info_i p_avi_demux->pp_info[i_number]
+ if( (__AVI_ParseStreamHeader( index.i_id, &i_number, &i_type ) == 0)
+ &&( i_number < p_avi_demux->i_streams )
+ &&( p_info_i->p_index )
+ &&( p_info_i->p_index[p_info_i->i_idxnb - 1].i_pos +
+ p_info_i->p_index[p_info_i->i_idxnb - 1].i_length + 8<=
+ index.i_pos )
+ &&( __AVIGetESTypeFromTwoCC( i_type ) == p_info_i->i_cat ) )
{
- p_info->i_idxpos++;
+ __AVI_AddEntryIndex( p_info_i, &index );
+ if( (p_info_i == p_info)&&(!b_inc) )
+ {
+ b_inc = 1;
+ }
}
- while( ((p_info->p_index[p_info->i_idxpos].i_flags&AVIIF_KEYFRAME) == 0)
- &&( p_info->i_idxpos < p_info->i_idxnb - 1 ) )
+#undef p_info_i
+ if( RIFF_NextChunk( p_input, p_avi_demux->p_movi ) != 0 )
{
- p_info->i_idxpos++;
+ p_info->i_idxposc++;
+ return( b_inc == 1 ? 0 : -1 );
}
+
+ }
+
+ p_info->i_idxposc++;
+ return( 0 );
+}
+
+/*****************************************************************************
+ * Functions to acces streams data
+ * Uses it, because i plane to read unseekable stream
+ * Don't work for the moment for unseekable stream
+ *****************************************************************************/
+
+/* __AVI_ReadStreamChunkInPES; load an entire chunk
+ __AVI_ReadStreamBytesInPES; load bytes
+ __AVI_GoToStreamChunk; go to chunk
+ __AVI_GoToStreamBytes; go to bytes in the all stream
+ not seekable file is not yet supported
+ */
+
+static int __AVI_GoToStreamChunk( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info,
+ int i_chunk )
+{
+ u32 u32_pos;
+ off_t i_pos;
+
+ if( !p_input->stream.b_seekable )
+ {
+ intf_ErrMsg( "input error: need the ability to seek in stream" );
+ return( -1 );
}
- else
+
+ if( p_info->p_index != NULL )
{
- /* search for a chunk before i_idxpos */
- while( (p_info->p_index[p_info->i_idxpos].i_offset +
- p_info->p_index[p_info->i_idxpos].i_length >= i_pos)
- &&( p_info->i_idxpos > 0 ) )
+ if( i_chunk >= p_info->i_idxnb )
+ {
+ p_info->i_idxposc = p_info->i_idxnb-1;
+ while( p_info->i_idxposc < i_chunk )
+ {
+ if( __AVI_NextIndexEntry( p_input, p_info ) != 0)
+ {
+ return( -1 );
+ }
+ }
+ }
+ else
{
- p_info->i_idxpos--;
+ p_info->i_idxposc = i_chunk;
}
- while( ((p_info->p_index[p_info->i_idxpos].i_flags&AVIIF_KEYFRAME) == 0)
- &( p_info->i_idxpos > 0 ) )
+
+ /* now do we have valid index for the chunk */
+ __RIFF_TellPos( p_input, &u32_pos );
+
+ i_pos = (off_t)p_info->p_index[i_chunk].i_pos;
+ if( i_pos != u32_pos )
{
- p_info->i_idxpos--;
+ p_input->pf_seek( p_input, i_pos );
+ input_AccessReinit( p_input );
}
+ p_info->i_idxposb = 0;
+ return( 0 );
+ }
+ else
+ {
+ return( -1 );
}
-
- return( 0 );
}
-static void __AVI_SynchroReInit( input_thread_t *p_input,
- AVIStreamInfo_t *p_info_master,
- AVIStreamInfo_t *p_info_slave )
-{
- demux_data_avi_file_t *p_avi_demux;
- p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data;
- p_avi_demux->i_date = mdate() + DEFAULT_PTS_DELAY
- - __AVI_GetPTS( p_info_master );
- /* TODO: a optimiser */
- p_info_slave->i_idxpos = 0;
- p_info_slave->b_unselected = 1; /* to correct audio */
- p_input->stream.p_selected_program->i_synchro_state = SYNCHRO_OK;
-}
-/** -1 in case of error, 0 of EOF, 1 otherwise **/
-static int AVIDemux( input_thread_t *p_input )
+
+static int __AVI_GoToStreamBytes( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info,
+ int i_byte )
{
- /* on cherche un block
- plusieurs cas :
- * encapsuler dans un chunk "rec "
- * juste une succesion de 00dc 01wb ...
- * pire tout audio puis tout video ou vice versa
- */
-/* TODO : * a better method to realign
- * verify that we are reading in p_movi
- * XXX be sure to send audio before video to avoid click
- *
- */
- riffchunk_t *p_chunk;
- int i;
- pes_packet_t *p_pes;
- demux_data_avi_file_t *p_avi_demux;
+ u32 u32_pos;
+ off_t i_pos;
- AVIStreamInfo_t *p_info_video;
- AVIStreamInfo_t *p_info_audio;
- AVIStreamInfo_t *p_info;
- /* XXX arrive pas a avoir acces a cette fct°
- input_ClockManageRef( p_input,
- p_input->stream.p_selected_program,
- (mtime_t)0 ); */
- p_avi_demux = (demux_data_avi_file_t*)p_input->p_demux_data;
+ if( !p_input->stream.b_seekable )
+ {
+ intf_ErrMsg( "input error: need the ability to seek in stream" );
+ return( -1 );
+ }
- /* search video and audio stream selected */
- p_info_video = NULL;
- p_info_audio = NULL;
-
- for( i = 0; i < p_avi_demux->i_streams; i++ )
+ /* now do we have valid index for the chunk */
+ if( p_info->p_index != NULL )
{
- if( p_avi_demux->pp_info[i]->p_es->p_decoder_fifo != NULL )
+ if( p_info->p_index[p_info->i_idxnb - 1].i_lengthtotal +
+ p_info->p_index[p_info->i_idxnb - 1].i_length <= i_byte)
{
- switch( p_avi_demux->pp_info[i]->p_es->i_cat )
+ p_info->i_idxposc = p_info->i_idxnb - 1;
+ while( p_info->p_index[p_info->i_idxposc].i_lengthtotal +
+ p_info->p_index[p_info->i_idxposc].i_length <= i_byte)
{
- case( VIDEO_ES ):
- p_info_video = p_avi_demux->pp_info[i];
- break;
- case( AUDIO_ES ):
- p_info_audio = p_avi_demux->pp_info[i];
- break;
+ if( __AVI_NextIndexEntry( p_input, p_info ) != 0)
+ {
+ return( -1 );
+ }
}
}
else
{
- p_avi_demux->pp_info[i]->b_unselected = 1;
+ /* uses dichototmie to be fast enougth */
+ int i_idxposc = p_info->i_idxposc;
+ int i_idxmax = p_info->i_idxnb;
+ int i_idxmin = 0;
+ for( ;; )
+ {
+ if( p_info->p_index[i_idxposc].i_lengthtotal > i_byte )
+ {
+ i_idxmax = i_idxposc ;
+ i_idxposc = ( i_idxmin + i_idxposc ) / 2 ;
+ }
+ else
+ {
+ if( p_info->p_index[i_idxposc].i_lengthtotal +
+ p_info->p_index[i_idxposc].i_length <= i_byte)
+ {
+ i_idxmin = i_idxposc ;
+ i_idxposc = (i_idxmax + i_idxposc ) / 2 ;
+ }
+ else
+ {
+ p_info->i_idxposc = i_idxposc;
+ break;
+ }
+ }
+ }
+ }
+
+ p_info->i_idxposb = i_byte -
+ p_info->p_index[p_info->i_idxposc].i_lengthtotal;
+
+ i_pos = (off_t)p_info->p_index[p_info->i_idxposc].i_pos +
+ p_info->i_idxposb + 8;
+ __RIFF_TellPos( p_input, &u32_pos );
+ if( u32_pos != i_pos )
+ {
+ p_input->pf_seek( p_input, i_pos );
+ input_AccessReinit( p_input );
}
+ return( 0 );
}
- if( p_info_video == NULL )
+ else
{
- intf_ErrMsg( "input error: no video ouput selected" );
return( -1 );
}
- if( input_ClockManageControl( p_input, p_input->stream.p_selected_program,
- (mtime_t)0) == PAUSE_S )
- {
- __AVI_SynchroReInit( p_input, p_info_video, p_info_audio );
+}
+
+static pes_packet_t *__AVI_ReadStreamChunkInPES( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info )
+{
+ pes_packet_t *p_pes;
+ if( p_info->i_idxposc >= p_info->i_idxnb )
+ {
+ return( NULL );
}
- /* after updated p_avi_demux->pp_info[i]->b_unselected !! */
- if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
- {
- /* TODO check if we have seek */
- __AVI_ReAlign( p_input, p_info_video ); /*on se realigne pr la video */
- __AVI_SynchroReInit( p_input, p_info_video, p_info_audio );
+ if( ( __AVI_GoToStreamChunk( p_input, p_info, p_info->i_idxposc ) != 0 )
+ ||( RIFF_LoadChunkDataInPES( p_input, &p_pes) != 0 ) )
+ {
+ return( NULL );
+ }
+ else
+ {
+ __AVI_NextIndexEntry( p_input, p_info);
+ p_info->i_idxposb = 0;
+ return( p_pes );
}
+}
+
+static pes_packet_t *__AVI_ReadStreamBytesInPES( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info,
+ int i_byte )
+{
+ pes_packet_t *p_pes = NULL;
+ data_packet_t *p_data;
+ int i_read;
- /* update i_date if previously unselected ES (ex: 2 channels audio ) */
- if( (p_info_audio != NULL)&&(p_info_audio->b_unselected ))
+ if( ( p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
{
- /* we have to go to the good pts */
- /* we will reach p_info_ok pts */
- while( __AVI_GetPTS( p_info_audio) < __AVI_GetPTS( p_info_video) )
+ return( NULL );
+ }
+
+ while( i_byte > 0 )
+ {
+ if( p_info->i_idxposc >= p_info->i_idxnb )
+ {
+ input_DeletePES( p_input->p_method_data, p_pes );
+ return( NULL );
+ }
+ if( __AVI_GoToStreamBytes( p_input, p_info,
+ p_info->p_index[p_info->i_idxposc].i_lengthtotal +
+ p_info->i_idxposb ) != 0 )
+ {
+ input_DeletePES( p_input->p_method_data, p_pes );
+ return( NULL );
+ }
+
+ i_read = input_SplitBuffer(p_input, &p_data,
+ __MIN( i_byte,
+ p_info->p_index[p_info->i_idxposc].i_length
+ - p_info->i_idxposb ) );
+ if( i_read <= 0 )
+ {
+ input_DeletePES( p_input->p_method_data, p_pes );
+ return( NULL );
+ }
+ p_pes->i_nb_data++;
+ if( !p_pes->p_first )
+ {
+ p_pes->p_first = p_data;
+ }
+ else
{
- __AVI_NextIndexEntry( p_input, p_info_audio );
+ p_pes->p_last->p_next = p_data;
}
- p_info_audio->b_unselected = 0 ;
+ p_pes->p_last = p_data;
+ p_pes->i_pes_size += i_read;
+
+ i_byte -= i_read;
+ p_info->i_idxposb += i_read;
+
+ if( p_info->i_idxposb >= p_info->p_index[p_info->i_idxposc].i_length )
+ {
+ p_info->i_idxposb = 0;
+ if( __AVI_NextIndexEntry( p_input, p_info) != 0 )
+ {
+ input_DeletePES( p_input->p_method_data, p_pes );
+ return( NULL );
+ }
+ }
+ };
+ return( p_pes );
+}
+
+/*****************************************************************************
+ * Function to convert pts to chunk or byte
+ *****************************************************************************/
+
+static inline mtime_t __AVI_PTSToChunk( AVIStreamInfo_t *p_info,
+ mtime_t i_pts )
+{
+ return( (mtime_t)((double)i_pts *
+ (double)p_info->header.i_rate /
+ (double)p_info->header.i_scale /
+ (double)1000000.0 ) );
+}
+
+static inline mtime_t __AVI_PTSToByte( AVIStreamInfo_t *p_info,
+ mtime_t i_pts )
+{
+ return( (mtime_t)((double)i_pts *
+ (double)p_info->header.i_samplesize *
+ (double)p_info->header.i_rate /
+ (double)p_info->header.i_scale /
+ (double)1000000.0 ) );
+
+}
+
+/* try to realign after a seek */
+static int AVI_ReAlign( input_thread_t *p_input )
+{
+ u32 u32_pos;
+ off_t i_pos;
+ int b_after = 0;
+ AVIStreamInfo_t *p_info;
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data;
+
+ p_info = (p_avi_demux->p_info_video != NULL ) ?
+ p_avi_demux->p_info_video :
+ p_avi_demux->p_info_audio;
+
+ __RIFF_TellPos( p_input, &u32_pos );
+
+ i_pos = (off_t)u32_pos;
+
+ if( i_pos <= p_info->p_index[0].i_pos )
+ {
+ /* before beginning of stream */
+ if( !p_info->header.i_samplesize )
+ {
+ __AVI_GoToStreamChunk( p_input,
+ p_info,
+ 0 );
+ }
+ else
+ {
+ __AVI_GoToStreamBytes( p_input,
+ p_info,
+ 0);
+ }
+ return( 0 );
}
-
- /* what stream we should read in first */
- if( p_info_audio == NULL )
+
+ if( (p_info->p_index[p_info->i_idxposc].i_pos <= i_pos)
+ && ( i_pos < p_info->p_index[p_info->i_idxposc].i_pos +
+ p_info->p_index[p_info->i_idxposc].i_length ) )
+ {
+ /* FIXME if master == audio and samplesize != 0 */
+ /* don't work with big chunk */
+ /* don't do anything we are in the current chunk */
+ return( 0 );
+ }
+ if( i_pos < p_info->p_index[p_info->i_idxposc].i_pos )
{
- p_info = p_info_video;
+ b_after = 0;
}
else
{
- if( __AVI_GetPTS( p_info_audio ) <=
- __AVI_GetPTS( p_info_video ) )
+ b_after = 1;
+ }
+ /* now find in what chunk we are */
+ while( ( i_pos < p_info->p_index[p_info->i_idxposc].i_pos )
+ &&( p_info->i_idxposc > 0 ) )
+ {
+ /* search before i_idxposc */
+ p_info->i_idxposc--;
+ }
+
+ while( i_pos >= p_info->p_index[p_info->i_idxposc].i_pos +
+ p_info->p_index[p_info->i_idxposc].i_length + 8 )
+ {
+ /* search after i_idxposc */
+ if( __AVI_NextIndexEntry( p_input, p_info ) != 0 )
+ {
+ return( -1 );
+ }
+ }
+ /* search nearest key frame */
+ if( ( !p_info->header.i_samplesize ) && ( p_info->i_cat == VIDEO_ES ) )
+ {
+ /* only for chunk stream */
+ if( b_after )
+ {
+ while(!(p_info->p_index[p_info->i_idxposc].i_flags&AVIIF_KEYFRAME) )
+ {
+ if( __AVI_NextIndexEntry( p_input, p_info ) != 0 )
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ while( ( p_info->i_idxposc > 0 ) &&
+ (!(p_info->p_index[p_info->i_idxposc].i_flags&AVIIF_KEYFRAME)) )
+ {
+ p_info->i_idxposc--;
+ }
+ }
+ }
+ __AVI_GoToStreamChunk( p_input, p_info, p_info->i_idxposc );
+
+ return( 0 );
+}
+
+/* make difference between audio and video pts as little as possible */
+static void AVI_SynchroReInit( input_thread_t *p_input )
+{
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data;
+#define p_info_video p_avi_demux->p_info_video
+#define p_info_audio p_avi_demux->p_info_audio
+ if( ( p_info_video )&&( p_info_audio ) )
+ {
+ /* now resynch audio video video */
+ /*don't care of AVIF_KEYFRAME */
+ if( !p_info_audio->header.i_samplesize )
{
- p_info = p_info_audio;
+ int i_chunk = __AVI_PTSToChunk( p_info_audio,
+ AVI_GetPTS( p_info_video ));
+ __AVI_GoToStreamChunk( p_input,
+ p_info_audio,
+ i_chunk );
}
else
{
- p_info = p_info_video;
+ int i_byte = __AVI_PTSToByte( p_info_audio,
+ AVI_GetPTS( p_info_video ) ) ;
+ __AVI_GoToStreamBytes( p_input,
+ p_info_audio,
+ i_byte );
}
+ }
+#undef p_info_video
+#undef p_info_audio
+}
+
+/*****************************************************************************
+ * AVI_GetFrameInPES : get dpts length(µs) in pes from stream
+ *****************************************************************************
+ * Handle multiple pes, and set pts to the good value
+ *****************************************************************************/
+static pes_packet_t *AVI_GetFrameInPES( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info,
+ mtime_t i_dpts)
+{
+ int i;
+ pes_packet_t *p_pes = NULL;
+ pes_packet_t *p_pes_tmp = NULL;
+ pes_packet_t *p_pes_first = NULL;
+ mtime_t i_pts;
+
+ /* we now that p_info != NULL */
+ if( i_dpts < 1000 )
+ {
+ return( NULL ) ;
}
- /* go to the good chunk to read */
+ if( !p_info->header.i_samplesize )
+ {
+ /* stream is chunk based , easy */
+ int i_chunk = __MAX( __AVI_PTSToChunk( p_info, i_dpts), 1 );
+ /* at least one frame */
+ /* read them */
+ p_pes_first = NULL;
+ for( i = 0; i < i_chunk; i++ )
+ {
+ /* get pts while is valid */
+ i_pts = AVI_GetPTS( p_info );
+ p_pes_tmp = __AVI_ReadStreamChunkInPES( p_input, p_info );
- __AVI_SeekToChunk( p_input, p_info );
-
- /* now we just need to read a chunk */
- if( (p_chunk = RIFF_ReadChunk( p_input )) == NULL )
- {
- intf_ErrMsg( "input demux: cannot read chunk" );
- return( -1 );
+ if( !p_pes_tmp )
+ {
+ return( p_pes_first );
+ }
+ p_pes_tmp->i_pts = i_pts;
+ if( !p_pes_first )
+ {
+ p_pes_first = p_pes_tmp;
+ }
+ else
+ {
+ p_pes->p_next = p_pes_tmp;
+ }
+ p_pes = p_pes_tmp;
+ }
+ return( p_pes_first );
}
+ else
+ {
+ /* stream is byte based */
+ int i_byte = __AVI_PTSToByte( p_info, i_dpts);
+ if( !i_byte )
+ {
+ return( NULL );
+ }
+ /* at least one Kbyte */
+ /*i_byte = __MIN( 1024*1000, i_byte ); *//* but no more than 1000ko */
+ i_pts = AVI_GetPTS( p_info );
- if( (p_chunk->i_id&0xFFFF0000) !=
- (p_info->p_index[p_info->i_idxpos].i_id&0xFFFF0000) )
+ p_pes = __AVI_ReadStreamBytesInPES( p_input, p_info, i_byte);
+
+ if( p_pes != NULL )
+ {
+ p_pes->i_pts = i_pts;
+ }
+ return( p_pes );
+ }
+}
+/*****************************************************************************
+ * AVI_DecodePES : send a pes to decoder
+ *****************************************************************************
+ * Handle multiple pes, and set pts to the good value
+ *****************************************************************************/
+static inline void AVI_DecodePES( input_thread_t *p_input,
+ AVIStreamInfo_t *p_info,
+ pes_packet_t *p_pes )
+{
+ pes_packet_t *p_pes_next;
+ /* input_decode want only one pes, but AVI_GetFrameInPES give
+ multiple pes so send one by one */
+ /* we now that p_info != NULL */
+ while( p_pes )
+ {
+ p_pes_next = p_pes->p_next;
+ p_pes->p_next = NULL;
+ p_pes->i_pts = input_ClockGetTS( p_input,
+ p_input->stream.p_selected_program,
+ p_pes->i_pts * 9/100);
+ input_DecodePES( p_info->p_es->p_decoder_fifo, p_pes );
+ p_pes = p_pes_next;
+ };
+
+}
+
+/*****************************************************************************
+ * AVIDemux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int AVIDemux( input_thread_t *p_input )
+{
+ int i;
+ pes_packet_t *p_pes;
+ AVIStreamInfo_t *p_info_master;
+ AVIStreamInfo_t *p_info_slave;
+
+ demux_data_avi_file_t *p_avi_demux =
+ (demux_data_avi_file_t*)p_input->p_demux_data;
+
+ /* search new video and audio stream selected
+ if current have been unselected*/
+ if( ( !p_avi_demux->p_info_video )
+ || ( !p_avi_demux->p_info_video->p_es->p_decoder_fifo ) )
{
- intf_WarnMsg( 2, "input demux: bad index entry" );
- __AVI_NextIndexEntry( p_input, p_info );
- return( 1 );
+ p_avi_demux->p_info_video = NULL;
+ for( i = 0; i < p_avi_demux->i_streams; i++ )
+ {
+ if( ( p_avi_demux->pp_info[i]->i_cat == VIDEO_ES )
+ &&( p_avi_demux->pp_info[i]->p_es->p_decoder_fifo != NULL ) )
+ {
+ p_avi_demux->p_info_video = p_avi_demux->pp_info[i];
+ p_avi_demux->p_info_video->b_selected = 1;
+ break;
+ }
+ }
}
-
- intf_WarnMsg( 6, "input demux: read %4.4s chunk %d bytes",
- (char*)&p_chunk->i_id,
- p_chunk->i_size);
-
- if( RIFF_LoadChunkDataInPES(p_input, p_chunk, &p_pes) != 0 )
+ if( ( !p_avi_demux->p_info_audio )
+ ||( !p_avi_demux->p_info_audio->p_es->p_decoder_fifo ) )
{
- intf_ErrMsg( "input error: cannot read data" );
+ p_avi_demux->p_info_audio = NULL;
+ for( i = 0; i < p_avi_demux->i_streams; i++ )
+ {
+ if( ( p_avi_demux->pp_info[i]->i_cat == AUDIO_ES )
+ &&( p_avi_demux->pp_info[i]->p_es->p_decoder_fifo != NULL ) )
+ {
+ p_avi_demux->p_info_audio = p_avi_demux->pp_info[i];
+ p_avi_demux->p_info_audio->b_selected = 1;
+ break;
+ }
+ }
+ }
+ /* by default video is master for resync audio (after a seek .. ) */
+ if( p_avi_demux->p_info_video )
+ {
+ p_info_master = p_avi_demux->p_info_video;
+ p_info_slave = p_avi_demux->p_info_audio;
+ }
+ else
+ {
+ p_info_master = p_avi_demux->p_info_audio;
+ p_info_slave = NULL;
+ }
+
+ if( !p_info_master )
+ {
+ intf_ErrMsg( "input error: no stream selected" );
return( -1 );
}
- p_pes->i_rate = p_input->stream.control.i_rate;
- p_pes->i_pts = p_avi_demux->i_date + __AVI_GetPTS( p_info );
- p_pes->i_dts = 0;
-
- __AVI_NextIndexEntry( p_input, p_info );
- /* send to decoder */
- vlc_mutex_lock( &p_info->p_es->p_decoder_fifo->data_lock );
- if( p_info->p_es->p_decoder_fifo->i_depth >= MAX_PACKETS_IN_FIFO )
+ /* check for signal from interface */
+ if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
+ {
+ /* we can supposed that is a seek */
+ /* first wait for empty buffer, arbitrary time */
+ msleep( DEFAULT_PTS_DELAY );
+ /* then try to realign in stream */
+ if( AVI_ReAlign( p_input ) != 0 )
+ {
+ return( 0 ); /* assume EOF */
+ }
+ AVI_SynchroReInit( p_input );
+ }
+ /* manage rate, if not default: skeep audio */
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ if( p_input->stream.control.i_rate != p_avi_demux->i_rate )
{
- /* Wait for the decoder. */
- vlc_cond_wait( &p_info->p_es->p_decoder_fifo->data_wait,
- &p_info->p_es->p_decoder_fifo->data_lock );
+ if( p_avi_demux->p_info_audio)
+ {
+ p_avi_demux->p_info_audio->b_selected = 1;
+ }
+ p_avi_demux->i_rate = p_input->stream.control.i_rate;
}
- vlc_mutex_unlock( &p_info->p_es->p_decoder_fifo->data_lock );
- input_DecodePES( p_info->p_es->p_decoder_fifo, p_pes );
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ if( p_avi_demux->i_rate != DEFAULT_RATE )
+ {
+ p_info_slave = NULL;
+ }
+
+ /* take care of newly selected audio ES */
+ if( p_info_master->b_selected )
+ {
+ p_info_master->b_selected = 0;
+ AVI_SynchroReInit( p_input );
+ }
+ if( ( p_info_slave )&&( p_info_slave->b_selected ) )
+ {
+ p_info_slave->b_selected = 0;
+ AVI_SynchroReInit( p_input );
+ }
+
+ /* wait for the good time */
+ input_ClockManageRef( p_input,
+ p_input->stream.p_selected_program,
+ p_avi_demux->i_pcr );
+ /* calculate pcr, time when we must read the next data */
+ /* 9/100 kludge ->need to convert to 1/1000000 clock unit to 1/90000 */
+ if( p_info_slave )
+ {
+ p_avi_demux->i_pcr = __MIN( AVI_GetPTS( p_info_master ),
+ AVI_GetPTS( p_info_slave ) ) * 9/100;
+ }
+ else
+ {
+ p_avi_demux->i_pcr = AVI_GetPTS( p_info_master ) * 9/100;
+ }
+
+ /* get video and audio frames */
+ p_pes = AVI_GetFrameInPES( p_input,
+ p_info_master,
+ 100000 ); /* 100 ms */
- return( 1 );
+ AVI_DecodePES( p_input,
+ p_info_master,
+ p_pes);
+ if( p_info_slave )
+ {
+ p_pes = AVI_GetFrameInPES( p_input,
+ p_info_slave,
+ AVI_GetPTS( p_info_master ) -
+ AVI_GetPTS( p_info_slave));
+ AVI_DecodePES( p_input,
+ p_info_slave,
+ p_pes );
+ }
+ /* at the end ? */
+ return( p_info_master->i_idxposc >= p_info_master->i_idxnb ? 0 : 1 );
}