+ }
+ 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];
+
+ if( stream_Read( p_demux->s, p_buffer, 8 ) < 8 )
+ return VLC_EGENERIC;
+
+ 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] );
+
+ 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];
+
+ uint32_t i_index_count;
+
+ if( p_sys->i_index_offset == 0 )
+ return;
+
+ stream_Seek( p_demux->s, p_sys->i_index_offset );
+
+ if( stream_Read( p_demux->s, buffer, 20 ) < 20 )
+ return ;
+
+ 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, "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;
+ }
+
+ 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 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;
+
+ 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] );
+
+ 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;
+ }
+
+ int i_ret;
+ switch( i_id )
+ {
+ case VLC_FOURCC('.','R','M','F'):
+ i_ret = HeaderRMF( p_demux );
+ break;
+ case VLC_FOURCC('P','R','O','P'):
+ i_ret = HeaderPROP( p_demux );
+ break;
+ 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;
+
+ if( i_id == VLC_FOURCC('D','A','T','A') ) /* In this case, parsing is finished */
+ break;
+
+ /* 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_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 )
+ {
+ 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 );
+ }
+
+ msg_Dbg( p_demux, " - audio codec=%4.4s channels=%d rate=%dHz",
+ (char*)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate );
+
+ RVoid( &p_data, &i_data, 3 );
+
+ 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 );
+ }
+ }
+
+ 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;
+
+ 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;
+
+ i_subpacket_size = i_subpacket_size_sipr[i_flavor];
+ 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 )
+ {
+ 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;
+
+ 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;
+
+ if( i_extra_codec > 0 )
+ {
+ fmt.p_extra = malloc( i_extra_codec );
+ if( !fmt.p_extra || i_extra_codec > i_data )
+ return VLC_ENOMEM;
+
+ fmt.i_extra = i_extra_codec;
+ memcpy( fmt.p_extra, p_data, fmt.i_extra );