static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i_request) { VLC_UNUSED(p_archive); callback_data_t *p_data = (callback_data_t *) p_object; access_sys_t *p_sys = p_data->p_access->p_sys; ssize_t i_skipped = 0; /* be smart as small seeks converts to reads */ if (p_sys->b_source_canseek) { int64_t i_pos = stream_Tell(p_sys->p_stream); if (i_pos >=0) stream_Seek(p_sys->p_stream, i_pos + i_request); i_skipped = stream_Tell(p_sys->p_stream) - i_pos; } else while(i_request) { int i_skip = __MIN(INT32_MAX, i_request); int i_read = stream_Read(p_sys->p_stream, NULL, i_skip); if (i_read > 0) i_skipped += i_read; else break; i_request -= i_read; } return i_skipped; }
static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence) { VLC_UNUSED(p_archive); callback_data_t *p_data = (callback_data_t *) p_object; access_sys_t *p_sys = p_data->p_access->p_sys; ssize_t i_pos; switch(i_whence) { case SEEK_CUR: i_pos = stream_Tell(p_sys->p_stream); break; case SEEK_SET: i_pos = 0; break; case SEEK_END: i_pos = stream_Size(p_sys->p_stream) - 1; break; default: return -1; } if (i_pos < 0) return -1; stream_Seek(p_sys->p_stream, i_pos + i_offset); /* We don't care about return val */ return stream_Tell(p_sys->p_stream); }
/**************************************************************************** * * Basics functions to manipulates chunks * ****************************************************************************/ static int AVI_ChunkReadCommon( stream_t *s, avi_chunk_t *p_chk ) { const uint8_t *p_peek; int i_peek; memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( ( i_peek = stream_Peek( s, &p_peek, 8 ) ) < 8 ) { return VLC_EGENERIC; } p_chk->common.i_chunk_fourcc = GetFOURCC( p_peek ); p_chk->common.i_chunk_size = GetDWLE( p_peek + 4 ); p_chk->common.i_chunk_pos = stream_Tell( s ); p_chk->common.p_father = NULL; p_chk->common.p_next = NULL; p_chk->common.p_first = NULL; p_chk->common.p_next = NULL; #ifdef AVI_DEBUG msg_Dbg( (vlc_object_t*)s, "found Chunk fourcc:%8.8x (%4.4s) size:%"PRId64" pos:%"PRId64, p_chk->common.i_chunk_fourcc, (char*)&p_chk->common.i_chunk_fourcc, p_chk->common.i_chunk_size, p_chk->common.i_chunk_pos ); #endif return VLC_SUCCESS; }
void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode ) { int64_t i_pos, i_size; switch( mode ) { case seek_beginning: i_pos = i_offset; break; case seek_end: i_pos = stream_Size( s ) - i_offset; break; default: i_pos= stream_Tell( s ) + i_offset; break; } if( i_pos < 0 || ( ( i_size = stream_Size( s ) ) != 0 && i_pos >= i_size ) ) { mb_eof = true; return; } mb_eof = false; if( stream_Seek( s, i_pos ) ) { mb_eof = true; } return; }
/***************************************************************************** * ParseTags: check if ID3/APE tags at common locations. ****************************************************************************/ static int ParseTags( vlc_object_t *p_this ) { demux_meta_t *p_demux_meta = (demux_meta_t *)p_this; demux_t *p_demux = (demux_t *)p_demux_meta->p_demux; bool b_seekable; int64_t i_init; msg_Dbg( p_demux_meta, "checking for ID3v1/2 and APEv1/2 tags" ); stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_seekable ); if( !b_seekable ) return VLC_EGENERIC; i_init = stream_Tell( p_demux->s ); TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments ); p_demux_meta->p_meta = NULL; /* */ CheckFooter( p_demux_meta ); /* */ CheckHeader( p_demux_meta ); /* Restore position * Demuxer will not see tags at the start as src/input/demux.c skips it * for them */ stream_Seek( p_demux->s, i_init ); if( !p_demux_meta->p_meta && p_demux_meta->i_attachments <= 0 ) return VLC_EGENERIC; return VLC_SUCCESS; }
/***************************************************************************** * Demux: read packet and send them to decoders ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; block_t *p_block; int64_t i_offset; unsigned i_frames = p_sys->i_block_frames; i_offset = stream_Tell( p_demux->s ); if( p_sys->i_data_size > 0 && i_offset >= p_sys->i_data_offset + p_sys->i_data_size ) { /* EOF */ return 0; } p_block = stream_Block( p_demux->s, p_sys->fmt.audio.i_bytes_per_frame * i_frames ); if( p_block == NULL ) { msg_Warn( p_demux, "cannot read data" ); return 0; } i_frames = p_block->i_buffer / p_sys->fmt.audio.i_bytes_per_frame; p_block->i_dts = p_block->i_pts = VLC_TS_0 + date_Get( &p_sys->pts ); es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts ); es_out_Send( p_demux->out, p_sys->p_es, p_block ); date_Increment( &p_sys->pts, i_frames * p_sys->fmt.audio.i_frame_length ); return 1; }
/** * Reads (delta) time from the next event of a given track. * @param s stream to read data from (must be positioned at the right offset) */ static int ReadDeltaTime (stream_t *s, mtrk_t *track) { int32_t delta_time; assert (stream_Tell (s) == track->offset); if (track->offset >= track->end) { /* This track is done */ track->next = UINT64_MAX; return 0; } delta_time = ReadVarInt (s); if (delta_time < 0) return -1; track->next += delta_time; track->offset = stream_Tell (s); return 0; }
/***************************************************************************** * Control: *****************************************************************************/ static int Control( demux_t *p_demux, int i_query, va_list args ) { int i_ret = demux_vaControlHelper( p_demux->s, 0, -1, 8*CDG_FRAME_SIZE*CDG_FRAME_RATE, CDG_FRAME_SIZE, i_query, args ); if( !i_ret && ( i_query == DEMUX_SET_POSITION || i_query == DEMUX_SET_TIME ) ) date_Set( &p_demux->p_sys->pts, stream_Tell( p_demux->s ) / CDG_FRAME_SIZE * INT64_C(1000000) / CDG_FRAME_RATE ); return i_ret; }
int AVI_ChunkReadRoot( stream_t *s, avi_chunk_t *p_root ) { avi_chunk_list_t *p_list = (avi_chunk_list_t*)p_root; avi_chunk_t *p_chk; bool b_seekable; stream_Control( s, STREAM_CAN_FASTSEEK, &b_seekable ); p_list->i_chunk_pos = 0; p_list->i_chunk_size = stream_Size( s ); p_list->i_chunk_fourcc = AVIFOURCC_LIST; p_list->p_father = NULL; p_list->p_next = NULL; p_list->p_first = NULL; p_list->p_last = NULL; p_list->i_type = VLC_FOURCC( 'r', 'o', 'o', 't' ); for( ; ; ) { p_chk = malloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_root->common.p_first ) { p_root->common.p_first = p_chk; } else { p_root->common.p_last->common.p_next = p_chk; } p_root->common.p_last = p_chk; if( AVI_ChunkRead( s, p_chk, p_root ) || ( stream_Tell( s ) >= (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found first RIFF-AVI */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF && p_chk->list.i_type == AVIFOURCC_AVI && !b_seekable ) { break; } } AVI_ChunkDumpDebug_level( (vlc_object_t*)s, p_root, 0 ); return VLC_SUCCESS; }
uint64 vlc_stream_io_callback::toRead( void ) { uint64_t i_size; if( s == NULL) return 0; i_size = stream_Size( s ); if( i_size == 0 ) return UINT64_MAX; return (uint64) i_size - stream_Tell( s ); }
uint64 vlc_stream_io_callback::toRead( void ) { uint64_t i_size; if( s == NULL) return 0; i_size = stream_Size( s ); if( i_size <= 0 ) return UINT64_MAX; return static_cast<uint64>( i_size - stream_Tell( s ) ); }
uint64 vlc_stream_io_callback::toRead( void ) { uint64_t i_size; if( s == NULL) return 0; stream_Control( s, STREAM_GET_SIZE, &i_size ); if( i_size == 0 ) return UINT64_MAX; return (uint64) i_size - stream_Tell( s ); }
/* Allow to append indexes after starting playback */ int AVI_ChunkFetchIndexes( stream_t *s, avi_chunk_t *p_riff ) { avi_chunk_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0 ); if ( !p_movi ) return VLC_EGENERIC; avi_chunk_t *p_chk; uint64_t i_indexpos = 8 + p_movi->common.i_chunk_pos + p_movi->common.i_chunk_size; bool b_seekable = false; int i_ret = VLC_SUCCESS; stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); if ( !b_seekable || stream_Seek( s, i_indexpos ) ) return VLC_EGENERIC; for( ; ; ) { p_chk = xmalloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if (unlikely( !p_riff->common.p_first )) p_riff->common.p_first = p_chk; else p_riff->common.p_last->common.p_next = p_chk; p_riff->common.p_last = p_chk; i_ret = AVI_ChunkRead( s, p_chk, p_riff ); if( i_ret ) break; if( p_chk->common.p_father->common.i_chunk_size > 0 && ( stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found any index */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_indx || p_chk->common.i_chunk_fourcc == AVIFOURCC_idx1 ) { break; } } return i_ret; }
static bool IsTarga(stream_t *s) { /* The header is not enough to ensure proper detection, we need * to have a look at the footer. But doing so can be slow. So * try to avoid it when possible */ const uint8_t *header; if (stream_Peek(s, &header, 18) < 18) /* Targa fixed header */ return false; if (header[1] > 1) /* Color Map Type */ return false; if ((header[1] != 0 || header[3 + 4] != 0) && header[3 + 4] != 8 && header[3 + 4] != 15 && header[3 + 4] != 16 && header[3 + 4] != 24 && header[3 + 4] != 32) return false; if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */ return false; if (GetWLE(&header[8 + 4]) <= 0 || /* Width */ GetWLE(&header[8 + 6]) <= 0) /* Height */ return false; if (header[8 + 8] != 8 && header[8 + 8] != 15 && header[8 + 8] != 16 && header[8 + 8] != 24 && header[8 + 8] != 32) return false; if (header[8 + 9] & 0xc0) /* Reserved bits */ return false; const int64_t size = stream_Size(s); if (size <= 18 + 26) return false; bool can_seek; if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek) return false; const int64_t position = stream_Tell(s); if (stream_Seek(s, size - 26)) return false; const uint8_t *footer; bool is_targa = stream_Peek(s, &footer, 26) >= 26 && !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18); stream_Seek(s, position); return is_targa; }
/***************************************************************************** * Demux: read packet and send them to decoders ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; block_t *p_block; const int64_t i_pos = stream_Tell( p_demux->s ); if( p_sys->i_data_size > 0 && i_pos >= p_sys->i_data_pos + p_sys->i_data_size ) { /* EOF */ return 0; } if( ( p_block = stream_Block( p_demux->s, p_sys->i_frame_size ) ) == NULL ) { msg_Warn( p_demux, "cannot read data" ); return 0; } p_block->i_dts = p_block->i_pts = VLC_TS_0 + date_Get( &p_sys->pts ); /* set PCR */ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts ); /* Do the channel reordering */ if( p_sys->i_chans_to_reorder ) aout_ChannelReorder( p_block->p_buffer, p_block->i_buffer, p_sys->fmt.audio.i_channels, p_sys->pi_chan_table, p_sys->fmt.i_codec ); es_out_Send( p_demux->out, p_sys->p_es, p_block ); date_Increment( &p_sys->pts, p_sys->i_frame_samples ); return 1; }
uint64 vlc_stream_io_callback::getFilePointer( void ) { if ( s == NULL ) return 0; return stream_Tell( s ); }
static uint64_t stream_tell( struct reader *p_reader ) { return stream_Tell( p_reader->u.s ); }
char *stream_ReadLine( stream_t *s ) { stream_priv_t *priv = (stream_priv_t *)s; char *p_line = NULL; int i_line = 0, i_read = 0; /* Let's fail quickly if this is a readdir access */ if( s->pf_read == NULL ) return NULL; for( ;; ) { char *psz_eol; const uint8_t *p_data; int i_data; int64_t i_pos; /* Probe new data */ i_data = stream_Peek( s, &p_data, STREAM_PROBE_LINE ); if( i_data <= 0 ) break; /* No more data */ /* BOM detection */ i_pos = stream_Tell( s ); if( i_pos == 0 && i_data >= 2 ) { const char *psz_encoding = NULL; if( !memcmp( p_data, "\xFF\xFE", 2 ) ) { psz_encoding = "UTF-16LE"; priv->text.little_endian = true; } else if( !memcmp( p_data, "\xFE\xFF", 2 ) ) { psz_encoding = "UTF-16BE"; } /* Open the converter if we need it */ if( psz_encoding != NULL ) { msg_Dbg( s, "UTF-16 BOM detected" ); priv->text.char_width = 2; priv->text.conv = vlc_iconv_open( "UTF-8", psz_encoding ); if( priv->text.conv == (vlc_iconv_t)-1 ) msg_Err( s, "iconv_open failed" ); } } if( i_data % priv->text.char_width ) { /* keep i_char_width boundary */ i_data = i_data - ( i_data % priv->text.char_width ); msg_Warn( s, "the read is not i_char_width compatible"); } if( i_data == 0 ) break; /* Check if there is an EOL */ if( priv->text.char_width == 1 ) { /* UTF-8: 0A <LF> */ psz_eol = memchr( p_data, '\n', i_data ); if( psz_eol == NULL ) /* UTF-8: 0D <CR> */ psz_eol = memchr( p_data, '\r', i_data ); } else { const uint8_t *p_last = p_data + i_data - priv->text.char_width; uint16_t eol = priv->text.little_endian ? 0x0A00 : 0x00A0; assert( priv->text.char_width == 2 ); psz_eol = NULL; /* UTF-16: 000A <LF> */ for( const uint8_t *p = p_data; p <= p_last; p += 2 ) { if( U16_AT( p ) == eol ) { psz_eol = (char *)p + 1; break; } } if( psz_eol == NULL ) { /* UTF-16: 000D <CR> */ eol = priv->text.little_endian ? 0x0D00 : 0x00D0; for( const uint8_t *p = p_data; p <= p_last; p += 2 ) { if( U16_AT( p ) == eol ) { psz_eol = (char *)p + 1; break; } } } } if( psz_eol ) { i_data = (psz_eol - (char *)p_data) + 1; p_line = realloc_or_free( p_line, i_line + i_data + priv->text.char_width ); /* add \0 */ if( !p_line ) goto error; i_data = stream_Read( s, &p_line[i_line], i_data ); if( i_data <= 0 ) break; /* Hmmm */ i_line += i_data - priv->text.char_width; /* skip \n */; i_read += i_data; /* We have our line */ break; } /* Read data (+1 for easy \0 append) */ p_line = realloc_or_free( p_line, i_line + STREAM_PROBE_LINE + priv->text.char_width ); if( !p_line ) goto error; i_data = stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE ); if( i_data <= 0 ) break; /* Hmmm */ i_line += i_data; i_read += i_data; if( i_read >= STREAM_LINE_MAX ) goto error; /* line too long */ } if( i_read > 0 ) { memset(p_line + i_line, 0, priv->text.char_width); i_line += priv->text.char_width; /* the added \0 */ if( priv->text.char_width > 1 ) { int i_new_line = 0; size_t i_in = 0, i_out = 0; const char * p_in = NULL; char * p_out = NULL; char * psz_new_line = NULL; /* iconv */ /* UTF-8 needs at most 150% of the buffer as many as UTF-16 */ i_new_line = i_line * 3 / 2; psz_new_line = malloc( i_new_line ); if( psz_new_line == NULL ) goto error; i_in = (size_t)i_line; i_out = (size_t)i_new_line; p_in = p_line; p_out = psz_new_line; if( vlc_iconv( priv->text.conv, &p_in, &i_in, &p_out, &i_out ) == (size_t)-1 ) { msg_Err( s, "iconv failed" ); msg_Dbg( s, "original: %d, in %d, out %d", i_line, (int)i_in, (int)i_out ); } free( p_line ); p_line = psz_new_line; i_line = (size_t)i_new_line - i_out; /* does not include \0 */ } /* Remove trailing LF/CR */ while( i_line >= 2 && ( p_line[i_line-2] == '\r' || p_line[i_line-2] == '\n') ) i_line--; /* Make sure the \0 is there */ p_line[i_line-1] = '\0'; return p_line; } error: /* We failed to read any data, probably EOF */ free( p_line ); /* */ if( priv->text.conv != (vlc_iconv_t)(-1) ) { vlc_iconv_close( priv->text.conv ); priv->text.conv = (vlc_iconv_t)(-1); } return NULL; }
/** * Non-MIDI Meta events handler */ static int HandleMeta (demux_t *p_demux, mtrk_t *tr) { stream_t *s = p_demux->s; demux_sys_t *p_sys = p_demux->p_sys; uint8_t *payload; uint8_t type; int32_t length; int ret = 0; if (stream_Read (s, &type, 1) != 1) return -1; length = ReadVarInt (s); if (length < 0) return -1; payload = malloc (length + 1); if ((payload == NULL) || (stream_Read (s, payload, length) != length)) { free (payload); return -1; } payload[length] = '\0'; switch (type) { case 0x00: /* Sequence Number */ break; case 0x01: /* Text (comment) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Text : %s", (char *)payload); break; case 0x02: /* Copyright */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Copyright : %s", (char *)payload); break; case 0x03: /* Track name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Track name: %s", (char *)payload); break; case 0x04: /* Instrument name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Instrument: %s", (char *)payload); break; case 0x05: /* Lyric (one syllable) */ /*EnsureUTF8 ((char *)payload);*/ break; case 0x06: /* Marker text */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Marker : %s", (char *)payload); case 0x07: /* Cue point (WAVE filename) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Cue point : %s", (char *)payload); break; case 0x08: /* Program/Patch name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Patch name: %s", (char *)payload); break; case 0x09: /* MIDI port name */ EnsureUTF8 ((char *)payload); msg_Dbg (p_demux, "MIDI port : %s", (char *)payload); break; case 0x2F: /* End of track */ if (tr->end != stream_Tell (s)) { msg_Err (p_demux, "misplaced end of track"); ret = -1; } break; case 0x51: /* Tempo */ if (length == 3) { uint32_t uspqn = (payload[0] << 16) | (payload[1] << 8) | payload[2]; unsigned tempo = 60 * 1000000 / (uspqn ? uspqn : 1); msg_Dbg (p_demux, "tempo: %uus/qn -> %u BPM", (unsigned)uspqn, tempo); if (tempo < TEMPO_MIN) { msg_Warn (p_demux, "tempo too slow -> %u BPM", TEMPO_MIN); tempo = TEMPO_MIN; } else if (tempo > TEMPO_MAX) { msg_Warn (p_demux, "tempo too fast -> %u BPM", TEMPO_MAX); tempo = TEMPO_MAX; } date_Change (&p_sys->pts, p_sys->ppqn * tempo, 60); } else ret = -1; break; case 0x54: /* SMPTE offset */ if (length == 5) msg_Warn (p_demux, "SMPTE offset not implemented"); else ret = -1; break; case 0x58: /* Time signature */ if (length == 4) ; else ret = -1; break; case 0x59: /* Key signature */ if (length == 2) ; else ret = -1; break; case 0x7f: /* Proprietary event */ msg_Dbg (p_demux, "ignored proprietary SMF Meta Event (%d bytes)", length); break; default: msg_Warn (p_demux, "unknown SMF Meta Event type 0x%02X (%d bytes)", type, length); } free (payload); return ret; }
/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; unsigned int i_size; unsigned int i_extended; const char *psz_name; WAVEFORMATEXTENSIBLE *p_wf_ext = NULL; WAVEFORMATEX *p_wf = NULL; /* Is it a wav file ? */ if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 ) return VLC_EGENERIC; if( memcmp( p_peek, "RIFF", 4 ) || memcmp( &p_peek[8], "WAVE", 4 ) ) { return VLC_EGENERIC; } p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = malloc( sizeof( *p_sys ) ); if( unlikely(!p_sys) ) return VLC_ENOMEM; p_sys->p_es = NULL; p_sys->i_chans_to_reorder = 0; p_sys->i_channel_mask = 0; /* skip riff header */ if( stream_Read( p_demux->s, NULL, 12 ) != 12 ) goto error; /* search fmt chunk */ if( ChunkFind( p_demux, "fmt ", &i_size ) ) { msg_Err( p_demux, "cannot find 'fmt ' chunk" ); goto error; } i_size += 2; if( i_size < sizeof( WAVEFORMATEX ) ) { msg_Err( p_demux, "invalid 'fmt ' chunk" ); goto error; } if( stream_Read( p_demux->s, NULL, 8 ) != 8 ) goto error; /* load waveformatex */ p_wf_ext = malloc( i_size ); if( unlikely( !p_wf_ext ) ) goto error; p_wf = &p_wf_ext->Format; p_wf->cbSize = 0; i_size -= 2; if( stream_Read( p_demux->s, p_wf, i_size ) != (int)i_size || ( ( i_size & 1 ) && stream_Read( p_demux->s, NULL, 1 ) != 1 ) ) { msg_Err( p_demux, "cannot load 'fmt ' chunk" ); goto error; } es_format_Init( &p_sys->fmt, AUDIO_ES, 0 ); wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_sys->fmt.i_codec, &psz_name ); p_sys->fmt.audio.i_channels = GetWLE ( &p_wf->nChannels ); p_sys->fmt.audio.i_rate = GetDWLE( &p_wf->nSamplesPerSec ); p_sys->fmt.audio.i_blockalign = GetWLE( &p_wf->nBlockAlign ); p_sys->fmt.i_bitrate = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8; p_sys->fmt.audio.i_bitspersample = GetWLE( &p_wf->wBitsPerSample ); if( i_size >= sizeof(WAVEFORMATEX) ) p_sys->fmt.i_extra = __MIN( GetWLE( &p_wf->cbSize ), i_size - sizeof(WAVEFORMATEX) ); i_extended = 0; /* Handle new WAVE_FORMAT_EXTENSIBLE wav files */ /* see the following link for more information: * http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EFAA */ if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_EXTENSIBLE && i_size >= sizeof( WAVEFORMATEXTENSIBLE ) && ( p_sys->fmt.i_extra + sizeof( WAVEFORMATEX ) >= sizeof( WAVEFORMATEXTENSIBLE ) ) ) { unsigned i_channel_mask; GUID guid_subformat; guid_subformat = p_wf_ext->SubFormat; guid_subformat.Data1 = GetDWLE( &p_wf_ext->SubFormat.Data1 ); guid_subformat.Data2 = GetWLE( &p_wf_ext->SubFormat.Data2 ); guid_subformat.Data3 = GetWLE( &p_wf_ext->SubFormat.Data3 ); sf_tag_to_fourcc( &guid_subformat, &p_sys->fmt.i_codec, &psz_name ); i_extended = sizeof( WAVEFORMATEXTENSIBLE ) - sizeof( WAVEFORMATEX ); p_sys->fmt.i_extra -= i_extended; i_channel_mask = GetDWLE( &p_wf_ext->dwChannelMask ); if( i_channel_mask ) { int i_match = 0; for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(*pi_channels_src); i++ ) { if( i_channel_mask & pi_channels_src[i] ) { if( !( p_sys->i_channel_mask & pi_channels_in[i] ) ) i_match++; i_channel_mask &= ~pi_channels_src[i]; p_sys->i_channel_mask |= pi_channels_in[i]; if( i_match >= p_sys->fmt.audio.i_channels ) break; } } if( i_channel_mask ) msg_Warn( p_demux, "Some channels are unrecognized or uselessly specified (0x%x)", i_channel_mask ); if( i_match < p_sys->fmt.audio.i_channels ) { int i_missing = p_sys->fmt.audio.i_channels - i_match; msg_Warn( p_demux, "Trying to fill up unspecified position for %d channels", p_sys->fmt.audio.i_channels - i_match ); static const uint32_t pi_pair[] = { AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT, AOUT_CHAN_MIDDLELEFT|AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT }; /* FIXME: Unused yet static const uint32_t pi_center[] = { AOUT_CHAN_REARCENTER, 0, AOUT_CHAN_CENTER }; */ /* Try to complete with pair */ for( unsigned i = 0; i < sizeof(pi_pair)/sizeof(*pi_pair); i++ ) { if( i_missing >= 2 && !(p_sys->i_channel_mask & pi_pair[i] ) ) { i_missing -= 2; p_sys->i_channel_mask |= pi_pair[i]; } } /* Well fill up with what we can */ for( unsigned i = 0; i < sizeof(pi_channels_in)/sizeof(*pi_channels_in) && i_missing > 0; i++ ) { if( !( p_sys->i_channel_mask & pi_channels_in[i] ) ) { p_sys->i_channel_mask |= pi_channels_in[i]; i_missing--; if( i_missing <= 0 ) break; } } i_match = p_sys->fmt.audio.i_channels - i_missing; } if( i_match < p_sys->fmt.audio.i_channels ) { msg_Err( p_demux, "Invalid/unsupported channel mask" ); p_sys->i_channel_mask = 0; } } } else if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_PCM && p_sys->fmt.audio.i_channels > 2 && p_sys->fmt.audio.i_channels <= 9 ) { for( int i = 0; i < p_sys->fmt.audio.i_channels; i++ ) p_sys->i_channel_mask |= pi_channels_in[i]; } if( p_sys->i_channel_mask ) { if( p_sys->fmt.i_codec == VLC_FOURCC('a','r','a','w') || p_sys->fmt.i_codec == VLC_FOURCC('p','c','m',' ') || p_sys->fmt.i_codec == VLC_FOURCC('a','f','l','t') ) p_sys->i_chans_to_reorder = aout_CheckChannelReorder( pi_channels_in, NULL, p_sys->i_channel_mask, p_sys->pi_chan_table ); msg_Dbg( p_demux, "channel mask: %x, reordering: %u", p_sys->i_channel_mask, p_sys->i_chans_to_reorder ); } p_sys->fmt.audio.i_physical_channels = p_sys->fmt.audio.i_original_channels = p_sys->i_channel_mask; if( p_sys->fmt.i_extra > 0 ) { p_sys->fmt.p_extra = malloc( p_sys->fmt.i_extra ); if( unlikely(!p_sys->fmt.p_extra) ) { p_sys->fmt.i_extra = 0; goto error; } memcpy( p_sys->fmt.p_extra, (uint8_t *)p_wf + sizeof( WAVEFORMATEX ) + i_extended, p_sys->fmt.i_extra ); } msg_Dbg( p_demux, "format: 0x%4.4x, fourcc: %4.4s, channels: %d, " "freq: %u Hz, bitrate: %uKo/s, blockalign: %d, bits/samples: %d, " "extra size: %d", GetWLE( &p_wf->wFormatTag ), (char *)&p_sys->fmt.i_codec, p_sys->fmt.audio.i_channels, p_sys->fmt.audio.i_rate, p_sys->fmt.i_bitrate / 8 / 1024, p_sys->fmt.audio.i_blockalign, p_sys->fmt.audio.i_bitspersample, p_sys->fmt.i_extra ); free( p_wf ); p_wf = NULL; switch( p_sys->fmt.i_codec ) { case VLC_FOURCC( 'a', 'r', 'a', 'w' ): case VLC_FOURCC( 'a', 'f', 'l', 't' ): case VLC_FOURCC( 'u', 'l', 'a', 'w' ): case VLC_CODEC_ALAW: case VLC_CODEC_MULAW: case VLC_FOURCC( 'p', 'c', 'm', ' ' ): if( FrameInfo_PCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_ADPCM_MS: if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_ADPCM_IMA_WAV: if( FrameInfo_IMA_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_FOURCC( 'm', 's', 0x00, 0x61 ): case VLC_FOURCC( 'm', 's', 0x00, 0x62 ): /* FIXME not sure at all FIXME */ if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_MPGA: case VLC_CODEC_A52: /* FIXME set end of area FIXME */ goto error; case VLC_CODEC_GSM_MS: case VLC_CODEC_ADPCM_G726: case VLC_CODEC_TRUESPEECH: case VLC_CODEC_ATRAC3: case VLC_CODEC_G723_1: if( FrameInfo_MSGSM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; default: msg_Err( p_demux, "unsupported codec (%4.4s)", (char*)&p_sys->fmt.i_codec ); goto error; } if( p_sys->i_frame_size <= 0 || p_sys->i_frame_samples <= 0 ) { msg_Dbg( p_demux, "invalid frame size: %i %i", p_sys->i_frame_size, p_sys->i_frame_samples ); goto error; } if( p_sys->fmt.audio.i_rate <= 0 ) { msg_Dbg( p_demux, "invalid sample rate: %i", p_sys->fmt.audio.i_rate ); goto error; } msg_Dbg( p_demux, "found %s audio format", psz_name ); if( ChunkFind( p_demux, "data", &p_sys->i_data_size ) ) { msg_Err( p_demux, "cannot find 'data' chunk" ); goto error; } if( stream_Read( p_demux->s, NULL, 8 ) != 8 ) goto error; p_sys->i_data_pos = stream_Tell( p_demux->s ); if( p_sys->fmt.i_bitrate <= 0 ) { p_sys->fmt.i_bitrate = (int64_t)p_sys->i_frame_size * p_sys->fmt.audio.i_rate * 8 / p_sys->i_frame_samples; } p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt ); date_Init( &p_sys->pts, p_sys->fmt.audio.i_rate, 1 ); date_Set( &p_sys->pts, 1 ); return VLC_SUCCESS; error: msg_Err( p_demux, "An error occurred during wav demuxing" ); free( p_wf ); free( p_sys ); return VLC_EGENERIC; }
static int HandleMessage (demux_t *p_demux, mtrk_t *tr) { stream_t *s = p_demux->s; block_t *block; uint8_t first, event; unsigned datalen; if (stream_Seek (s, tr->offset) || (stream_Read (s, &first, 1) != 1)) return -1; event = (first & 0x80) ? first : tr->running_event; switch (event & 0xf0) { case 0xF0: /* System Exclusive */ switch (event) { case 0xF0: /* System Specific start */ case 0xF7: /* System Specific continuation */ { /* Variable length followed by SysEx event data */ int32_t len = ReadVarInt (s); if (len == -1) return -1; block = stream_Block (s, len); if (block == NULL) return -1; block = block_Realloc (block, 1, len); if (block == NULL) return -1; block->p_buffer[0] = event; goto send; } case 0xFF: /* SMF Meta Event */ if (HandleMeta (p_demux, tr)) return -1; /* We MUST NOT pass this event forward. It would be * confused as a MIDI Reset real-time event. */ goto skip; case 0xF1: case 0xF3: datalen = 1; break; case 0xF2: datalen = 2; break; case 0xF4: case 0xF5: /* We cannot handle undefined "common" (non-real-time) * events inside SMF, as we cannot differentiate a * one byte delta-time (< 0x80) from event data. */ default: datalen = 0; break; } break; case 0xC0: case 0xD0: datalen = 1; break; default: datalen = 2; break; } /* FIXME: one message per block is very inefficient */ block = block_New (p_demux, 1 + datalen); if (block == NULL) goto skip; block->p_buffer[0] = event; if (first & 0x80) { stream_Read (s, block->p_buffer + 1, datalen); } else { if (datalen == 0) { msg_Err (p_demux, "malformatted MIDI event"); return -1; /* can't use implicit running status with empty payload! */ } block->p_buffer[1] = first; if (datalen > 1) stream_Read (s, block->p_buffer + 2, datalen - 1); } send: block->i_dts = block->i_pts = VLC_TS_0 + date_Get (&p_demux->p_sys->pts); es_out_Send (p_demux->out, p_demux->p_sys->es, block); skip: if (event < 0xF8) /* If event is not real-time, update running status */ tr->running_event = event; tr->offset = stream_Tell (s); return 0; }
/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/ static int Open (vlc_object_t * p_this) { demux_t *p_demux = (demux_t *)p_this; stream_t *stream = p_demux->s; demux_sys_t *p_sys; const uint8_t *peek; unsigned tracks, ppqn; bool multitrack; /* (Try to) parse the SMF header */ /* Header chunk always has 6 bytes payload */ if (stream_Peek (stream, &peek, 14) < 14) return VLC_EGENERIC; /* Skip RIFF MIDI header if present */ if (!memcmp (peek, "RIFF", 4) && !memcmp (peek + 8, "RMID", 4)) { uint32_t riff_len = GetDWLE (peek + 4); msg_Dbg (p_this, "detected RIFF MIDI file (%u bytes)", (unsigned)riff_len); if ((stream_Read (stream, NULL, 12) < 12)) return VLC_EGENERIC; /* Look for the RIFF data chunk */ for (;;) { char chnk_hdr[8]; uint32_t chnk_len; if ((riff_len < 8) || (stream_Read (stream, chnk_hdr, 8) < 8)) return VLC_EGENERIC; riff_len -= 8; chnk_len = GetDWLE (chnk_hdr + 4); if (riff_len < chnk_len) return VLC_EGENERIC; riff_len -= chnk_len; if (!memcmp (chnk_hdr, "data", 4)) break; /* found! */ if (stream_Read (stream, NULL, chnk_len) < (ssize_t)chnk_len) return VLC_EGENERIC; } /* Read real SMF header. Assume RIFF data chunk length is proper. */ if (stream_Peek (stream, &peek, 14) < 14) return VLC_EGENERIC; } if (memcmp (peek, "MThd\x00\x00\x00\x06", 8)) return VLC_EGENERIC; peek += 8; /* First word: SMF type */ switch (GetWBE (peek)) { case 0: multitrack = false; break; case 1: multitrack = true; break; default: /* We don't implement SMF2 (as do many) */ msg_Err (p_this, "unsupported SMF file type %u", GetWBE (peek)); return VLC_EGENERIC; } peek += 2; /* Second word: number of tracks */ tracks = GetWBE (peek); peek += 2; if (!multitrack && (tracks != 1)) { msg_Err (p_this, "invalid SMF type 0 file"); return VLC_EGENERIC; } msg_Dbg (p_this, "detected Standard MIDI File (type %u) with %u track(s)", multitrack, tracks); /* Third/last word: timing */ ppqn = GetWBE (peek); if (ppqn & 0x8000) { /* FIXME */ msg_Err (p_this, "SMPTE timestamps not implemented"); return VLC_EGENERIC; } else { msg_Dbg (p_this, " %u pulses per quarter note", ppqn); } p_sys = malloc (sizeof (*p_sys) + (sizeof (mtrk_t) * tracks)); if (p_sys == NULL) return VLC_ENOMEM; /* We've had a valid SMF header - now skip it*/ if (stream_Read (stream, NULL, 14) < 14) goto error; p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys; /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */ date_Init (&p_sys->pts, ppqn * 2, 1); date_Set (&p_sys->pts, 0); p_sys->pulse = 0; p_sys->ppqn = ppqn; p_sys->trackc = tracks; /* Prefetch track offsets */ for (unsigned i = 0; i < tracks; i++) { uint8_t head[8]; if (i > 0) { /* Seeking screws streaming up, but there is no way around this, * as SMF1 tracks are performed simultaneously. * Not a big deal as SMF1 are usually only a few kbytes anyway. */ if (stream_Seek (stream, p_sys->trackv[i-1].end)) { msg_Err (p_this, "cannot build SMF index (corrupted file?)"); goto error; } } for (;;) { if (stream_Read (stream, head, 8) < 8) { /* FIXME: don't give up if we have at least one valid track */ msg_Err (p_this, "incomplete SMF chunk, file is corrupted"); goto error; } if (memcmp (head, "MTrk", 4) == 0) break; msg_Dbg (p_this, "skipping unknown SMF chunk"); stream_Read (stream, NULL, GetDWBE (head + 4)); } p_sys->trackv[i].offset = stream_Tell (stream); p_sys->trackv[i].end = p_sys->trackv[i].offset + GetDWBE (head + 4); p_sys->trackv[i].next = 0; ReadDeltaTime (stream, p_sys->trackv + i); p_sys->trackv[i].running_event = 0xF6; /* Why 0xF6 (Tuning Calibration)? * Because it has zero bytes of data, so the parser will detect the * error if the first event uses running status. */ } es_format_t fmt; es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_MIDI); fmt.audio.i_channels = 2; fmt.audio.i_rate = 44100; /* dummy value */ p_sys->es = es_out_Add (p_demux->out, &fmt); return VLC_SUCCESS; error: free (p_sys); return VLC_EGENERIC; }
/***************************************************************************** * Demux: reads and demuxes data packets ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; block_t *p_block_in, *p_block_out; /* Align stream */ int64_t i_pos = stream_Tell( p_demux->s ); if( i_pos % 2 ) stream_Read( p_demux->s, NULL, 1 ); if( !( p_block_in = stream_Block( p_demux->s, A52_PACKET_SIZE ) ) ) { return 0; } if( !p_sys->b_big_endian && p_block_in->i_buffer ) { /* Convert to big endian */ #ifdef HAVE_SWAB swab(p_block_in->p_buffer, p_block_in->p_buffer, p_block_in->i_buffer); #else int i; byte_t *p_tmp, tmp; p_tmp = p_block_in->p_buffer; for( i = p_block_in->i_buffer / 2 ; i-- ; ) { tmp = p_tmp[0]; p_tmp[0] = p_tmp[1]; p_tmp[1] = tmp; p_tmp += 2; } #endif } if( p_sys->b_start ) p_block_in->i_pts = p_block_in->i_dts = 1; else p_block_in->i_pts = p_block_in->i_dts = 0; while( (p_block_out = p_sys->p_packetizer->pf_packetize( p_sys->p_packetizer, &p_block_in )) ) { p_sys->b_start = VLC_FALSE; while( p_block_out ) { block_t *p_next = p_block_out->p_next; /* We assume a constant bitrate */ if( p_block_out->i_length ) { p_sys->i_mux_rate = p_block_out->i_buffer * I64C(1000000)/p_block_out->i_length; } /* set PCR */ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block_out->i_dts ); es_out_Send( p_demux->out, p_sys->p_es, p_block_out ); p_block_out = p_next; } } return 1; }
/**************************************************************************** * * Functions to read chunks * ****************************************************************************/ static int AVI_ChunkRead_list( stream_t *s, avi_chunk_t *p_container ) { avi_chunk_t *p_chk; const uint8_t *p_peek; bool b_seekable; if( p_container->common.i_chunk_size > 0 && p_container->common.i_chunk_size < 8 ) { /* empty box */ msg_Warn( (vlc_object_t*)s, "empty list chunk" ); return VLC_EGENERIC; } if( stream_Peek( s, &p_peek, 12 ) < 12 ) { msg_Warn( (vlc_object_t*)s, "cannot peek while reading list chunk" ); return VLC_EGENERIC; } stream_Control( s, STREAM_CAN_FASTSEEK, &b_seekable ); p_container->list.i_type = GetFOURCC( p_peek + 8 ); /* XXX fixed for on2 hack */ if( p_container->common.i_chunk_fourcc == AVIFOURCC_ON2 && p_container->list.i_type == AVIFOURCC_ON2f ) { p_container->common.i_chunk_fourcc = AVIFOURCC_RIFF; p_container->list.i_type = AVIFOURCC_AVI; } if( p_container->common.i_chunk_fourcc == AVIFOURCC_LIST && p_container->list.i_type == AVIFOURCC_movi ) { msg_Dbg( (vlc_object_t*)s, "skipping movi chunk" ); if( b_seekable ) { return AVI_NextChunk( s, p_container ); } return VLC_SUCCESS; /* point at begining of LIST-movi */ } if( stream_Read( s, NULL, 12 ) != 12 ) { msg_Warn( (vlc_object_t*)s, "cannot enter chunk" ); return VLC_EGENERIC; } #ifdef AVI_DEBUG msg_Dbg( (vlc_object_t*)s, "found LIST chunk: \'%4.4s\'", (char*)&p_container->list.i_type ); #endif msg_Dbg( (vlc_object_t*)s, "<list \'%4.4s\'>", (char*)&p_container->list.i_type ); for( ; ; ) { p_chk = malloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_container->common.p_first ) { p_container->common.p_first = p_chk; } else { p_container->common.p_last->common.p_next = p_chk; } p_container->common.p_last = p_chk; if( AVI_ChunkRead( s, p_chk, p_container ) ) { break; } if( p_chk->common.p_father->common.i_chunk_size > 0 && ( stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found LIST-movi */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST && p_chk->list.i_type == AVIFOURCC_movi && ( !b_seekable || p_chk->common.i_chunk_size == 0 ) ) { break; } } msg_Dbg( (vlc_object_t*)s, "</list \'%4.4s\'>", (char*)&p_container->list.i_type ); return VLC_SUCCESS; }
/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; xa_header_t p_xa; const uint8_t *p_buf; /* XA file heuristic */ if( stream_Peek( p_demux->s, &p_buf, sizeof( p_xa ) ) < (signed)sizeof( p_xa ) ) return VLC_EGENERIC; memcpy( &p_xa, p_buf, sizeof( p_xa ) ); if( ( strncmp( p_xa.xa_id, "XAI", 4 ) && strncmp( p_xa.xa_id, "XAJ", 4 ) ) || ( GetWLE( &p_xa.wFormatTag ) != 0x0001) || ( GetWLE( &p_xa.wBitsPerSample ) != 16) ) return VLC_EGENERIC; p_sys = malloc( sizeof( demux_sys_t ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys; p_sys->p_es = NULL; /* skip XA header -- cannot fail */ stream_Read( p_demux->s, NULL, sizeof( p_xa ) ); es_format_Init( &p_sys->fmt, AUDIO_ES, VLC_FOURCC('X','A','J',0) ); msg_Dbg( p_demux, "assuming EA ADPCM audio codec" ); p_sys->fmt.audio.i_rate = GetDWLE( &p_xa.nSamplesPerSec ); p_sys->fmt.audio.i_bytes_per_frame = 15 * GetWLE( &p_xa.nChannels ); p_sys->fmt.audio.i_frame_length = 28; /* 28 samples of 4 bits each */ p_sys->fmt.audio.i_channels = GetWLE ( &p_xa.nChannels ); p_sys->fmt.audio.i_blockalign = p_sys->fmt.audio.i_bytes_per_frame; p_sys->fmt.audio.i_bitspersample = 16; p_sys->fmt.i_bitrate = (p_sys->fmt.audio.i_rate * p_sys->fmt.audio.i_bytes_per_frame * 8) / p_sys->fmt.audio.i_frame_length; p_sys->fmt.i_extra = 0; p_sys->fmt.p_extra = NULL; p_sys->i_data_offset = stream_Tell( p_demux->s ); /* FIXME: better computation */ p_sys->i_data_size = p_xa.iSize * 15 / 56; /* How many frames per block (1:1 is too CPU intensive) */ p_sys->i_block_frames = p_sys->fmt.audio.i_rate / (28 * 20) + 1; msg_Dbg( p_demux, "fourcc: %4.4s, channels: %d, " "freq: %d Hz, bitrate: %dKo/s, blockalign: %d", (char *)&p_sys->fmt.i_codec, p_sys->fmt.audio.i_channels, p_sys->fmt.audio.i_rate, p_sys->fmt.i_bitrate / 8192, p_sys->fmt.audio.i_blockalign ); p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt ); date_Init( &p_sys->pts, p_sys->fmt.audio.i_rate, 1 ); date_Set( &p_sys->pts, 1 ); return VLC_SUCCESS; }
static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr, const char *volume_mrl) { const uint8_t *peek; int min_size = 7+21; if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) min_size += 8; if (hdr->size < (unsigned)min_size) return VLC_EGENERIC; if (stream_Peek(s, &peek, min_size) < min_size) return VLC_EGENERIC; /* */ uint32_t file_size_low = GetDWLE(&peek[7+4]); uint8_t method = peek[7+18]; uint16_t name_size = GetWLE(&peek[7+19]); uint32_t file_size_high = 0; if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) file_size_high = GetDWLE(&peek[7+25]); const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low; char *name = calloc(1, name_size + 1); if (!name) return VLC_EGENERIC; const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25); if (name_offset + name_size <= hdr->size) { const int max_size = name_offset + name_size; if (stream_Peek(s, &peek, max_size) < max_size) { free(name); return VLC_EGENERIC; } memcpy(name, &peek[name_offset], name_size); } rar_file_t *current = NULL; if (method != 0x30) { msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method); goto exit; } /* */ if( *count > 0 ) current = (*file)[*count - 1]; if (current && (current->is_complete || strcmp(current->name, name) || (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0)) current = NULL; if (!current) { if (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) goto exit; current = malloc(sizeof(*current)); if (!current) goto exit; TAB_APPEND(*count, *file, current); current->name = name; current->size = file_size; current->is_complete = false; current->real_size = 0; TAB_INIT(current->chunk_count, current->chunk); name = NULL; } /* Append chunks */ rar_file_chunk_t *chunk = malloc(sizeof(*chunk)); if (chunk) { chunk->mrl = strdup(volume_mrl); chunk->offset = stream_Tell(s) + hdr->size; chunk->size = hdr->add_size; chunk->cummulated_size = 0; if (current->chunk_count > 0) { rar_file_chunk_t *previous = current->chunk[current->chunk_count-1]; chunk->cummulated_size += previous->cummulated_size + previous->size; } TAB_APPEND(current->chunk_count, current->chunk, chunk); current->real_size += hdr->add_size; } if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0) current->is_complete = true; exit: /* */ free(name); /* We stop on the first non empty file if we cannot seek */ if (current) { bool can_seek = false; stream_Control(s, STREAM_CAN_SEEK, &can_seek); if (!can_seek && current->size > 0) return VLC_EGENERIC; } if (SkipBlock(s, hdr)) return VLC_EGENERIC; return VLC_SUCCESS; }
int RarStreamOpen(vlc_object_t *object) { stream_t *s = (stream_t*)object; if (RarProbe(s->p_source)) return VLC_EGENERIC; int count; rar_file_t **files; const int64_t position = stream_Tell(s->p_source); if ((RarParse(s->p_source, &count, &files, false) && RarParse(s->p_source, &count, &files, true )) || count == 0 ) { stream_Seek(s->p_source, position); msg_Info(s, "Invalid or unsupported RAR archive"); free(files); return VLC_EGENERIC; } /* TODO use xspf to have node for directories * Reusing WriteXSPF from the zip access is probably a good idea * (becareful about '\' and '/'. */ char *mrl; if (asprintf(&mrl, "%s://%s", s->psz_access, s->psz_path)< 0) mrl = NULL; char *base; char *encoded = mrl ? encode_URI_component(mrl) : NULL; free(mrl); if (!encoded || asprintf(&base, "rar://%s", encoded) < 0) base = NULL; free(encoded); char *data = strdup("#EXTM3U\n"); for (int i = 0; i < count; i++) { rar_file_t *f = files[i]; char *next; if (base && data && asprintf(&next, "%s" "#EXTINF:,,%s\n" "%s|%s\n", data, f->name, base, f->name) >= 0) { free(data); data = next; } RarFileDelete(f); } free(base); free(files); if (!data) return VLC_EGENERIC; stream_t *payload = stream_MemoryNew(s, (uint8_t*)data, strlen(data), false); if (!payload) { free(data); return VLC_EGENERIC; } s->pf_read = Read; s->pf_peek = Peek; s->pf_control = Control; stream_sys_t *sys = s->p_sys = malloc(sizeof(*sys)); if (!sys) { stream_Delete(payload); return VLC_ENOMEM; } sys->payload = payload; char *tmp; if (asprintf(&tmp, "%s.m3u", s->psz_path) < 0) { RarStreamClose(object); return VLC_ENOMEM; } free(s->psz_path); s->psz_path = tmp; return VLC_SUCCESS; }