You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
541 lines
16 KiB
541 lines
16 KiB
/*****************************************************************************
|
|
* util.cpp : matroska demuxer
|
|
*****************************************************************************
|
|
* Copyright (C) 2003-2004 VLC authors and VideoLAN
|
|
* $Id$
|
|
*
|
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
|
* Steve Lhomme <steve.lhomme@free.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
#include "mkv.hpp"
|
|
#include "util.hpp"
|
|
#include "demux.hpp"
|
|
#include "../../codec/webvtt/helpers.h"
|
|
|
|
/*****************************************************************************
|
|
* Local prototypes
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_ZLIB_H
|
|
int32_t zlib_decompress_extra( demux_t * p_demux, mkv_track_t & tk )
|
|
{
|
|
int result;
|
|
z_stream d_stream;
|
|
size_t n = 0;
|
|
uint8_t * p_new_extra = NULL;
|
|
|
|
msg_Dbg(p_demux,"Inflating private data");
|
|
|
|
d_stream.zalloc = Z_NULL;
|
|
d_stream.zfree = Z_NULL;
|
|
d_stream.opaque = Z_NULL;
|
|
if( inflateInit( &d_stream ) != Z_OK )
|
|
{
|
|
msg_Err( p_demux, "Couldn't initiate inflation ignore track %u",
|
|
tk.i_number );
|
|
return 1;
|
|
}
|
|
|
|
d_stream.next_in = tk.p_extra_data;
|
|
d_stream.avail_in = tk.i_extra_data;
|
|
do
|
|
{
|
|
n++;
|
|
void *alloc = realloc(p_new_extra, n*1024);
|
|
if( alloc == NULL )
|
|
{
|
|
msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u",
|
|
tk.i_number );
|
|
free(p_new_extra);
|
|
inflateEnd( &d_stream );
|
|
return 1;
|
|
}
|
|
|
|
p_new_extra = static_cast<uint8_t *>( alloc );
|
|
d_stream.next_out = &p_new_extra[(n - 1) * 1024];
|
|
d_stream.avail_out = 1024;
|
|
result = inflate(&d_stream, Z_NO_FLUSH);
|
|
if( result != Z_OK && result != Z_STREAM_END )
|
|
{
|
|
msg_Err( p_demux, "Zlib decompression failed. Result: %d", result );
|
|
inflateEnd( &d_stream );
|
|
free(p_new_extra);
|
|
return 1;
|
|
}
|
|
}
|
|
while ( d_stream.avail_out == 0 && d_stream.avail_in != 0 &&
|
|
result != Z_STREAM_END );
|
|
|
|
free( tk.p_extra_data );
|
|
tk.i_extra_data = d_stream.total_out;
|
|
p_new_extra = static_cast<uint8_t *>( realloc(p_new_extra, tk.i_extra_data) );
|
|
if( !p_new_extra )
|
|
{
|
|
msg_Err( p_demux, "Couldn't allocate buffer to inflate data, ignore track %u",
|
|
tk.i_number );
|
|
inflateEnd( &d_stream );
|
|
tk.p_extra_data = NULL;
|
|
return 1;
|
|
}
|
|
|
|
tk.p_extra_data = p_new_extra;
|
|
|
|
inflateEnd( &d_stream );
|
|
return 0;
|
|
}
|
|
|
|
block_t *block_zlib_decompress( vlc_object_t *p_this, block_t *p_in_block ) {
|
|
int result, dstsize, n;
|
|
unsigned char *dst;
|
|
block_t *p_block;
|
|
z_stream d_stream;
|
|
|
|
d_stream.zalloc = NULL;
|
|
d_stream.zfree = NULL;
|
|
d_stream.opaque = NULL;
|
|
result = inflateInit(&d_stream);
|
|
if( result != Z_OK )
|
|
{
|
|
msg_Dbg( p_this, "inflateInit() failed. Result: %d", result );
|
|
return NULL;
|
|
}
|
|
|
|
d_stream.next_in = (Bytef *)p_in_block->p_buffer;
|
|
d_stream.avail_in = p_in_block->i_buffer;
|
|
n = 0;
|
|
p_block = block_Alloc( 0 );
|
|
dst = NULL;
|
|
do
|
|
{
|
|
n++;
|
|
p_block = block_Realloc( p_block, 0, n * 1000 );
|
|
dst = static_cast<unsigned char *>( p_block->p_buffer );
|
|
d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
|
|
d_stream.avail_out = 1000;
|
|
result = inflate(&d_stream, Z_NO_FLUSH);
|
|
if( ( result != Z_OK ) && ( result != Z_STREAM_END ) )
|
|
{
|
|
msg_Err( p_this, "Zlib decompression failed. Result: %d", result );
|
|
inflateEnd( &d_stream );
|
|
block_Release( p_block );
|
|
return p_in_block;
|
|
}
|
|
}
|
|
while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
|
|
( result != Z_STREAM_END ) );
|
|
|
|
dstsize = d_stream.total_out;
|
|
inflateEnd( &d_stream );
|
|
|
|
p_block = block_Realloc( p_block, 0, dstsize );
|
|
p_block->i_buffer = dstsize;
|
|
block_Release( p_in_block );
|
|
|
|
return p_block;
|
|
}
|
|
#endif
|
|
|
|
/* Utility function for BlockDecode */
|
|
block_t *MemToBlock( uint8_t *p_mem, size_t i_mem, size_t offset)
|
|
{
|
|
if( unlikely( i_mem > SIZE_MAX - offset ) )
|
|
return NULL;
|
|
|
|
block_t *p_block = block_Alloc( i_mem + offset );
|
|
if( likely(p_block != NULL) )
|
|
{
|
|
memcpy( p_block->p_buffer + offset, p_mem, i_mem );
|
|
}
|
|
return p_block;
|
|
}
|
|
|
|
|
|
void handle_real_audio(demux_t * p_demux, mkv_track_t * p_tk, block_t * p_blk, vlc_tick_t i_pts)
|
|
{
|
|
uint8_t * p_frame = p_blk->p_buffer;
|
|
Cook_PrivateTrackData * p_sys = (Cook_PrivateTrackData *) p_tk->p_sys;
|
|
size_t size = p_blk->i_buffer;
|
|
|
|
if( p_tk->i_last_dts == VLC_TICK_INVALID )
|
|
{
|
|
for( size_t i = 0; i < p_sys->i_subpackets; i++)
|
|
if( p_sys->p_subpackets[i] )
|
|
{
|
|
block_Release(p_sys->p_subpackets[i]);
|
|
p_sys->p_subpackets[i] = NULL;
|
|
}
|
|
p_sys->i_subpacket = 0;
|
|
p_sys->i_subpackets = 0;
|
|
|
|
if ( !( p_blk->i_flags & BLOCK_FLAG_TYPE_I) )
|
|
{
|
|
msg_Dbg( p_demux, "discard non-key preroll block in track %u at %" PRId64,
|
|
p_tk->i_number, i_pts );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( p_tk->fmt.i_codec == VLC_CODEC_COOK ||
|
|
p_tk->fmt.i_codec == VLC_CODEC_ATRAC3 )
|
|
{
|
|
const uint16_t i_num = p_sys->i_frame_size / p_sys->i_subpacket_size;
|
|
if ( i_num == 0 )
|
|
return;
|
|
const size_t y = p_sys->i_subpacket / i_num;
|
|
|
|
for( uint16_t i = 0; i < i_num; i++ )
|
|
{
|
|
size_t i_index = (size_t) p_sys->i_sub_packet_h * i +
|
|
((p_sys->i_sub_packet_h + 1) / 2) * (y&1) + (y>>1);
|
|
if( i_index >= p_sys->i_subpackets )
|
|
return;
|
|
|
|
block_t *p_block = block_Alloc( p_sys->i_subpacket_size );
|
|
if( !p_block )
|
|
return;
|
|
|
|
if( size < p_sys->i_subpacket_size )
|
|
return;
|
|
|
|
memcpy( p_block->p_buffer, p_frame, p_sys->i_subpacket_size );
|
|
p_block->i_dts = VLC_TICK_INVALID;
|
|
p_block->i_pts = VLC_TICK_INVALID;
|
|
if( !p_sys->i_subpacket )
|
|
{
|
|
p_tk->i_last_dts =
|
|
p_block->i_pts = i_pts;
|
|
}
|
|
|
|
p_frame += p_sys->i_subpacket_size;
|
|
size -= p_sys->i_subpacket_size;
|
|
|
|
p_sys->i_subpacket++;
|
|
p_sys->p_subpackets[i_index] = p_block;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*TODO*/
|
|
}
|
|
if( p_sys->i_subpacket == p_sys->i_subpackets )
|
|
{
|
|
for( size_t i = 0; i < p_sys->i_subpackets; i++)
|
|
{
|
|
send_Block( p_demux, p_tk, p_sys->p_subpackets[i], 1, 0 );
|
|
p_sys->p_subpackets[i] = NULL;
|
|
}
|
|
p_sys->i_subpacket = 0;
|
|
p_sys->i_subpackets = 0;
|
|
}
|
|
}
|
|
|
|
block_t *WEBVTT_Repack_Sample(block_t *p_block, bool b_webm,
|
|
const uint8_t *p_add, size_t i_add)
|
|
{
|
|
struct webvtt_cueelements_s els;
|
|
memset(&els, 0, sizeof(els));
|
|
size_t newsize = 0;
|
|
block_t *newblock = nullptr;
|
|
/* Repack to ISOBMFF samples format */
|
|
if( !b_webm ) /* S_TEXT/WEBVTT */
|
|
{
|
|
/* process addition fields */
|
|
if( i_add )
|
|
{
|
|
const uint8_t *end = p_add + i_add;
|
|
const uint8_t *iden =
|
|
reinterpret_cast<const uint8_t *>(std::memchr( p_add, '\n', i_add ));
|
|
if( iden && ++iden != end )
|
|
{
|
|
els.sttg.p_data = p_add;
|
|
els.sttg.i_data = &iden[-1] - p_add;
|
|
const uint8_t *comm =
|
|
reinterpret_cast<const uint8_t *>(std::memchr( iden, '\n', end - iden ));
|
|
els.iden.p_data = iden;
|
|
if( comm )
|
|
els.iden.i_data = comm - iden;
|
|
else
|
|
els.iden.i_data = end - iden;
|
|
}
|
|
}
|
|
/* the payload being in the block */
|
|
els.payl.p_data = p_block->p_buffer;
|
|
els.payl.i_data = p_block->i_buffer;
|
|
}
|
|
else /* deprecated D_WEBVTT/ */
|
|
{
|
|
const uint8_t *start = p_block->p_buffer;
|
|
const uint8_t *end = p_block->p_buffer + p_block->i_buffer;
|
|
const uint8_t *sttg =
|
|
reinterpret_cast<const uint8_t *>(std::memchr( start, '\n', p_block->i_buffer ));
|
|
if( !sttg || ++sttg == end )
|
|
goto error;
|
|
const uint8_t *payl =
|
|
reinterpret_cast<const uint8_t *>(std::memchr( sttg, '\n', end - sttg ));
|
|
if( !payl || ++payl == end )
|
|
goto error;
|
|
els.iden.p_data = start;
|
|
els.iden.i_data = &sttg[-1] - start;
|
|
els.sttg.p_data = sttg;
|
|
els.sttg.i_data = &payl[-1] - sttg;
|
|
els.payl.p_data = payl;
|
|
els.payl.i_data = end - payl;
|
|
}
|
|
|
|
newsize = WEBVTT_Pack_CueElementsGetNewSize( &els );
|
|
newblock = block_Alloc( newsize );
|
|
if( !newblock )
|
|
goto error;
|
|
WEBVTT_Pack_CueElements( &els, newblock->p_buffer );
|
|
block_CopyProperties( newblock, p_block );
|
|
block_Release( p_block );
|
|
return newblock;
|
|
|
|
error:
|
|
block_Release( p_block );
|
|
return NULL;
|
|
}
|
|
|
|
int UpdatePCR( demux_t * p_demux )
|
|
{
|
|
demux_sys_t *p_sys = (demux_sys_t *)p_demux->p_sys;
|
|
matroska_segment_c *p_segment = p_sys->p_current_vsegment->CurrentSegment();
|
|
|
|
int64_t i_pcr = VLC_TICK_INVALID;
|
|
|
|
typedef matroska_segment_c::tracks_map_t tracks_map_t;
|
|
|
|
for( tracks_map_t::const_iterator it = p_segment->tracks.begin(); it != p_segment->tracks.end(); ++it )
|
|
{
|
|
mkv_track_t &track = *it->second;
|
|
|
|
if( track.i_last_dts == VLC_TICK_INVALID )
|
|
continue;
|
|
|
|
if( track.fmt.i_cat != VIDEO_ES && track.fmt.i_cat != AUDIO_ES )
|
|
continue;
|
|
|
|
if( track.i_last_dts < i_pcr || i_pcr <= VLC_TICK_INVALID )
|
|
{
|
|
i_pcr = track.i_last_dts;
|
|
}
|
|
}
|
|
|
|
if( i_pcr > VLC_TICK_INVALID && i_pcr > p_sys->i_pcr )
|
|
{
|
|
if( es_out_SetPCR( p_demux->out, i_pcr ) )
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
p_sys->i_pcr = i_pcr;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
void send_Block( demux_t * p_demux, mkv_track_t * p_tk, block_t * p_block, unsigned int i_number_frames, vlc_tick_t i_duration )
|
|
{
|
|
demux_sys_t *p_sys = p_demux->p_sys;
|
|
matroska_segment_c *p_segment = p_sys->p_current_vsegment->CurrentSegment();
|
|
|
|
if( p_tk->fmt.i_cat == AUDIO_ES && p_tk->i_chans_to_reorder )
|
|
{
|
|
aout_ChannelReorder( p_block->p_buffer, p_block->i_buffer,
|
|
p_tk->i_chans_to_reorder,
|
|
p_tk->pi_chan_table, p_tk->fmt.i_codec );
|
|
}
|
|
|
|
if( p_block->i_dts > VLC_TICK_INVALID &&
|
|
( p_tk->fmt.i_cat == VIDEO_ES || p_tk->fmt.i_cat == AUDIO_ES ) )
|
|
{
|
|
p_tk->i_last_dts = p_block->i_dts;
|
|
}
|
|
|
|
if( !p_tk->b_no_duration )
|
|
{
|
|
p_block->i_length = i_duration * p_tk->f_timecodescale *
|
|
(double) p_segment->i_timescale / ( 1000.0 * i_number_frames );
|
|
}
|
|
|
|
if( p_tk->b_discontinuity )
|
|
{
|
|
p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
|
|
p_tk->b_discontinuity = false;
|
|
}
|
|
|
|
if ( p_sys->i_pcr == VLC_TICK_INVALID )
|
|
UpdatePCR( p_demux );
|
|
|
|
es_out_Send( p_demux->out, p_tk->p_es, p_block);
|
|
}
|
|
|
|
int32_t Cook_PrivateTrackData::Init()
|
|
{
|
|
i_subpackets = (size_t) i_sub_packet_h * (size_t) i_frame_size / (size_t) i_subpacket_size;
|
|
p_subpackets = static_cast<block_t**> ( calloc(i_subpackets, sizeof(block_t*)) );
|
|
|
|
if( unlikely( !p_subpackets ) )
|
|
{
|
|
i_subpackets = 0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Cook_PrivateTrackData::~Cook_PrivateTrackData()
|
|
{
|
|
for( size_t i = 0; i < i_subpackets; i++ )
|
|
if( p_subpackets[i] )
|
|
block_Release( p_subpackets[i] );
|
|
|
|
free( p_subpackets );
|
|
}
|
|
|
|
static inline void fill_wvpk_block(uint16_t version, uint32_t block_samples, uint32_t flags,
|
|
uint32_t crc, uint8_t * src, size_t srclen, uint8_t * dst)
|
|
{
|
|
const uint8_t wvpk_header[] = {'w','v','p','k', /* ckId */
|
|
0x0, 0x0, 0x0, 0x0, /* ckSize */
|
|
0x0, 0x0, /* version */
|
|
0x0, /* track_no */
|
|
0x0, /* index_no */
|
|
0xFF, 0xFF, 0xFF, 0xFF, /* total_samples */
|
|
0x0, 0x0, 0x0, 0x0 }; /* block_index */
|
|
memcpy( dst, wvpk_header, sizeof( wvpk_header ) );
|
|
SetDWLE( dst + 4, srclen + 24 );
|
|
SetWLE( dst + 8, version );
|
|
SetDWLE( dst + 20, block_samples );
|
|
SetDWLE( dst + 24, flags );
|
|
SetDWLE( dst + 28, crc );
|
|
memcpy( dst + 32, src, srclen );
|
|
}
|
|
|
|
block_t * packetize_wavpack( const mkv_track_t & tk, uint8_t * buffer, size_t size)
|
|
{
|
|
uint16_t version = 0x403;
|
|
uint32_t block_samples;
|
|
uint32_t flags;
|
|
uint32_t crc;
|
|
block_t * p_block = NULL;
|
|
|
|
if( tk.i_extra_data >= 2 )
|
|
version = GetWLE( tk.p_extra_data );
|
|
|
|
if( size < 12 )
|
|
return NULL;
|
|
|
|
block_samples = GetDWLE(buffer);
|
|
buffer += 4;
|
|
flags = GetDWLE(buffer);
|
|
size -= 4;
|
|
|
|
/* Check if WV_INITIAL_BLOCK and WV_FINAL_BLOCK are present */
|
|
if( ( flags & 0x1800 ) == 0x1800 )
|
|
{
|
|
crc = GetDWLE(buffer+4);
|
|
buffer += 8;
|
|
size -= 8;
|
|
|
|
p_block = block_Alloc( size + 32 );
|
|
if( !p_block )
|
|
return NULL;
|
|
|
|
fill_wvpk_block(version, block_samples, flags, crc, buffer, size, p_block->p_buffer);
|
|
}
|
|
else
|
|
{
|
|
/* Multiblock */
|
|
size_t total_size = 0;
|
|
|
|
p_block = block_Alloc( 0 );
|
|
if( !p_block )
|
|
return NULL;
|
|
|
|
while(size >= 12)
|
|
{
|
|
flags = GetDWLE(buffer);
|
|
buffer += 4;
|
|
crc = GetDWLE(buffer);
|
|
buffer += 4;
|
|
uint32_t bsz = GetDWLE(buffer);
|
|
buffer+= 4;
|
|
size -= 12;
|
|
|
|
bsz = (bsz < size)?bsz:size;
|
|
|
|
total_size += bsz + 32;
|
|
|
|
assert(total_size >= p_block->i_buffer);
|
|
|
|
p_block = block_Realloc( p_block, 0, total_size );
|
|
|
|
if( !p_block )
|
|
return NULL;
|
|
|
|
fill_wvpk_block(version, block_samples, flags, crc, buffer, bsz,
|
|
p_block->p_buffer + total_size - bsz - 32 );
|
|
buffer += bsz;
|
|
size -= bsz;
|
|
}
|
|
}
|
|
|
|
return p_block;
|
|
}
|
|
|
|
void MkvTree_va( demux_t& demuxer, int i_level, const char* fmt, va_list args)
|
|
{
|
|
static const char indent[] = "| ";
|
|
static const char prefix[] = "+ ";
|
|
static int const indent_len = sizeof( indent ) - 1;
|
|
static int const prefix_len = sizeof( prefix ) - 1;
|
|
|
|
char fixed_buffer[256] = {};
|
|
size_t const static_len = sizeof( fixed_buffer );
|
|
char * buffer = fixed_buffer;
|
|
size_t total_len = indent_len * i_level + prefix_len + strlen( fmt ) + 1;
|
|
|
|
if( total_len >= static_len ) {
|
|
buffer = new (std::nothrow) char[total_len] ();
|
|
|
|
if (buffer == NULL) {
|
|
msg_Err (&demuxer, "Unable to allocate memory for format string");
|
|
return;
|
|
}
|
|
}
|
|
|
|
char * dst = buffer;
|
|
|
|
for (int i = 0; i < i_level; ++i, dst += indent_len)
|
|
memcpy( dst, indent, indent_len );
|
|
|
|
strcat( dst, prefix );
|
|
strcat( dst, fmt );
|
|
|
|
msg_GenericVa( &demuxer, VLC_MSG_DBG, buffer, args );
|
|
|
|
if (buffer != fixed_buffer)
|
|
delete [] buffer;
|
|
}
|
|
|
|
void MkvTree( demux_t & demuxer, int i_level, const char *psz_format, ... )
|
|
{
|
|
va_list args; va_start( args, psz_format );
|
|
MkvTree_va( demuxer, i_level, psz_format, args );
|
|
va_end( args );
|
|
}
|
|
|