X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Freal.c;h=720528dc810d2235d9637223a95dfa07b97c7fba;hb=04a5bdf3fb10b76f5c44df4816834fd1cbb60bfd;hp=7b634296ba1faf74cc02ef4b39f03d3085fe411d;hpb=bf6da8b847557674bf35cfc0eb90401372108393;p=vlc diff --git a/modules/demux/real.c b/modules/demux/real.c index 7b634296ba..720528dc81 100644 --- a/modules/demux/real.c +++ b/modules/demux/real.c @@ -31,42 +31,49 @@ * - cook is ok. * - raac, racp are ok. * - dnet is twisted "The byte order of the data is reversed - * from standard AC3" - * - 28_8 seem problematic. - * - sipr should be fine, but our decoder suxx :) + * from standard AC3" but ok + * - 28_8 is ok. + * - sipr is ok. * - ralf is unsupported, but hardly any sample exist. * - mp3 is unsupported, one sample exists... * * Real Audio Only * --------------- - * Not supported... + * v3 and v4/5 headers are parsed. + * Doesn't work yet... */ /***************************************************************************** * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include #include #include #include +#include + /***************************************************************************** * Module descriptor *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); -vlc_module_begin(); - set_description( _("Real demuxer" ) ); - set_capability( "demux2", 15 ); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_DEMUX ); - set_callbacks( Open, Close ); - add_shortcut( "real" ); - add_shortcut( "rm" ); -vlc_module_end(); +vlc_module_begin () + set_description( N_("Real demuxer" ) ) + set_capability( "demux", 50 ) + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_DEMUX ) + set_callbacks( Open, Close ) + add_shortcut( "real", "rm" ) +vlc_module_end () /***************************************************************************** * Local prototypes @@ -79,21 +86,36 @@ typedef struct es_out_id_t *p_es; - int i_frame; + unsigned i_frame_size; + + int i_frame_num; + unsigned i_frame_pos; + int i_frame_slice; + int i_frame_slice_count; block_t *p_frame; int i_subpacket_h; int i_subpacket_size; int i_coded_frame_size; - int i_frame_size; int i_subpacket; int i_subpackets; block_t **p_subpackets; + mtime_t *p_subpackets_timecode; int i_out_subpacket; + block_t *p_sipr_packet; + int i_sipr_subpacket_count; + mtime_t i_last_dts; } real_track_t; +typedef struct +{ + uint32_t i_file_offset; + uint32_t i_time_offset; + uint32_t i_frame_index; +} real_index_t; + struct demux_sys_t { int64_t i_data_offset; @@ -102,8 +124,9 @@ struct demux_sys_t uint32_t i_data_packets; int64_t i_data_offset_next; - int i_our_duration; - int i_mux_rate; + bool b_real_audio; + + int64_t i_our_duration; char* psz_title; char* psz_artist; @@ -113,19 +136,37 @@ struct demux_sys_t int i_track; real_track_t **track; + size_t i_buffer; uint8_t buffer[65536]; int64_t i_pcr; + + int64_t i_index_offset; + bool b_seek; + real_index_t *p_index; }; -static int Demux( demux_t *p_demux ); -static int Control( demux_t *p_demux, int i_query, va_list args ); +static const unsigned char i_subpacket_size_sipr[4] = { 29, 19, 37, 20 }; + +static int Demux( demux_t * ); +static int Control( demux_t *, int i_query, va_list args ); + + +static void DemuxVideo( demux_t *, real_track_t *tk, mtime_t i_dts, unsigned i_flags ); +static void DemuxAudio( demux_t *, real_track_t *tk, mtime_t i_pts, unsigned i_flags ); + +static int ControlSeekByte( demux_t *, int64_t i_bytes ); +static int ControlSeekTime( demux_t *, mtime_t i_time ); static int HeaderRead( demux_t *p_demux ); -static int ReadCodecSpecificData( demux_t *p_demux, int i_len, int i_num ); +static int CodecParse( demux_t *p_demux, int i_len, int i_num ); -// Map flavour to bytes per second -static int sipr_fl2bps[4] = {813, 1062, 625, 2000}; // 6.5, 8.5, 5, 16 kbit per second +static void RVoid( const uint8_t **pp_data, int *pi_data, int i_size ); +static int RLength( const uint8_t **pp_data, int *pi_data ); +static uint8_t R8( const uint8_t **pp_data, int *pi_data ); +static uint16_t R16( const uint8_t **pp_data, int *pi_data ); +static uint32_t R32( const uint8_t **pp_data, int *pi_data ); +static void SiprPacketReorder(uint8_t *buf, int sub_packet_h, int framesize); /***************************************************************************** * Open @@ -136,41 +177,52 @@ static int Open( vlc_object_t *p_this ) demux_sys_t *p_sys; const uint8_t *p_peek; + bool b_real_audio = false; + + if( stream_Peek( p_demux->s, &p_peek, 10 ) < 10 ) + return VLC_EGENERIC; - if( stream_Peek( p_demux->s, &p_peek, 10 ) < 10 ) return VLC_EGENERIC; - if( memcmp( p_peek, ".RMF", 4 ) ) return VLC_EGENERIC; + /* Real Audio */ + if( !memcmp( p_peek, ".ra", 3 ) ) + { + msg_Err( p_demux, ".ra files unsuported" ); + b_real_audio = true; + } + /* Real Media Format */ + else if( memcmp( p_peek, ".RMF", 4 ) ) + { + return VLC_EGENERIC; + } /* Fill p_demux field */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; - p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); - memset( p_sys, 0, sizeof( demux_sys_t ) ); + + p_demux->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); + if( !p_sys ) + return VLC_ENOMEM; + p_sys->i_data_offset = 0; p_sys->i_track = 0; p_sys->track = NULL; - p_sys->i_pcr = 1; + p_sys->i_pcr = VLC_TS_INVALID; + p_sys->b_seek = false; + p_sys->b_real_audio = b_real_audio; /* Parse the headers */ - if( HeaderRead( p_demux ) ) + /* Real Audio files */ + if( b_real_audio ) + { + CodecParse( p_demux, 32, 0 ); /* At least 32 */ + return VLC_EGENERIC; /* We don't know how to read + correctly the data yet */ + } + /* RMF files */ + else if( HeaderRead( p_demux ) ) { - int i; msg_Err( p_demux, "invalid header" ); - for( i = 0; i < p_sys->i_track; i++ ) - { - real_track_t *tk = p_sys->track[i]; - - if( tk->p_es ) - { - es_out_Del( p_demux->out, tk->p_es ); - } - free( tk ); - } - if( p_sys->i_track > 0 ) - { - free( p_sys->track ); - } - free( p_sys ); + Close( p_this ); return VLC_EGENERIC; } @@ -184,32 +236,39 @@ static void Close( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys = p_demux->p_sys; - int i; - for( i = 0; i < p_sys->i_track; i++ ) + for( int i = 0; i < p_sys->i_track; i++ ) { real_track_t *tk = p_sys->track[i]; - int j = tk->i_subpackets; - if( tk->p_frame ) block_Release( tk->p_frame ); es_format_Clean( &tk->fmt ); - while( j-- ) + if( tk->p_frame ) + block_Release( tk->p_frame ); + + for( int j = 0; j < tk->i_subpackets; j++ ) { if( tk->p_subpackets[ j ] ) block_Release( tk->p_subpackets[ j ] ); } - if( tk->i_subpackets ) free( tk->p_subpackets ); - + if( tk->i_subpackets ) + { + free( tk->p_subpackets ); + free( tk->p_subpackets_timecode ); + } + if( tk->p_sipr_packet ) + block_Release( tk->p_sipr_packet ); free( tk ); } + if( p_sys->i_track > 0 ) + free( p_sys->track ); - if( p_sys->psz_title ) free( p_sys->psz_title ); - if( p_sys->psz_artist ) free( p_sys->psz_artist ); - if( p_sys->psz_copyright ) free( p_sys->psz_copyright ); - if( p_sys->psz_description ) free( p_sys->psz_description ); + free( p_sys->psz_title ); + free( p_sys->psz_artist ); + free( p_sys->psz_copyright ); + free( p_sys->psz_description ); + free( p_sys->p_index ); - if( p_sys->i_track > 0 ) free( p_sys->track ); free( p_sys ); } @@ -221,22 +280,16 @@ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; uint8_t header[18]; - int i_size, i_id, i_flags, i; - int64_t i_pts; - real_track_t *tk = NULL; - vlc_bool_t b_selected; if( p_sys->i_data_packets >= p_sys->i_data_packets_count && p_sys->i_data_packets_count ) { if( stream_Read( p_demux->s, header, 18 ) < 18 ) - { return 0; - } - if( strncmp( (char *)header, "DATA", 4 ) ) - { + + if( memcmp( header, "DATA", 4 ) ) return 0; - } + p_sys->i_data_offset = stream_Tell( p_demux->s ) - 18; p_sys->i_data_size = GetDWBE( &header[4] ); p_sys->i_data_packets_count = GetDWBE( &header[10] ); @@ -245,386 +298,66 @@ static int Demux( demux_t *p_demux ) msg_Dbg( p_demux, "entering new DATA packets=%d next=%u", p_sys->i_data_packets_count, - (uint32_t)p_sys->i_data_offset_next ); + (unsigned int)p_sys->i_data_offset_next ); } - if( stream_Read( p_demux->s, header, 12 ) < 12 ) return 0; - - i_size = GetWBE( &header[2] ) - 12; - i_id = GetWBE( &header[4] ); - i_pts = 1000 * GetDWBE( &header[6] ); - i_pts += 1000; /* Avoid 0 pts */ - i_flags= header[11]; /* flags 0x02 -> keyframe */ - -#if 0 - msg_Dbg( p_demux, "packet %d size=%d id=%d pts=%u", - p_sys->i_data_packets, i_size, i_id, (uint32_t)(i_pts/1000) ); -#endif + /* Read Packet Header */ + if( stream_Read( p_demux->s, header, 12 ) < 12 ) + return 0; + //const int i_version = GetWBE( &header[0] ); + const size_t i_size = GetWBE( &header[2] ) - 12; + const int i_id = GetWBE( &header[4] ); + const int64_t i_pts = VLC_TS_0 + 1000 * GetDWBE( &header[6] ); + const int i_flags= header[11]; /* flags 0x02 -> keyframe */ p_sys->i_data_packets++; + if( i_size > sizeof(p_sys->buffer) ) + { + msg_Err( p_demux, "Got a NUKK size to read. (Invalid format?)" ); + return 1; + } - stream_Read( p_demux->s, p_sys->buffer, i_size ); + p_sys->i_buffer = stream_Read( p_demux->s, p_sys->buffer, i_size ); + if( p_sys->i_buffer < i_size ) + return 0; - for( i = 0; i < p_sys->i_track; i++ ) + real_track_t *tk = NULL; + for( int i = 0; i < p_sys->i_track; i++ ) { - if( p_sys->track[i]->i_id == i_id ) tk = p_sys->track[i]; + if( p_sys->track[i]->i_id == i_id ) + tk = p_sys->track[i]; } - if( tk == NULL ) + if( !tk ) { msg_Warn( p_demux, "unknown track id(0x%x)", i_id ); return 1; } - es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b_selected ); - if( tk->fmt.i_cat == VIDEO_ES && b_selected ) + if( tk->fmt.i_cat == VIDEO_ES ) { - uint8_t *p = p_sys->buffer; - - while( p < &p_sys->buffer[i_size - 2] ) - { - uint8_t h = *p++; - int i_len = 0; - int i_copy; - int i_subseq = 0; - int i_seqnum = 0; - int i_offset = 0; - - if( (h&0xc0) == 0x40 ) - { - /* Short header */ - p++; - i_len = &p_sys->buffer[i_size] - p; - } - else - { - if( (h&0x40) == 0 ) - { - i_subseq = (*p++)&0x7f; - } - i_len = (p[0] << 8)|p[1]; p += 2; - if( (i_len&0xc000) == 0 ) - { - i_len <<= 16; - i_len |= (p[0] << 8)|p[1]; p += 2; - i_len &= 0x3fffffff; - } - else - { - i_len &= 0x3fff; - } - - i_offset = (p[0] << 8)|p[1]; p += 2; - if( (i_offset&0xc000) == 0 ) - { - i_offset <<= 16; - i_offset |= (p[0] << 8)|p[1]; p += 2; - i_offset &= 0x3fffffff; - } - else - { - i_offset &= 0x3fff; - } - i_seqnum = *p++; - } - - i_copy = i_len - i_offset; - if( i_copy > &p_sys->buffer[i_size] - p ) - { - i_copy = &p_sys->buffer[i_size] - p; - } - else if( i_copy < 0 ) - { - break; - } - - msg_Dbg( p_demux, " - len=%d offset=%d size=%d subseq=%d seqnum=%d", - i_len, i_offset, i_copy, i_subseq, i_seqnum ); - - if( (h&0xc0) == 0x80 ) - { - /* last fragment -> fixes */ - i_copy = i_offset; - i_offset = i_len - i_copy; - msg_Dbg( p_demux, "last fixing copy=%d offset=%d", - i_copy, i_offset ); - } - - if( tk->p_frame && - ( tk->p_frame->i_dts != i_pts || - tk->i_frame != i_len ) ) - { - msg_Dbg( p_demux, "sending size=%d", tk->p_frame->i_buffer ); - - if( p_sys->i_pcr < tk->p_frame->i_dts ) - { - p_sys->i_pcr = tk->p_frame->i_dts; - - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - (int64_t)p_sys->i_pcr ); - } - es_out_Send( p_demux->out, tk->p_es, tk->p_frame ); - - tk->i_frame = 0; - tk->p_frame = NULL; - } - - if( (h&0xc0) != 0x80 && (h&0xc0) != 0x00 && !tk->p_frame ) - { - /* no fragment */ - i_len = i_copy; - i_offset = 0; - } - - - if( tk->p_frame == NULL ) - { - msg_Dbg( p_demux, "new frame size=%d", i_len ); - tk->i_frame = i_len; - if( !( tk->p_frame = block_New( p_demux, i_len + 8 + 1000) ) ) - { - return -1; - } - memset( &tk->p_frame->p_buffer[8], 0, i_len ); - tk->p_frame->i_dts = i_pts; - tk->p_frame->i_pts = i_pts; - - ((uint32_t*)tk->p_frame->p_buffer)[0] = i_len; /* len */ - ((uint32_t*)tk->p_frame->p_buffer)[1] = 0; /* chunk counts */ - } - - if( i_offset < tk->i_frame) - { - int i_ck = ((uint32_t*)tk->p_frame->p_buffer)[1]++; - - msg_Dbg( p_demux, "copying new buffer n=%d offset=%d copy=%d", - i_ck, i_offset, i_copy ); - - ((uint32_t*)(tk->p_frame->p_buffer+i_len+8))[i_ck] = i_offset; - - memcpy( &tk->p_frame->p_buffer[i_offset + 8], p, i_copy ); - } - - p += i_copy; - - if( (h&0xc0) != 0x80 ) - { - break; - } - -#if 0 - if( tk->p_frame ) - { - /* append data */ - int i_ck = ((uint32_t*)tk->p_frame->p_buffer)[1]++; - - if( (h&0xc0) == 0x80 ) - { - /* last fragment */ - i_copy = i_offset; - i_offset = i_len - i_offset; - - ((uint32_t*)(tk->p_frame->p_buffer+i_len+8))[i_ck] = i_offset; - memcpy( &tk->p_frame->p_buffer[i_offset+ 8], p, i_copy ); - p += i_copy; - - if( p_sys->i_pcr < tk->p_frame->i_dts ) - { - p_sys->i_pcr = tk->p_frame->i_dts; - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - (int64_t)p_sys->i_pcr ); - } - es_out_Send( p_demux->out, tk->p_es, tk->p_frame ); - - tk->i_frame = 0; - tk->p_frame = NULL; - - continue; - } - - ((uint32_t*)(tk->p_frame->p_buffer+i_len+8))[i_ck] = i_offset; - memcpy( &tk->p_frame->p_buffer[i_offset + 8], p, i_copy ); - break; - } - - if( (h&0xc0) != 0x00 ) - { - block_t *p_frame; - - /* not fragmented */ - if( !( p_frame = block_New( p_demux, i_copy + 8 + 8 ) ) ) - { - return -1; - } - p_frame->i_dts = i_pts; - p_frame->i_pts = i_pts; - - ((uint32_t*)p_frame->p_buffer)[0] = i_copy; - ((uint32_t*)p_frame->p_buffer)[1] = 1; - ((uint32_t*)(p_frame->p_buffer+i_copy+8))[0] = 0; - memcpy( &p_frame->p_buffer[8], p, i_copy ); - - p += i_copy; - - if( p_sys->i_pcr < p_frame->i_dts ) - { - p_sys->i_pcr = p_frame->i_dts; - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - (int64_t)p_sys->i_pcr ); - } - es_out_Send( p_demux->out, tk->p_es, p_frame ); - } - else - { - /* First fragment */ - tk->i_frame = i_len; - if( !( tk->p_frame = block_New( p_demux, i_len + 8 + 1000) ) ) - { - return -1; - } - memset( &tk->p_frame->p_buffer[8], 0, i_len ); - tk->p_frame->i_dts = i_pts; - tk->p_frame->i_pts = i_pts; - - ((uint32_t*)tk->p_frame->p_buffer)[0] = i_len; /* len */ - ((uint32_t*)tk->p_frame->p_buffer)[1] = 1; /* chunk counts */ - ((uint32_t*)(tk->p_frame->p_buffer+i_len+8))[0] = i_offset; - memcpy( &tk->p_frame->p_buffer[i_offset + 8], p, i_copy ); - - break; - } -#endif - } + DemuxVideo( p_demux, tk, i_pts, i_flags ); } - else if( tk->fmt.i_cat == AUDIO_ES && b_selected ) + else { - /* Set PCR */ - if( p_sys->i_pcr < i_pts ) - { - p_sys->i_pcr = i_pts; - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - (int64_t)p_sys->i_pcr ); - } - - if( tk->fmt.i_codec == VLC_FOURCC( 'm','p','4','a' ) ) - { - int i_sub = (p_sys->buffer[1] >> 4)&0x0f; - uint8_t *p_sub = &p_sys->buffer[2+2*i_sub]; - - int i; - for( i = 0; i < i_sub; i++ ) - { - int i_sub_size = GetWBE( &p_sys->buffer[2+i*2]); - block_t *p_block = block_New( p_demux, i_sub_size ); - if( p_block ) - { - memcpy( p_block->p_buffer, p_sub, i_sub_size ); - p_sub += i_sub_size; - - p_block->i_dts = - p_block->i_pts = ( i == 0 ? i_pts : 0 ); - - es_out_Send( p_demux->out, tk->p_es, p_block ); - } - } - } - else if( tk->fmt.i_codec == VLC_FOURCC( 'c', 'o', 'o', 'k' ) || - tk->fmt.i_codec == VLC_FOURCC( 'a', 't', 'r', 'c') || - tk->fmt.i_codec == VLC_FOURCC( 's', 'i', 'p', 'r') || - tk->fmt.i_codec == VLC_FOURCC( '2', '8', '_', '8') ) - { - uint8_t *p_buf = p_sys->buffer; - int y = tk->i_subpacket / ( tk->i_frame_size /tk->i_subpacket_size); - int i_index, i; - - /* Sanity check */ - if( i_flags & 2 ) y = tk->i_subpacket = 0; - - if( tk->fmt.i_codec == VLC_FOURCC( 'c', 'o', 'o', 'k' ) || - tk->fmt.i_codec == VLC_FOURCC( 'a', 't', 'r', 'c' )) - for( i = 0; i < tk->i_frame_size / tk->i_subpacket_size; i++ ) - { - block_t *p_block = block_New( p_demux, tk->i_subpacket_size ); - memcpy( p_block->p_buffer, p_buf, tk->i_subpacket_size ); - p_buf += tk->i_subpacket_size; - - i_index = tk->i_subpacket_h * i + - ((tk->i_subpacket_h + 1) / 2) * (y&1) + (y>>1); - - p_block->i_dts = p_block->i_pts = i_pts; - tk->p_subpackets[i_index] = p_block; - tk->i_subpacket++; - } - - if( tk->fmt.i_codec == VLC_FOURCC( '2', '8', '_', '8' ) || - tk->fmt.i_codec == VLC_FOURCC( 's', 'i', 'p', 'r' ) ) - for( i = 0; i < tk->i_subpacket_h / 2; i++ ) - { - block_t *p_block = block_New( p_demux, tk->i_coded_frame_size); - memcpy( p_block->p_buffer, p_buf, tk->i_coded_frame_size ); - p_buf += tk->i_coded_frame_size; - - i_index = (i * 2 * tk->i_frame_size) / - tk->i_coded_frame_size + y; - - p_block->i_dts = p_block->i_pts = i_pts; - tk->p_subpackets[i_index] = p_block; - tk->i_subpacket++; - } - - while( tk->i_out_subpacket != tk->i_subpackets && - tk->p_subpackets[tk->i_out_subpacket] ) - { - block_t *p_block = tk->p_subpackets[tk->i_out_subpacket]; - tk->p_subpackets[tk->i_out_subpacket] = 0; - - if( tk->i_out_subpacket ) p_block->i_dts = p_block->i_pts = 0; - es_out_Send( p_demux->out, tk->p_es, p_block ); - - tk->i_out_subpacket++; - } - - if( tk->i_subpacket == tk->i_subpackets && - tk->i_out_subpacket != tk->i_subpackets ) - { - msg_Warn( p_demux, "i_subpacket != i_out_subpacket, " - "this shouldn't happen" ); - } - - if( tk->i_subpacket == tk->i_subpackets ) - { - tk->i_subpacket = 0; - tk->i_out_subpacket = 0; - } - } - else - { - block_t *p_block = block_New( p_demux, i_size ); - - if( tk->fmt.i_codec == VLC_FOURCC( 'a', '5', '2', ' ' ) ) - { - uint8_t *src = p_sys->buffer; - uint8_t *dst = p_block->p_buffer; - - /* byte swap data */ - while( dst < &p_block->p_buffer[i_size- 1]) - { - *dst++ = src[1]; - *dst++ = src[0]; - - src += 2; - } - } - else - { - memcpy( p_block->p_buffer, p_sys->buffer, i_size ); - } - p_block->i_dts = p_block->i_pts = i_pts; - es_out_Send( p_demux->out, tk->p_es, p_block ); - } + assert( tk->fmt.i_cat == AUDIO_ES ); + DemuxAudio( p_demux, tk, i_pts, i_flags ); } + /* Update PCR */ + mtime_t i_pcr = VLC_TS_INVALID; + for( int i = 0; i < p_sys->i_track; i++ ) + { + real_track_t *tk = p_sys->track[i]; + if( i_pcr <= VLC_TS_INVALID || ( tk->i_last_dts > VLC_TS_INVALID && tk->i_last_dts < i_pcr ) ) + i_pcr = tk->i_last_dts; + } + if( i_pcr > VLC_TS_INVALID && i_pcr != p_sys->i_pcr ) + { + p_sys->i_pcr = i_pcr; + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr ); + } return 1; } @@ -634,66 +367,92 @@ static int Demux( demux_t *p_demux ) static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; -#if 0 double f, *pf; int64_t i64; -#endif int64_t *pi64; switch( i_query ) { -#if 0 case DEMUX_GET_POSITION: pf = (double*) va_arg( args, double* ); - i64 = stream_Size( p_demux->s ); - if( i64 > 0 ) + + /* read stream size maybe failed in rtsp streaming, + so use duration to determin the position at first */ + if( p_sys->i_our_duration > 0 ) { - *pf = (double)stream_Tell( p_demux->s ) / (double)i64; + if( p_sys->i_pcr > VLC_TS_INVALID ) + *pf = (double)p_sys->i_pcr / 1000.0 / p_sys->i_our_duration; + else + *pf = 0.0; + return VLC_SUCCESS; } + + i64 = stream_Size( p_demux->s ); + if( i64 > 0 ) + *pf = (double)1.0*stream_Tell( p_demux->s ) / (double)i64; else - { *pf = 0.0; - } return VLC_SUCCESS; - case DEMUX_SET_POSITION: - f = (double) va_arg( args, double ); - i64 = stream_Size( p_demux->s ); - - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); - - return stream_Seek( p_demux->s, (int64_t)(i64 * f) ); - case DEMUX_GET_TIME: pi64 = (int64_t*)va_arg( args, int64_t * ); - if( p_sys->i_mux_rate > 0 ) + + if( p_sys->i_our_duration > 0 ) + { + *pi64 = p_sys->i_pcr > VLC_TS_INVALID ? p_sys->i_pcr : 0; + return VLC_SUCCESS; + } + + /* same as GET_POSTION */ + i64 = stream_Size( p_demux->s ); + if( p_sys->i_our_duration > 0 && i64 > 0 ) { - *pi64 = (int64_t)1000000 * ( stream_Tell( p_demux->s ) / 50 ) / p_sys->i_mux_rate; + *pi64 = (int64_t)( 1000.0 * p_sys->i_our_duration * stream_Tell( p_demux->s ) / i64 ); return VLC_SUCCESS; } + *pi64 = 0; return VLC_EGENERIC; -#endif + + case DEMUX_SET_POSITION: + f = (double) va_arg( args, double ); + i64 = (int64_t) ( stream_Size( p_demux->s ) * f ); + + if( !p_sys->p_index && i64 != 0 ) + { + /* TODO seek */ + msg_Err( p_demux,"Seek No Index Real File failed!" ); + return VLC_EGENERIC; // no index! + } + else if( i64 == 0 ) + { + /* it is a rtsp stream , it is specials in access/rtsp/... */ + msg_Dbg(p_demux, "Seek in real rtsp stream!"); + p_sys->i_pcr = VLC_TS_0 + INT64_C(1000) * ( p_sys->i_our_duration * f ); + p_sys->b_seek = true; + return stream_Seek( p_demux->s, p_sys->i_pcr - VLC_TS_0 ); + } + return ControlSeekByte( p_demux, i64 ); + + case DEMUX_SET_TIME: + if( !p_sys->p_index ) + return VLC_EGENERIC; + + i64 = (int64_t) va_arg( args, int64_t ); + return ControlSeekTime( p_demux, i64 ); case DEMUX_GET_LENGTH: pi64 = (int64_t*)va_arg( args, int64_t * ); - /* the commented following lines are fen's implementation, which doesn't seem to - * work for one reason or another -- FK */ - /*if( p_sys->i_mux_rate > 0 ) + if( p_sys->i_our_duration <= 0 ) { - *pi64 = (int64_t)1000000 * ( stream_Size( p_demux->s ) / 50 ) / p_sys->i_mux_rate; - return VLC_SUCCESS; - }*/ - if( p_sys->i_our_duration > 0 ) - { - /* our stored duration is in ms, so... */ - *pi64 = (int64_t)1000 * p_sys->i_our_duration; - - return VLC_SUCCESS; + *pi64 = 0; + return VLC_EGENERIC; } - *pi64 = 0; - return VLC_EGENERIC; + + /* our stored duration is in ms, so... */ + *pi64 = INT64_C(1000) * p_sys->i_our_duration; + return VLC_SUCCESS; case DEMUX_GET_META: { @@ -712,7 +471,6 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) return VLC_SUCCESS; } - case DEMUX_SET_TIME: case DEMUX_GET_FPS: default: return VLC_EGENERIC; @@ -721,529 +479,1257 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) } /***************************************************************************** - * HeaderRead: + * Helpers: demux *****************************************************************************/ -static int HeaderRead( demux_t *p_demux ) +static void CheckPcr( demux_t *p_demux, real_track_t *tk, mtime_t i_dts ) { demux_sys_t *p_sys = p_demux->p_sys; - uint8_t header[100]; /* FIXME */ - uint32_t i_id; - uint32_t i_size; - int64_t i_skip; - int i_version; + if( i_dts > VLC_TS_INVALID ) + tk->i_last_dts = i_dts; - for( ;; ) + if( p_sys->i_pcr > VLC_TS_INVALID || i_dts <= VLC_TS_INVALID ) + return; + + p_sys->i_pcr = i_dts; + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr ); +} + +static void DemuxVideo( demux_t *p_demux, real_track_t *tk, mtime_t i_dts, unsigned i_flags ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + const uint8_t *p_data = p_sys->buffer; + int i_data = p_sys->i_buffer; + + while( i_data > 1 ) { - /* Read the header */ - if( stream_Read( p_demux->s, header, 10 ) < 10 ) - { - return VLC_EGENERIC; - } - i_id = VLC_FOURCC( header[0], header[1], header[2], header[3] ); - i_size = GetDWBE( &header[4] ); - i_version = GetWBE( &header[8] ); + uint8_t i_hdr = R8( &p_data, &i_data ); + uint8_t i_type = i_hdr >> 6; - msg_Dbg( p_demux, "object %4.4s size=%d version=%d", - (char*)&i_id, i_size, i_version ); + uint8_t i_seq; + int i_len; + int i_pos; + int i_frame_num; - if( i_size < 10 && i_id != VLC_FOURCC('D','A','T','A') ) + if( i_type == 1 ) { - msg_Dbg( p_demux, "invalid size for object %4.4s", (char*)&i_id ); - return VLC_EGENERIC; + R8( &p_data, &i_data ); + i_len = i_data; + i_pos = 0; + i_frame_num = -1; + i_seq = 1; + i_hdr &= ~0x3f; } - i_skip = i_size - 10; - - if( i_id == VLC_FOURCC('.','R','M','F') ) + else if( i_type == 3 ) { - if( stream_Read( p_demux->s, header, 8 ) < 8 ) return VLC_EGENERIC; - msg_Dbg( p_demux, " - file version=0x%x num headers=%d", - GetDWBE( &header[0] ), GetDWBE( &header[4] ) ); - - i_skip -= 8; + i_len = RLength( &p_data, &i_data ); + i_pos = RLength( &p_data, &i_data ); + i_frame_num = R8( &p_data, &i_data ); + i_seq = 1; + i_hdr &= ~0x3f; } - else if( i_id == VLC_FOURCC('P','R','O','P') ) + else { - int i_flags; - - if( stream_Read(p_demux->s, header, 40) < 40 ) return VLC_EGENERIC; - - msg_Dbg( p_demux, " - max bitrate=%d avg bitrate=%d", - GetDWBE(&header[0]), GetDWBE(&header[4]) ); - msg_Dbg( p_demux, " - max packet size=%d avg bitrate=%d", - GetDWBE(&header[8]), GetDWBE(&header[12]) ); - msg_Dbg( p_demux, " - packets count=%d", GetDWBE(&header[16]) ); - msg_Dbg( p_demux, " - duration=%d ms", GetDWBE(&header[20]) ); - msg_Dbg( p_demux, " - preroll=%d ms", GetDWBE(&header[24]) ); - msg_Dbg( p_demux, " - index offset=%d", GetDWBE(&header[28]) ); - msg_Dbg( p_demux, " - data offset=%d", GetDWBE(&header[32]) ); - msg_Dbg( p_demux, " - num streams=%d", GetWBE(&header[36]) ); - - /* set the duration for export in control */ - p_sys->i_our_duration = (int)GetDWBE(&header[20]); - - i_flags = GetWBE(&header[38]); - msg_Dbg( p_demux, " - flags=0x%x %s%s%s", - i_flags, - i_flags&0x0001 ? "PN_SAVE_ENABLED " : "", - i_flags&0x0002 ? "PN_PERFECT_PLAY_ENABLED " : "", - i_flags&0x0004 ? "PN_LIVE_BROADCAST" : "" ); - i_skip -= 40; + assert( i_type == 0 || i_type == 2 ); + i_seq = R8( &p_data, &i_data ); + i_len = RLength( &p_data, &i_data ); + + i_pos = RLength( &p_data, &i_data ); + i_frame_num = R8( &p_data, &i_data ); } - else if( i_id == VLC_FOURCC('C','O','N','T') ) + + if( (i_seq & 0x7f) == 1 || tk->i_frame_num != i_frame_num ) { - int i_len; - char *psz; - - /* FIXME FIXME: should convert from whatever the character - * encoding of the input meta data is to UTF-8. */ + tk->i_frame_slice = 0; + tk->i_frame_slice_count = 2 * (i_hdr & 0x3f) + 1; + tk->i_frame_pos = 2*4 * tk->i_frame_slice_count + 1; + tk->i_frame_size = i_len + 2*4 * tk->i_frame_slice_count + 1; + tk->i_frame_num = i_frame_num; - stream_Read( p_demux->s, header, 2 ); - if( ( i_len = GetWBE( header ) ) > 0 ) - { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - title=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_title, psz ); - free( psz ); - i_skip -= i_len; - } - i_skip -= 2; + if( tk->p_frame ) + block_Release( tk->p_frame ); - stream_Read( p_demux->s, header, 2 ); - if( ( i_len = GetWBE( header ) ) > 0 ) + tk->p_frame = block_New( p_demux, tk->i_frame_size ); + if( !tk->p_frame ) { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - author=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_artist, psz ); - free( psz ); - i_skip -= i_len; + tk->i_frame_size = 0; + return; } - i_skip -= 2; - stream_Read( p_demux->s, header, 2 ); - if( ( i_len = GetWBE( header ) ) > 0 ) - { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - copyright=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_copyright, psz ); - free( psz ); - i_skip -= i_len; - } - i_skip -= 2; + tk->p_frame->i_dts = i_dts; + tk->p_frame->i_pts = VLC_TS_INVALID; + if( i_flags & 0x02 ) + tk->p_frame->i_flags |= BLOCK_FLAG_TYPE_I; - stream_Read( p_demux->s, header, 2 ); - if( ( i_len = GetWBE( header ) ) > 0 ) - { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - comment=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_description, psz ); - free( psz ); - i_skip -= i_len; - } - i_skip -= 2; + i_dts = VLC_TS_INVALID; } - else if( i_id == VLC_FOURCC('M','D','P','R') ) - { - /* Media properties header */ - int i_num; - int i_len; - char *psz; - - if( stream_Read(p_demux->s, header, 30) < 30 ) return VLC_EGENERIC; - i_num = GetWBE( header ); - msg_Dbg( p_demux, " - id=0x%x", i_num ); - msg_Dbg( p_demux, " - max bitrate=%d avg bitrate=%d", - GetDWBE(&header[2]), GetDWBE(&header[6]) ); - msg_Dbg( p_demux, " - max packet size=%d avg packet size=%d", - GetDWBE(&header[10]), GetDWBE(&header[14]) ); - msg_Dbg( p_demux, " - start time=%d", GetDWBE(&header[18]) ); - msg_Dbg( p_demux, " - preroll=%d", GetDWBE(&header[22]) ); - msg_Dbg( p_demux, " - duration=%d", GetDWBE(&header[26]) ); - - i_skip -= 30; - stream_Read( p_demux->s, header, 1 ); - if( ( i_len = header[0] ) > 0 ) - { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; + int i_frame_data; + if( i_type == 3 ) + { + i_frame_data = i_len; + } + else + { + i_frame_data = i_data; + if( i_type == 2 && i_frame_data > i_pos ) + i_frame_data = i_pos; + } + if( i_frame_data > i_data ) + break; - msg_Dbg( p_demux, " - name=`%s'", psz ); - free( psz ); - i_skip -= i_len; - } - i_skip--; + /* */ + tk->i_frame_slice++; + if( tk->i_frame_slice > tk->i_frame_slice_count || !tk->p_frame ) + break; - stream_Read( p_demux->s, header, 1 ); - if( ( i_len = header[0] ) > 0 ) - { - psz = malloc( i_len + 1 ); - stream_Read( p_demux->s, psz, i_len ); - psz[i_len] = '\0'; + /* */ + SetDWLE( &tk->p_frame->p_buffer[2*4*(tk->i_frame_slice-1) + 1 + 0], 1 ); + SetDWLE( &tk->p_frame->p_buffer[2*4*(tk->i_frame_slice-1) + 1 + 4], tk->i_frame_pos - (2*4 * tk->i_frame_slice_count + 1) ); - msg_Dbg( p_demux, " - mime=`%s'", psz ); - free( psz ); - i_skip -= i_len; - } - i_skip--; + if( tk->i_frame_pos + i_frame_data > tk->i_frame_size ) + break; - stream_Read( p_demux->s, header, 4 ); - if( ( i_len = GetDWBE( header ) ) > 0 ) - { - ReadCodecSpecificData( p_demux, i_len, i_num ); - stream_Read( p_demux->s, NULL, i_len ); + memcpy( &tk->p_frame->p_buffer[tk->i_frame_pos], p_data, i_frame_data ); + RVoid( &p_data, &i_data, i_frame_data ); + tk->i_frame_pos += i_frame_data; - i_skip -= i_len; - } - i_skip -= 4; - } - else if( i_id == VLC_FOURCC('D','A','T','A') ) + if( i_type != 0 || tk->i_frame_pos >= tk->i_frame_size ) { - stream_Read( p_demux->s, header, 8 ); + /* Fix the buffer once the real number of slice is known */ + tk->p_frame->p_buffer[0] = tk->i_frame_slice - 1; + tk->p_frame->i_buffer = tk->i_frame_pos - 2*4*( tk->i_frame_slice_count - tk->i_frame_slice ); - p_sys->i_data_offset = stream_Tell( p_demux->s ) - 10; - p_sys->i_data_size = i_size; - p_sys->i_data_packets_count = GetDWBE( header ); - p_sys->i_data_packets = 0; - p_sys->i_data_offset_next = GetDWBE( &header[4] ); + memmove( &tk->p_frame->p_buffer[1+2*4*tk->i_frame_slice ], + &tk->p_frame->p_buffer[1+2*4*tk->i_frame_slice_count], + tk->i_frame_pos - (2*4*tk->i_frame_slice_count + 1) ); - msg_Dbg( p_demux, " - packets count=%d next=%u", - p_sys->i_data_packets_count, - (uint32_t)p_sys->i_data_offset_next ); + /* Send it */ + CheckPcr( p_demux, tk, tk->p_frame->i_dts ); + es_out_Send( p_demux->out, tk->p_es, tk->p_frame ); - /* we have finished the header */ - break; + tk->i_frame_size = 0; + tk->p_frame = NULL; } - else + } +} + +static void DemuxAudioMethod1( demux_t *p_demux, real_track_t *tk, mtime_t i_pts, unsigned int i_flags ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + uint8_t *p_buf = p_sys->buffer; + + /* Sanity check */ + if( (i_flags & 2) || p_sys->b_seek ) + { + tk->i_subpacket = 0; + tk->i_out_subpacket = 0; + p_sys->b_seek = false; + } + + if( tk->fmt.i_codec == VLC_CODEC_COOK || + tk->fmt.i_codec == VLC_CODEC_ATRAC3 ) + { + const int i_num = tk->i_frame_size / tk->i_subpacket_size; + const int y = tk->i_subpacket / ( tk->i_frame_size / tk->i_subpacket_size ); + + for( int i = 0; i < i_num; i++ ) { - /* unknow header */ - msg_Dbg( p_demux, "unknown chunk" ); + block_t *p_block = block_New( p_demux, tk->i_subpacket_size ); + if( !p_block ) + return; + if( &p_buf[tk->i_subpacket_size] > &p_sys->buffer[p_sys->i_buffer] ) + return; + + memcpy( p_block->p_buffer, p_buf, tk->i_subpacket_size ); + p_block->i_dts = + p_block->i_pts = VLC_TS_INVALID; + + p_buf += tk->i_subpacket_size; + + int i_index = tk->i_subpacket_h * i + + ((tk->i_subpacket_h + 1) / 2) * (y&1) + (y>>1); + + if( tk->p_subpackets[i_index] != NULL ) + { + msg_Dbg(p_demux, "p_subpackets[ %d ] not null!", i_index ); + block_Release( tk->p_subpackets[i_index] ); + } + + tk->p_subpackets[i_index] = p_block; + if( tk->i_subpacket == 0 ) + tk->p_subpackets_timecode[0] = i_pts; + tk->i_subpacket++; } + } + else + { + const int y = tk->i_subpacket / (tk->i_subpacket_h / 2); + assert( tk->fmt.i_codec == VLC_CODEC_RA_288 ); + + for( int i = 0; i < tk->i_subpacket_h / 2; i++ ) + { + block_t *p_block = block_New( p_demux, tk->i_coded_frame_size); + if( !p_block ) + return; + if( &p_buf[tk->i_coded_frame_size] > &p_sys->buffer[p_sys->i_buffer] ) + return; - if( i_skip < 0 ) return VLC_EGENERIC; - stream_Read( p_demux->s, NULL, i_skip ); + int i_index = (i * 2 * tk->i_frame_size / tk->i_coded_frame_size) + y; + + memcpy( p_block->p_buffer, p_buf, tk->i_coded_frame_size ); + p_block->i_dts = + p_block->i_pts = i_index == 0 ? i_pts : VLC_TS_INVALID; + + p_buf += tk->i_coded_frame_size; + + if( tk->p_subpackets[i_index] != NULL ) + { + msg_Dbg(p_demux, "p_subpackets[ %d ] not null!", i_index ); + block_Release( tk->p_subpackets[i_index] ); + } + + tk->p_subpackets[i_index] = p_block; + tk->i_subpacket++; + } } - /* TODO read index if possible */ + while( tk->i_out_subpacket != tk->i_subpackets && + tk->p_subpackets[tk->i_out_subpacket] ) + { + block_t *p_block = tk->p_subpackets[tk->i_out_subpacket]; + tk->p_subpackets[tk->i_out_subpacket] = NULL; - return VLC_SUCCESS; + if( tk->p_subpackets_timecode[tk->i_out_subpacket] ) + { + p_block->i_dts = + p_block->i_pts = tk->p_subpackets_timecode[tk->i_out_subpacket]; + + tk->p_subpackets_timecode[tk->i_out_subpacket] = 0; + } + tk->i_out_subpacket++; + + CheckPcr( p_demux, tk, p_block->i_pts ); + es_out_Send( p_demux->out, tk->p_es, p_block ); + } + + if( tk->i_subpacket == tk->i_subpackets && + tk->i_out_subpacket != tk->i_subpackets ) + { + msg_Warn( p_demux, "i_subpacket != i_out_subpacket, " + "this shouldn't happen" ); + } + + if( tk->i_subpacket == tk->i_subpackets ) + { + tk->i_subpacket = 0; + tk->i_out_subpacket = 0; + } } -static int ReadCodecSpecificData( demux_t *p_demux, int i_len, int i_num ) +static void DemuxAudioMethod2( demux_t *p_demux, real_track_t *tk, mtime_t i_pts ) { demux_sys_t *p_sys = p_demux->p_sys; - es_format_t fmt; - real_track_t *tk; - const uint8_t *p_peek; - msg_Dbg( p_demux, " - specific data len=%d", i_len ); - if( stream_Peek(p_demux->s, &p_peek, i_len) < i_len ) return VLC_EGENERIC; + if( p_sys->i_buffer < 2 ) + return; - if( ( i_len >= 8 ) && !memcmp( &p_peek[4], "VIDO", 4 ) ) + unsigned i_sub = (p_sys->buffer[1] >> 4)&0x0f; + if( p_sys->i_buffer < 2+2*i_sub ) + return; + + uint8_t *p_sub = &p_sys->buffer[2+2*i_sub]; + + for( unsigned i = 0; i < i_sub; i++ ) { - es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[8], p_peek[9], - p_peek[10], p_peek[11] ) ); - fmt.video.i_width = GetWBE( &p_peek[12] ); - fmt.video.i_height= GetWBE( &p_peek[14] ); + const int i_sub_size = GetWBE( &p_sys->buffer[2+i*2] ); + block_t *p_block = block_New( p_demux, i_sub_size ); + if( !p_block ) + break; + + if( &p_sub[i_sub_size] > &p_sys->buffer[p_sys->i_buffer] ) + break; + + memcpy( p_block->p_buffer, p_sub, i_sub_size ); + p_sub += i_sub_size; + + p_block->i_dts = + p_block->i_pts = i == 0 ? i_pts : VLC_TS_INVALID; + + CheckPcr( p_demux, tk, p_block->i_pts ); + es_out_Send( p_demux->out, tk->p_es, p_block ); + } +} +static void DemuxAudioMethod3( demux_t *p_demux, real_track_t *tk, mtime_t i_pts ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + if( p_sys->i_buffer <= 0 ) + return; - fmt.i_extra = 8; - fmt.p_extra = malloc( 8 ); - ((uint32_t*)fmt.p_extra)[0] = GetDWBE( &p_peek[26] ); - ((uint32_t*)fmt.p_extra)[1] = GetDWBE( &p_peek[30] ); + block_t *p_block = block_New( p_demux, p_sys->i_buffer ); + if( !p_block ) + return; - msg_Dbg( p_demux, " - video 0x%08x 0x%08x", - ((uint32_t*)fmt.p_extra)[0], ((uint32_t*)fmt.p_extra)[1] ); + if( tk->fmt.i_codec == VLC_CODEC_A52 ) + { + uint8_t *p_src = p_sys->buffer; + uint8_t *p_dst = p_block->p_buffer; - if( GetDWBE( &p_peek[30] ) == 0x10003000 || - GetDWBE( &p_peek[30] ) == 0x10003001 ) + /* byte swap data */ + while( p_dst < &p_block->p_buffer[p_sys->i_buffer - 1]) { - fmt.i_codec = VLC_FOURCC( 'R','V','1','3' ); + *p_dst++ = p_src[1]; + *p_dst++ = p_src[0]; + + p_src += 2; } - else if( GetDWBE( &p_peek[30] ) == 0x20001000 || - GetDWBE( &p_peek[30] ) == 0x20100001 || - GetDWBE( &p_peek[30] ) == 0x20200002 ) - { - fmt.i_codec = VLC_FOURCC( 'R','V','2','0' ); + } + else + { + memcpy( p_block->p_buffer, p_sys->buffer, p_sys->i_buffer ); + } + p_block->i_dts = + p_block->i_pts = i_pts; + + CheckPcr( p_demux, tk, p_block->i_pts ); + es_out_Send( p_demux->out, tk->p_es, p_block ); +} + +// Sipr packet re-ordering code and index table borrowed from +// the MPlayer Realmedia demuxer. +static const uint8_t sipr_swap_index_table[38][2] = { + { 0, 63 }, { 1, 22 }, { 2, 44 }, { 3, 90 }, + { 5, 81 }, { 7, 31 }, { 8, 86 }, { 9, 58 }, + { 10, 36 }, { 12, 68 }, { 13, 39 }, { 14, 73 }, + { 15, 53 }, { 16, 69 }, { 17, 57 }, { 19, 88 }, + { 20, 34 }, { 21, 71 }, { 24, 46 }, { 25, 94 }, + { 26, 54 }, { 28, 75 }, { 29, 50 }, { 32, 70 }, + { 33, 92 }, { 35, 74 }, { 38, 85 }, { 40, 56 }, + { 42, 87 }, { 43, 65 }, { 45, 59 }, { 48, 79 }, + { 49, 93 }, { 51, 89 }, { 55, 95 }, { 61, 76 }, + { 67, 83 }, { 77, 80 } +}; + +static void SiprPacketReorder(uint8_t *buf, int sub_packet_h, int framesize) +{ + int n, bs = sub_packet_h * framesize * 2 / 96; // nibbles per subpacket + + for (n = 0; n < 38; n++) { + int j; + int i = bs * sipr_swap_index_table[n][0]; + int o = bs * sipr_swap_index_table[n][1]; + + /* swap 4 bit-nibbles of block 'i' with 'o' */ + for (j = 0; j < bs; j++, i++, o++) { + int x = (buf[i >> 1] >> (4 * (i & 1))) & 0xF, + y = (buf[o >> 1] >> (4 * (o & 1))) & 0xF; + + buf[o >> 1] = (x << (4 * (o & 1))) | + (buf[o >> 1] & (0xF << (4 * !(o & 1)))); + buf[i >> 1] = (y << (4 * (i & 1))) | + (buf[i >> 1] & (0xF << (4 * !(i & 1)))); } - else if( GetDWBE( &p_peek[30] ) == 0x30202002 ) + } +} + +static void DemuxAudioSipr( demux_t *p_demux, real_track_t *tk, mtime_t i_pts ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + block_t *p_block = tk->p_sipr_packet; + + if( p_sys->i_buffer < tk->i_frame_size ) + return; + + if( !p_block ) + { + p_block = block_New( p_demux, tk->i_frame_size * tk->i_subpacket_h ); + if( !p_block ) + return; + tk->p_sipr_packet = p_block; + } + + memcpy( p_block->p_buffer + tk->i_sipr_subpacket_count * tk->i_frame_size, + p_sys->buffer, tk->i_frame_size ); + if (!tk->i_sipr_subpacket_count) + { + p_block->i_dts = + p_block->i_pts = i_pts; + } + + if( ++tk->i_sipr_subpacket_count < tk->i_subpacket_h ) + return; + + SiprPacketReorder(p_block->p_buffer, tk->i_subpacket_h, tk->i_frame_size); + CheckPcr( p_demux, tk, p_block->i_pts ); + es_out_Send( p_demux->out, tk->p_es, p_block ); + tk->i_sipr_subpacket_count = 0; + tk->p_sipr_packet = NULL; +} + +static void DemuxAudio( demux_t *p_demux, real_track_t *tk, mtime_t i_pts, unsigned i_flags ) +{ + switch( tk->fmt.i_codec ) + { + case VLC_CODEC_COOK: + case VLC_CODEC_ATRAC3: + case VLC_CODEC_RA_288: + DemuxAudioMethod1( p_demux, tk, i_pts, i_flags ); + break; + case VLC_CODEC_MP4A: + DemuxAudioMethod2( p_demux, tk, i_pts ); + break; + case VLC_CODEC_SIPR: + DemuxAudioSipr( p_demux, tk, i_pts ); + break; + default: + DemuxAudioMethod3( p_demux, tk, i_pts ); + break; + } +} + +/***************************************************************************** + * Helpers: seek/control + *****************************************************************************/ +static int ControlGoToIndex( demux_t *p_demux, real_index_t *p_index ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + p_sys->b_seek = true; + p_sys->i_pcr = INT64_C(1000) * p_index->i_time_offset; + for( int i = 0; i < p_sys->i_track; i++ ) + p_sys->track[i]->i_last_dts = 0; + return stream_Seek( p_demux->s, p_index->i_file_offset ); +} +static int ControlSeekTime( demux_t *p_demux, mtime_t i_time ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + real_index_t *p_index = p_sys->p_index; + + while( p_index->i_file_offset != 0 ) + { + if( p_index->i_time_offset * INT64_C(1000) > i_time ) { - fmt.i_codec = VLC_FOURCC( 'R','V','3','0' ); + if( p_index != p_sys->p_index ) + p_index--; + break; } - else if( GetDWBE( &p_peek[30] ) == 0x40000000 ) + p_index++; + } + if( p_index->i_file_offset == 0 ) + return VLC_EGENERIC; + return ControlGoToIndex( p_demux, p_index ); +} +static int ControlSeekByte( demux_t *p_demux, int64_t i_bytes ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + real_index_t *p_index = p_sys->p_index; + + while( p_index->i_file_offset != 0 ) + { + if( p_index->i_file_offset > i_bytes ) { - fmt.i_codec = VLC_FOURCC( 'R','V','4','0' ); + if( p_index != p_sys->p_index ) + p_index--; + break; } + p_index++; + } + if( p_index->i_file_offset == 0 ) + return VLC_EGENERIC; + return ControlGoToIndex( p_demux, p_index ); +} - msg_Dbg( p_demux, " - video %4.4s %dx%d", - (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height ); +/***************************************************************************** + * Helpers: header reading + *****************************************************************************/ - tk = malloc( sizeof( real_track_t ) ); - tk->i_out_subpacket = 0; - tk->i_subpacket = 0; - tk->i_subpackets = 0; - tk->p_subpackets = NULL; - tk->i_id = i_num; - tk->fmt = fmt; - tk->i_frame = 0; - tk->p_frame = NULL; - tk->p_es = es_out_Add( p_demux->out, &fmt ); - - TAB_APPEND( p_sys->i_track, p_sys->track, tk ); +/** + * This function will read a pascal string with size stored in 2 bytes from + * a stream_t. + * + * FIXME what is the right charset ? + */ +static char *StreamReadString2( stream_t *s ) +{ + uint8_t p_tmp[2]; + + if( stream_Read( s, p_tmp, 2 ) < 2 ) + return NULL; + + const int i_length = GetWBE( p_tmp ); + if( i_length <= 0 ) + return NULL; + + char *psz_string = calloc( 1, i_length + 1 ); + + stream_Read( s, psz_string, i_length ); /* Valid even if !psz_string */ + + if( psz_string ) + EnsureUTF8( psz_string ); + return psz_string; +} + +/** + * This function will read a pascal string with size stored in 1 byte from a + * memory buffer. + * + * FIXME what is the right charset ? + */ +static char *MemoryReadString1( const uint8_t **pp_data, int *pi_data ) +{ + const uint8_t *p_data = *pp_data; + int i_data = *pi_data; + + char *psz_string = NULL; + + if( i_data < 1 ) + goto exit; + + int i_length = *p_data++; i_data--; + if( i_length > i_data ) + i_length = i_data; + + if( i_length > 0 ) + { + psz_string = strndup( (const char*)p_data, i_length ); + if( psz_string ) + EnsureUTF8( psz_string ); + + p_data += i_length; + i_data -= i_length; } - else if( !strncmp( (char *)p_peek, ".ra\xfd", 4 ) ) + +exit: + *pp_data = p_data; + *pi_data = i_data; + return psz_string; +} + +/** + * This function parses(skip) the .RMF identification chunk. + */ +static int HeaderRMF( demux_t *p_demux ) +{ + uint8_t p_buffer[8]; + + if( stream_Read( p_demux->s, p_buffer, 8 ) < 8 ) + return VLC_EGENERIC; + + msg_Dbg( p_demux, " - file version=0x%x num headers=%d", + GetDWBE( &p_buffer[0] ), GetDWBE( &p_buffer[4] ) ); + return VLC_SUCCESS; +} +/** + * This function parses the PROP properties chunk. + */ +static int HeaderPROP( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + uint8_t p_buffer[40]; + int i_flags; + + if( stream_Read( p_demux->s, p_buffer, 40 ) < 40 ) + return VLC_EGENERIC; + + msg_Dbg( p_demux, " - max bitrate=%d avg bitrate=%d", + GetDWBE(&p_buffer[0]), GetDWBE(&p_buffer[4]) ); + msg_Dbg( p_demux, " - max packet size=%d avg bitrate=%d", + GetDWBE(&p_buffer[8]), GetDWBE(&p_buffer[12]) ); + msg_Dbg( p_demux, " - packets count=%d", GetDWBE(&p_buffer[16]) ); + msg_Dbg( p_demux, " - duration=%d ms", GetDWBE(&p_buffer[20]) ); + msg_Dbg( p_demux, " - preroll=%d ms", GetDWBE(&p_buffer[24]) ); + msg_Dbg( p_demux, " - index offset=%d", GetDWBE(&p_buffer[28]) ); + msg_Dbg( p_demux, " - data offset=%d", GetDWBE(&p_buffer[32]) ); + msg_Dbg( p_demux, " - num streams=%d", GetWBE(&p_buffer[36]) ); + + /* set the duration for export in control */ + p_sys->i_our_duration = GetDWBE(&p_buffer[20]); + + p_sys->i_index_offset = GetDWBE(&p_buffer[28]); + + i_flags = GetWBE(&p_buffer[38]); + msg_Dbg( p_demux, " - flags=0x%x %s%s%s", + i_flags, + i_flags&0x0001 ? "PN_SAVE_ENABLED " : "", + i_flags&0x0002 ? "PN_PERFECT_PLAY_ENABLED " : "", + i_flags&0x0004 ? "PN_LIVE_BROADCAST" : "" ); + + return VLC_SUCCESS; +} +/** + * This functions parses the CONT commentairs chunk. + */ +static int HeaderCONT( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + /* */ + p_sys->psz_title = StreamReadString2( p_demux->s ); + if( p_sys->psz_title ) + msg_Dbg( p_demux, " - title=`%s'", p_sys->psz_title ); + + /* */ + p_sys->psz_artist = StreamReadString2( p_demux->s ); + if( p_sys->psz_artist ) + msg_Dbg( p_demux, " - artist=`%s'", p_sys->psz_artist ); + + /* */ + p_sys->psz_copyright = StreamReadString2( p_demux->s ); + if( p_sys->psz_copyright ) + msg_Dbg( p_demux, " - copyright=`%s'", p_sys->psz_copyright ); + + /* */ + p_sys->psz_description = StreamReadString2( p_demux->s ); + if( p_sys->psz_description ) + msg_Dbg( p_demux, " - comment=`%s'", p_sys->psz_description ); + + return VLC_SUCCESS; +} +/** + * This function parses the MDPR (Media properties) chunk. + */ +static int HeaderMDPR( demux_t *p_demux ) +{ + uint8_t p_buffer[30]; + + if( stream_Read( p_demux->s, p_buffer, 30 ) < 30 ) + return VLC_EGENERIC; + + const int i_num = GetWBE( &p_buffer[0] ); + msg_Dbg( p_demux, " - id=0x%x", i_num ); + msg_Dbg( p_demux, " - max bitrate=%d avg bitrate=%d", + GetDWBE(&p_buffer[2]), GetDWBE(&p_buffer[6]) ); + msg_Dbg( p_demux, " - max packet size=%d avg packet size=%d", + GetDWBE(&p_buffer[10]), GetDWBE(&p_buffer[14]) ); + msg_Dbg( p_demux, " - start time=%d", GetDWBE(&p_buffer[18]) ); + msg_Dbg( p_demux, " - preroll=%d", GetDWBE(&p_buffer[22]) ); + msg_Dbg( p_demux, " - duration=%d", GetDWBE(&p_buffer[26]) ); + + /* */ + const uint8_t *p_peek; + int i_peek_org = stream_Peek( p_demux->s, &p_peek, 2 * 256 ); + int i_peek = i_peek_org; + if( i_peek <= 0 ) + return VLC_EGENERIC; + + char *psz_name = MemoryReadString1( &p_peek, &i_peek ); + if( psz_name ) + { + msg_Dbg( p_demux, " - name=`%s'", psz_name ); + free( psz_name ); + } + char *psz_mime = MemoryReadString1( &p_peek, &i_peek ); + if( psz_mime ) { - int i_header_size, i_flavor, i_coded_frame_size, i_subpacket_h; - int i_frame_size, i_subpacket_size; - int i_version = GetWBE( &p_peek[4] ); /* [0..3] = '.','r','a',0xfd */ - msg_Dbg( p_demux, " - audio version=%d", i_version ); + msg_Dbg( p_demux, " - mime=`%s'", psz_mime ); + free( psz_mime ); + } + const int i_skip = i_peek_org - i_peek; + if( i_skip > 0 && stream_Read( p_demux->s, NULL, i_skip ) < i_skip ) + return VLC_EGENERIC; - p_peek += 6; /* 4 + version */ - es_format_Init( &fmt, AUDIO_ES, 0 ); + /* */ + if( stream_Read( p_demux->s, p_buffer, 4 ) < 4 ) + return VLC_EGENERIC; - if( i_version == 3 ) - { - int i_len; - char *psz; + const uint32_t i_size = GetDWBE( p_buffer ); + if( i_size > 0 ) + { + CodecParse( p_demux, i_size, i_num ); + unsigned size = stream_Read( p_demux->s, NULL, i_size ); + if( size < i_size ) + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} +/** + * This function parses DATA chunk (it contains the actual movie data). + */ +static int HeaderDATA( demux_t *p_demux, uint32_t i_size ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + uint8_t p_buffer[8]; - i_header_size = GetWBE( p_peek ); p_peek += 2; /* Size from now */ - p_peek += 10; /* Unknown */ + if( stream_Read( p_demux->s, p_buffer, 8 ) < 8 ) + return VLC_EGENERIC; - p_peek += 4; /* Data Size */ + p_sys->i_data_offset = stream_Tell( p_demux->s ) - 10; + p_sys->i_data_size = i_size; + p_sys->i_data_packets_count = GetDWBE( p_buffer ); + p_sys->i_data_packets = 0; + p_sys->i_data_offset_next = GetDWBE( &p_buffer[4] ); - /* Title */ - i_len = *p_peek ; p_peek++; - if( i_len > 0 ) - { - psz = malloc( i_len + 1 ); - memcpy( psz, p_peek, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - title=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_title, psz ); - free( psz ); - } - p_peek += i_len; + msg_Dbg( p_demux, " - packets count=%d next=%u", + p_sys->i_data_packets_count, + (unsigned int)p_sys->i_data_offset_next ); + return VLC_SUCCESS; +} +/** + * This function parses the INDX (movie index chunk). + * It is optional but seeking without it is ... hard. + */ +static void HeaderINDX( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + uint8_t buffer[20]; - /* Authors */ - i_len = *p_peek ; p_peek++; - if( i_len > 0 ) - { - psz = malloc( i_len + 1 ); - memcpy( psz, p_peek, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - artist=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_artist, psz ); - free( psz ); - } - p_peek += i_len; + uint32_t i_index_count; - /* Copyright */ - i_len = *p_peek ; p_peek++; - if( i_len > 0 ) - { - psz = malloc( i_len + 1 ); - memcpy( psz, p_peek, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - Copyright=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_copyright, psz ); - free( psz ); - } - p_peek += i_len; + if( p_sys->i_index_offset == 0 ) + return; - /* Comment */ - i_len = *p_peek ; p_peek++; - if( i_len > 0 ) - { - psz = malloc( i_len + 1 ); - memcpy( psz, p_peek, i_len ); - psz[i_len] = '\0'; - - msg_Dbg( p_demux, " - Comment=`%s'", psz ); - EnsureUTF8( psz ); - asprintf( &p_sys->psz_description, psz ); - free( psz ); - } - /* This might be unusefull */ - p_peek += i_len; + stream_Seek( p_demux->s, p_sys->i_index_offset ); - p_peek ++; /* Unknown */ - p_peek ++; /* FourCC length = 4 */ - memcpy( (char *)&fmt.i_codec, p_peek, 4 ); p_peek += 4; - /* Up to here :) */ + if( stream_Read( p_demux->s, buffer, 20 ) < 20 ) + return ; - fmt.audio.i_channels = 1; /* This is always the case in rm3 */ - fmt.audio.i_rate = 8000; + const uint32_t i_id = VLC_FOURCC( buffer[0], buffer[1], buffer[2], buffer[3] ); + const uint32_t i_size = GetDWBE( &buffer[4] ); + int i_version = GetWBE( &buffer[8] ); - msg_Dbg( p_demux, " - audio codec=%4.4s channels=%d rate=%dHz", - (char*)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate ); - } - else + msg_Dbg( p_demux, "Real index %4.4s size=%d version=%d", + (char*)&i_id, i_size, i_version ); + + if( (i_size < 20) && (i_id != VLC_FOURCC('I','N','D','X')) ) + return; + + i_index_count = GetDWBE( &buffer[10] ); + + msg_Dbg( p_demux, "Real Index : num : %d ", i_index_count ); + + if( i_index_count >= ( 0xffffffff / sizeof(*p_sys->p_index) ) ) + return; + + if( GetDWBE( &buffer[16] ) > 0 ) + msg_Dbg( p_demux, "Real Index: Does next index exist? %d ", + GetDWBE( &buffer[16] ) ); + + /* One extra entry is allocated (that MUST be set to 0) to identify the + * end of the index. + * TODO add a clean entry count (easier to build index on the fly) */ + p_sys->p_index = calloc( i_index_count + 1, sizeof(*p_sys->p_index) ); + if( !p_sys->p_index ) + return; + + for( unsigned int i = 0; i < i_index_count; i++ ) + { + uint8_t p_entry[14]; + + if( stream_Read( p_demux->s, p_entry, 14 ) < 14 ) + return ; + + if( GetWBE( &p_entry[0] ) != 0 ) { + msg_Dbg( p_demux, "Real Index: invaild version of index entry %d ", + GetWBE( &p_entry[0] ) ); + return; + } - p_peek += 2; /* 00 00 */ - p_peek += 4; /* .ra4 or .ra5 */ - p_peek += 4; /* data size */ - p_peek += 2; /* version (4 or 5) */ - i_header_size = GetDWBE( p_peek ); p_peek += 4; /* header size */ - i_flavor = GetWBE( p_peek ); p_peek += 2; /* codec flavor */ - i_coded_frame_size = GetDWBE( p_peek ); p_peek += 4; /* coded frame size*/ - p_peek += 4; /* ?? */ - p_peek += 4; /* ?? */ - p_peek += 4; /* ?? */ - i_subpacket_h = GetWBE( p_peek ); p_peek += 2; /* 1 */ - i_frame_size = GetWBE( p_peek ); p_peek += 2; /* frame size */ - i_subpacket_size = GetWBE( p_peek ); p_peek += 2; /* subpacket_size */ - p_peek += 2; /* ?? */ - - if( i_version == 5 ) p_peek += 6; /* 0, srate, 0 */ - - fmt.audio.i_rate = GetWBE( p_peek ); p_peek += 2; /* Sample Rate */ - p_peek += 2; /* ?? */ - fmt.audio.i_bitspersample = GetWBE( p_peek ); p_peek += 2;/* Sure?*/ - fmt.audio.i_channels = GetWBE( p_peek ); p_peek += 2; /* Channels */ - fmt.audio.i_blockalign = i_frame_size; + real_index_t *p_idx = &p_sys->p_index[i]; + + p_idx->i_time_offset = GetDWBE( &p_entry[2] ); + p_idx->i_file_offset = GetDWBE( &p_entry[6] ); + p_idx->i_frame_index = GetDWBE( &p_entry[10] ); - if( i_version == 5 ) - { - p_peek += 4; /* genr */ - memcpy( (char *)&fmt.i_codec, p_peek, 4 ); p_peek += 4; - } - else - { - p_peek += p_peek[0] + 1; /* descr 1 */ - memcpy( (char *)&fmt.i_codec, p_peek + 1, 4 ); /* descr 2 */ - p_peek += p_peek[0] + 1; - } +#if 0 + msg_Dbg( p_demux, + "Real Index: time %"PRIu32" file %"PRIu32" frame %"PRIu32, + p_idx->i_time_offset, + p_idx->i_file_offset, + p_idx->i_frame_index ); +#endif + } +} + + +/** + * This function parses the complete RM headers and move the + * stream pointer to the data to be read. + */ +static int HeaderRead( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + for( ;; ) + { + const int64_t i_stream_position = stream_Tell( p_demux->s ); + uint8_t header[100]; /* FIXME */ + + /* Read the header */ + if( stream_Read( p_demux->s, header, 10 ) < 10 ) + return VLC_EGENERIC; - msg_Dbg( p_demux, " - audio codec=%4.4s channels=%d rate=%dHz", - (char*)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate ); + const uint32_t i_id = VLC_FOURCC( header[0], header[1], + header[2], header[3] ); + const uint32_t i_size = GetDWBE( &header[4] ); + const int i_version = GetWBE( &header[8] ); - p_peek += 3; /* ?? */ - if( i_version == 5 ) p_peek++; - /* Extra Data then: DWord + byte[] */ - fmt.i_extra = GetDWBE( p_peek ); p_peek += 4; + msg_Dbg( p_demux, "object %4.4s size=%d version=%d", + (char*)&i_id, i_size, i_version ); + + /* */ + if( i_size < 10 && i_id != VLC_FOURCC('D','A','T','A') ) + { + msg_Dbg( p_demux, "invalid size for object %4.4s", (char*)&i_id ); + return VLC_EGENERIC; } - switch( fmt.i_codec ) + int i_ret; + switch( i_id ) { - case VLC_FOURCC('1','4','_','4'): + case VLC_FOURCC('.','R','M','F'): + i_ret = HeaderRMF( p_demux ); break; - case VLC_FOURCC('l','p','c','J'): - fmt.i_codec = VLC_FOURCC( '1','4','_','4' ); + case VLC_FOURCC('P','R','O','P'): + i_ret = HeaderPROP( p_demux ); break; - - case VLC_FOURCC('2','8','_','8'): - fmt.i_extra = 0; - fmt.audio.i_blockalign = i_coded_frame_size; + case VLC_FOURCC('C','O','N','T'): + i_ret = HeaderCONT( p_demux ); + break; + case VLC_FOURCC('M','D','P','R'): + i_ret = HeaderMDPR( p_demux ); + break; + case VLC_FOURCC('D','A','T','A'): + i_ret = HeaderDATA( p_demux, i_size ); + break; + default: + /* unknow header */ + msg_Dbg( p_demux, "unknown chunk" ); + i_ret = VLC_SUCCESS; break; + } + if( i_ret ) + return i_ret; - case VLC_FOURCC( 'd','n','e','t' ): - fmt.i_codec = VLC_FOURCC( 'a','5','2',' ' ); + if( i_id == VLC_FOURCC('D','A','T','A') ) /* In this case, parsing is finished */ break; - case VLC_FOURCC( 'r','a','a','c' ): - case VLC_FOURCC( 'r','a','c','p' ): - if( fmt.i_extra > 0 ) { fmt.i_extra--; p_peek++; } - if( fmt.i_extra > 0 ) + /* Skip unread data */ + const int64_t i_stream_current = stream_Tell( p_demux->s ); + const int64_t i_stream_skip = (i_stream_position + i_size) - i_stream_current; + + if( i_stream_skip > 0 ) + { + if( stream_Read( p_demux->s, NULL, i_stream_skip ) != i_stream_skip ) + return VLC_EGENERIC; + } + else if( i_stream_skip < 0 ) + { + return VLC_EGENERIC; + } + } + + /* read index if possible */ + if( p_sys->i_index_offset > 0 ) + { + const int64_t i_position = stream_Tell( p_demux->s ); + + HeaderINDX( p_demux ); + + if( stream_Seek( p_demux->s, i_position ) ) + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +static void CodecMetaRead( demux_t *p_demux, const uint8_t **pp_data, int *pi_data ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + /* Title */ + p_sys->psz_title = MemoryReadString1( pp_data, pi_data ); + if( p_sys->psz_title ) + msg_Dbg( p_demux, " - title=`%s'", p_sys->psz_title ); + + /* Authors */ + p_sys->psz_artist = MemoryReadString1( pp_data, pi_data ); + if( p_sys->psz_artist ) + msg_Dbg( p_demux, " - artist=`%s'", p_sys->psz_artist ); + + /* Copyright */ + p_sys->psz_copyright = MemoryReadString1( pp_data, pi_data ); + if( p_sys->psz_copyright ) + msg_Dbg( p_demux, " - copyright=`%s'", p_sys->psz_copyright ); + + /* Comment */ + p_sys->psz_description = MemoryReadString1( pp_data, pi_data ); + if( p_sys->psz_description ) + msg_Dbg( p_demux, " - Comment=`%s'", p_sys->psz_description ); +} + +static int CodecVideoParse( demux_t *p_demux, int i_tk_id, const uint8_t *p_data, int i_data ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + + if( i_data < 34 ) + return VLC_EGENERIC; + + /* */ + es_format_t fmt; + es_format_Init( &fmt, VIDEO_ES, + VLC_FOURCC( p_data[8], p_data[9], p_data[10], p_data[11] ) ); + fmt.video.i_width = GetWBE( &p_data[12] ); + fmt.video.i_height= GetWBE( &p_data[14] ); + fmt.video.i_frame_rate = (GetWBE( &p_data[22] ) << 16) | GetWBE( &p_data[24] ); + fmt.video.i_frame_rate_base = 1 << 16; + + fmt.i_extra = 8; + fmt.p_extra = malloc( 8 ); + if( !fmt.p_extra ) + return VLC_ENOMEM; + + memcpy( fmt.p_extra, &p_data[26], 8 ); + + //msg_Dbg( p_demux, " - video 0x%08x 0x%08x", dw0, dw1 ); + + /* */ + switch( GetDWBE( &p_data[30] ) ) + { + case 0x10003000: + case 0x10003001: + fmt.i_codec = VLC_CODEC_RV13; + break; + case 0x20001000: + case 0x20100001: + case 0x20200002: + case 0x20201002: + fmt.i_codec = VLC_CODEC_RV20; + break; + case 0x30202002: + fmt.i_codec = VLC_CODEC_RV30; + break; + case 0x40000000: + fmt.i_codec = VLC_CODEC_RV40; + break; + } + msg_Dbg( p_demux, " - video %4.4s %dx%d - %8.8x", + (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height, GetDWBE( &p_data[30] ) ); + + real_track_t *tk = malloc( sizeof( *tk ) ); + if( !tk ) + { + es_format_Clean( &fmt ); + return VLC_ENOMEM; + } + tk->i_out_subpacket = 0; + tk->i_subpacket = 0; + tk->i_subpackets = 0; + tk->p_subpackets = NULL; + tk->p_subpackets_timecode = NULL; + tk->i_id = i_tk_id; + tk->fmt = fmt; + tk->i_frame_num = -1; + tk->i_frame_size = 0; + tk->p_frame = NULL; + tk->i_last_dts = 0; + tk->p_sipr_packet = NULL; + tk->p_es = es_out_Add( p_demux->out, &fmt ); + + TAB_APPEND( p_sys->i_track, p_sys->track, tk ); + return VLC_SUCCESS; +} +static int CodecAudioParse( demux_t *p_demux, int i_tk_id, const uint8_t *p_data, int i_data ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + es_format_t fmt; + + if( i_data < 6 ) + return VLC_EGENERIC; + + int i_flavor = 0; + int i_coded_frame_size = 0; + int i_subpacket_h = 0; + int i_frame_size = 0; + int i_subpacket_size = 0; + char p_genr[4]; + int i_version = GetWBE( &p_data[4] ); + int i_extra_codec = 0; + + msg_Dbg( p_demux, " - audio version=%d", i_version ); + + es_format_Init( &fmt, AUDIO_ES, 0 ); + + RVoid( &p_data, &i_data, 6 ); /* 4 + version */ + if( i_version == 3 ) /* RMF version 3 or .ra version 3 */ + { + RVoid( &p_data, &i_data, 2 + 10 + 4 ); + + /* Meta Datas */ + CodecMetaRead( p_demux, &p_data, &i_data ); + + RVoid( &p_data, &i_data, 1 + 1 ); + if( i_data >= 4 ) + memcpy( &fmt.i_codec, p_data, 4 ); + RVoid( &p_data, &i_data, 4 ); + + fmt.audio.i_channels = 1; /* This is always the case in rm3 */ + fmt.audio.i_rate = 8000; + + msg_Dbg( p_demux, " - audio codec=%4.4s channels=%d rate=%dHz", + (char*)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate ); + } + else /* RMF version 4/5 or .ra version 4 */ + { + RVoid( &p_data, &i_data, 2 + 4 + 4 + 2 + 4 ); + i_flavor = R16( &p_data, &i_data ); + i_coded_frame_size = R32( &p_data, &i_data ); + RVoid( &p_data, &i_data, 4 + 4 + 4 ); + i_subpacket_h = R16( &p_data, &i_data ); + i_frame_size = R16( &p_data, &i_data ); + i_subpacket_size = R16( &p_data, &i_data ); + if( !i_frame_size || !i_coded_frame_size ) + return VLC_EGENERIC; + + RVoid( &p_data, &i_data, 2 + (i_version == 5 ? 6 : 0 ) ); + + fmt.audio.i_rate = R16( &p_data, &i_data ); + RVoid( &p_data, &i_data, 2 ); + fmt.audio.i_bitspersample = R16( &p_data, &i_data ); + fmt.audio.i_channels = R16( &p_data, &i_data ); + fmt.audio.i_blockalign = i_frame_size; + + if( i_version == 5 ) + { + if( i_data >= 8 ) { - fmt.p_extra = malloc( fmt.i_extra ); - memcpy( fmt.p_extra, p_peek, fmt.i_extra ); + memcpy( p_genr, &p_data[0], 4 ); + memcpy( &fmt.i_codec, &p_data[4], 4 ); } + RVoid( &p_data, &i_data, 8 ); + } + else /* version 4 */ + { + if( i_data > 0 ) + RVoid( &p_data, &i_data, 1 + *p_data ); + if( i_data >= 1 + 4 ) + memcpy( &fmt.i_codec, &p_data[1], 4 ); + if( i_data > 0 ) + RVoid( &p_data, &i_data, 1 + *p_data ); + } - fmt.i_codec = VLC_FOURCC( 'm','p','4','a' ); - break; + msg_Dbg( p_demux, " - audio codec=%4.4s channels=%d rate=%dHz", + (char*)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate ); - case VLC_FOURCC('c','o','o','k'): - case VLC_FOURCC('a','t','r','c'): - fmt.audio.i_blockalign = i_subpacket_size; - if( !fmt.i_extra ) break; - fmt.p_extra = malloc( fmt.i_extra ); - memcpy( fmt.p_extra, p_peek, fmt.i_extra ); - break; + RVoid( &p_data, &i_data, 3 ); - case VLC_FOURCC('s','i','p','r'): - fmt.i_extra = 0; - fmt.audio.i_blockalign = i_coded_frame_size; - fmt.audio.i_bitspersample = sipr_fl2bps[ i_flavor ]; - break; + if( p_sys->b_real_audio ) + { + CodecMetaRead( p_demux, &p_data, &i_data ); + } + else + { + if( i_version == 5 ) + RVoid( &p_data, &i_data, 1 ); + i_extra_codec = R32( &p_data, &i_data ); + } + } - case VLC_FOURCC('r','a','l','f'): - msg_Dbg( p_demux, " - audio codec not supported=%4.4s", - (char*)&fmt.i_codec ); - break; + switch( fmt.i_codec ) + { + case VLC_FOURCC('l','p','c','J'): + case VLC_FOURCC('1','4','_','4'): + fmt.i_codec = VLC_CODEC_RA_144; + fmt.audio.i_blockalign = 0x14 ; + break; + + case VLC_FOURCC('2','8','_','8'): + fmt.i_codec = VLC_CODEC_RA_288; + fmt.audio.i_blockalign = i_coded_frame_size; + break; + + case VLC_FOURCC( 'a','5','2',' ' ): + case VLC_FOURCC( 'd','n','e','t' ): + fmt.i_codec = VLC_CODEC_A52; + break; + + case VLC_FOURCC( 'r','a','a','c' ): + case VLC_FOURCC( 'r','a','c','p' ): + fmt.i_codec = VLC_CODEC_MP4A; + + if( i_extra_codec > 0 ) + { + i_extra_codec--; + RVoid( &p_data, &i_data, 1 ); + } + if( i_extra_codec > 0 ) + { + fmt.p_extra = malloc( i_extra_codec ); + if( !fmt.p_extra || i_extra_codec > i_data ) + return VLC_ENOMEM; - default: - msg_Dbg( p_demux, " - unknown audio codec=%4.4s", - (char*)&fmt.i_codec ); - break; + fmt.i_extra = i_extra_codec; + memcpy( fmt.p_extra, p_data, fmt.i_extra ); } + break; + + case VLC_FOURCC( 's','i','p','r' ): + fmt.i_codec = VLC_CODEC_SIPR; + if( i_flavor > 3 ) + return VLC_EGENERIC; - if( fmt.i_codec != 0 ) + i_subpacket_size = i_subpacket_size_sipr[i_flavor]; + // The libavcodec sipr decoder requires stream bitrate + // to be set during initialization so that the correct mode + // can be selected. + fmt.i_bitrate = fmt.audio.i_rate; + msg_Dbg( p_demux, " - sipr flavor=%i", i_flavor ); + + case VLC_FOURCC( 'c','o','o','k' ): + case VLC_FOURCC( 'a','t','r','c' ): + if( i_subpacket_size <= 0 || i_frame_size / i_subpacket_size <= 0 ) { - int i; + es_format_Clean( &fmt ); + return VLC_EGENERIC; + } + if( !memcmp( p_genr, "genr", 4 ) ) + fmt.audio.i_blockalign = i_subpacket_size; + else + fmt.audio.i_blockalign = i_coded_frame_size; - msg_Dbg( p_demux, " - extra data=%d", fmt.i_extra ); + if( fmt.i_codec == VLC_FOURCC( 'c','o','o','k' ) ) + fmt.i_codec = VLC_CODEC_COOK; + else if( fmt.i_codec == VLC_FOURCC( 'a','t','r','c' ) ) + fmt.i_codec = VLC_CODEC_ATRAC3; - tk = malloc( sizeof( real_track_t ) ); - tk->i_id = i_num; - tk->fmt = fmt; - tk->i_frame = 0; - tk->p_frame = NULL; + if( i_extra_codec > 0 ) + { + fmt.p_extra = malloc( i_extra_codec ); + if( !fmt.p_extra || i_extra_codec > i_data ) + return VLC_ENOMEM; - tk->i_subpacket_h = i_subpacket_h; - tk->i_subpacket_size = i_subpacket_size; - tk->i_coded_frame_size = i_coded_frame_size; - tk->i_frame_size = i_frame_size; - - tk->i_out_subpacket = 0; - tk->i_subpacket = 0; - tk->i_subpackets = 0; - tk->p_subpackets = NULL; - if( fmt.i_codec == VLC_FOURCC('c','o','o','k') - || fmt.i_codec == VLC_FOURCC('a','t','r','c') ) - { - tk->i_subpackets = - i_subpacket_h * i_frame_size / tk->i_subpacket_size; - tk->p_subpackets = - malloc( tk->i_subpackets * sizeof(block_t *) ); - } - else if( fmt.i_codec == VLC_FOURCC('2','8','_','8') ) - { - tk->i_subpackets = - i_subpacket_h * i_frame_size / tk->i_coded_frame_size; - tk->p_subpackets = - malloc( tk->i_subpackets * sizeof(block_t *) ); - } + fmt.i_extra = i_extra_codec; + memcpy( fmt.p_extra, p_data, fmt.i_extra ); + } - for( i = 0; i < tk->i_subpackets; i++ ) tk->p_subpackets[i] = NULL; + break; - tk->p_es = es_out_Add( p_demux->out, &fmt ); + case VLC_FOURCC('r','a','l','f'): + msg_Dbg( p_demux, " - audio codec not supported=%4.4s", + (char*)&fmt.i_codec ); + break; - TAB_APPEND( p_sys->i_track, p_sys->track, tk ); - } + default: + msg_Dbg( p_demux, " - unknown audio codec=%4.4s", + (char*)&fmt.i_codec ); + break; } + msg_Dbg( p_demux, " - extra data=%d", fmt.i_extra ); + + /* */ + real_track_t *tk = malloc( sizeof( *tk ) ); + if( !tk ) + { + es_format_Clean( &fmt ); + return VLC_ENOMEM; + } + tk->i_id = i_tk_id; + tk->fmt = fmt; + tk->i_frame_size = 0; + tk->p_frame = NULL; + + tk->i_subpacket_h = i_subpacket_h; + tk->i_subpacket_size = i_subpacket_size; + tk->i_coded_frame_size = i_coded_frame_size; + tk->i_frame_size = i_frame_size; + + tk->i_out_subpacket = 0; + tk->i_subpacket = 0; + tk->i_subpackets = 0; + tk->p_subpackets = NULL; + tk->p_subpackets_timecode = NULL; + + tk->p_sipr_packet = NULL; + tk->i_sipr_subpacket_count = 0; + + if( fmt.i_codec == VLC_CODEC_COOK || + fmt.i_codec == VLC_CODEC_ATRAC3 ) + { + tk->i_subpackets = + i_subpacket_h * i_frame_size / tk->i_subpacket_size; + tk->p_subpackets = + calloc( tk->i_subpackets, sizeof(block_t *) ); + tk->p_subpackets_timecode = + calloc( tk->i_subpackets , sizeof( int64_t ) ); + } + else if( fmt.i_codec == VLC_CODEC_RA_288 ) + { + tk->i_subpackets = + i_subpacket_h * i_frame_size / tk->i_coded_frame_size; + tk->p_subpackets = + calloc( tk->i_subpackets, sizeof(block_t *) ); + tk->p_subpackets_timecode = + calloc( tk->i_subpackets , sizeof( int64_t ) ); + } + + /* Check if the calloc went correctly */ + if( tk->i_subpacket > 0 && ( !tk->p_subpackets || !tk->p_subpackets_timecode ) ) + { + free( tk->p_subpackets_timecode ); + free( tk->p_subpackets ); + free( tk ); + msg_Err( p_demux, "Can't alloc subpacket" ); + return VLC_EGENERIC; + } + + tk->i_last_dts = 0; + tk->p_es = es_out_Add( p_demux->out, &fmt ); + + TAB_APPEND( p_sys->i_track, p_sys->track, tk ); return VLC_SUCCESS; } + + +static int CodecParse( demux_t *p_demux, int i_len, int i_num ) +{ + const uint8_t *p_peek; + + msg_Dbg( p_demux, " - specific data len=%d", i_len ); + if( stream_Peek( p_demux->s, &p_peek, i_len ) < i_len ) + return VLC_EGENERIC; + + if( i_len >= 8 && !memcmp( &p_peek[4], "VIDO", 4 ) ) + { + return CodecVideoParse( p_demux, i_num, p_peek, i_len ); + } + else if( i_len >= 4 && !memcmp( &p_peek[0], ".ra\xfd", 4 ) ) + { + return CodecAudioParse( p_demux, i_num, p_peek, i_len ); + } + return VLC_SUCCESS; +} + +/***************************************************************************** + * Helpers: memory buffer fct. + *****************************************************************************/ +static void RVoid( const uint8_t **pp_data, int *pi_data, int i_size ) +{ + if( i_size > *pi_data ) + i_size = *pi_data; + + *pp_data += i_size; + *pi_data -= i_size; +} +#define RX(name, type, size, code ) \ +static type name( const uint8_t **pp_data, int *pi_data ) { \ + if( *pi_data < (size) ) \ + return 0; \ + type v = code; \ + RVoid( pp_data, pi_data, size ); \ + return v; \ +} +RX(R8, uint8_t, 1, **pp_data ) +RX(R16, uint16_t, 2, GetWBE( *pp_data ) ) +RX(R32, uint32_t, 4, GetDWBE( *pp_data ) ) +static int RLength( const uint8_t **pp_data, int *pi_data ) +{ + const int v0 = R16( pp_data, pi_data ) & 0x7FFF; + if( v0 >= 0x4000 ) + return v0 - 0x4000; + return (v0 << 16) | R16( pp_data, pi_data ); +} + +