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

/*****************************************************************************
* 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 );
}