Commit b5ad5b61 authored by Laurent Aimar's avatar Laurent Aimar

Modified the way xiph codecs headers are transported in VLC.

It makes VLC compatible with FFmpeg vorbis/theora decoder.
It fixes support of big headers (like comments with an art encoded
as a base64 string...).
It simplifies a bit.
parent d61a6d40
......@@ -36,6 +36,7 @@
#include <vlc_plugin.h>
#include <vlc_codecs.h>
#include "../../demux/xiph.h"
#include "rtp.h"
......@@ -112,28 +113,20 @@ static ssize_t vorbis_header (void **pextra, const uint8_t *buf, size_t len)
setuplen = len - (idlen + cmtlen);
/* Create the VLC extra format header */
uint8_t *extra = malloc ((size_t)6 + idlen + cmtlen + setuplen);
if (extra == NULL)
return -1;
uint8_t *ptr = *pextra = extra;
/* Identification header */
*ptr++ = idlen >> 8;
*ptr++ = idlen & 0xff;
memcpy (ptr, buf, idlen);
buf += idlen;
ptr += idlen;
/* Comments header */
*ptr++ = cmtlen >> 8;
*ptr++ = cmtlen & 0xff;
memcpy (ptr, buf, cmtlen);
buf += cmtlen;
ptr += cmtlen;
/* Setup header */
*ptr++ = setuplen >> 8;
*ptr++ = setuplen & 0xff;
memcpy (ptr, buf, setuplen);
ptr += setuplen;
return ptr - extra;
unsigned sizes[3] = {
idlen, cmtlen, setuplen
};
void *payloads[3] = {
buf + 0,
buf + idlen,
buf + cmtlen
};
void *extra;
int extra_size;
if (xiph_PackHeaders (&extra_size, &extra, sizes, payloads, 3))
return -1;;
*pextra = extra;
return extra_size;
}
......
......@@ -33,6 +33,7 @@
#include <vlc_input.h>
#include <vlc_codec.h>
#include <vlc_osd.h>
#include "../demux/xiph.h"
#include <kate/kate.h>
#ifdef HAVE_TIGER
......@@ -73,8 +74,7 @@ struct decoder_sys_t
/*
* Input properties
*/
int i_num_headers;
int i_headers;
bool b_has_headers;
/*
* Kate properties
......@@ -377,8 +377,7 @@ static int OpenDecoder( vlc_object_t *p_this )
kate_comment_init( &p_sys->kc );
kate_info_init( &p_sys->ki );
p_sys->i_num_headers = 0;
p_sys->i_headers = 0;
p_sys->b_has_headers = false;
/* retrieve options */
p_sys->b_formatted = var_CreateGetBool( p_dec, "kate-formatted" );
......@@ -492,42 +491,14 @@ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
/* Block to Kate packet */
kate_packet_wrap(&kp, p_block->i_buffer, p_block->p_buffer);
if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
if( !p_sys->b_has_headers )
{
/* Headers already available as extra data */
p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0];
p_sys->i_headers = p_sys->i_num_headers;
}
else if( kp.nbytes && (p_sys->i_headers==0 || p_sys->i_headers < p_sys->ki.num_headers ))
{
/* Backup headers as extra data */
uint8_t *p_extra;
p_dec->fmt_in.p_extra = xrealloc( p_dec->fmt_in.p_extra,
p_dec->fmt_in.i_extra + kp.nbytes + 2 );
p_extra = (void*)(((unsigned char*)p_dec->fmt_in.p_extra) + p_dec->fmt_in.i_extra);
*(p_extra++) = kp.nbytes >> 8;
*(p_extra++) = kp.nbytes & 0xFF;
memcpy( p_extra, kp.data, kp.nbytes );
p_dec->fmt_in.i_extra += kp.nbytes + 2;
block_Release( *pp_block );
p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0];
p_sys->i_headers++;
return NULL;
}
if( p_sys->i_headers == p_sys->i_num_headers && p_sys->i_num_headers>0 )
{
if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
if( ProcessHeaders( p_dec ) )
{
p_sys->i_headers = 0;
p_dec->fmt_in.i_extra = 0;
block_Release( *pp_block );
return NULL;
}
else p_sys->i_headers++;
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &kp, pp_block );
......@@ -540,37 +511,28 @@ static int ProcessHeaders( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
kate_packet kp;
uint8_t *p_extra;
int i_extra;
int i_headeridx;
int i_ret;
if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
p_extra = p_dec->fmt_in.p_extra;
i_extra = p_dec->fmt_in.i_extra;
/* skip number of headers */
++p_extra;
--i_extra;
/* Take care of the initial Kate header */
kp.nbytes = *(p_extra++) << 8;
kp.nbytes |= (*(p_extra++) & 0xFF);
kp.data = p_extra;
p_extra += kp.nbytes;
i_extra -= (kp.nbytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
int i_ret = VLC_SUCCESS;
if( i_count < 1 )
{
i_ret = VLC_EGENERIC;
goto end;
}
/* Take care of the initial Kate header */
kp.nbytes = pi_size[0];
kp.data = pp_data[0];
i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( i_ret < 0 )
{
msg_Err( p_dec, "this bitstream does not contain Kate data (%d)", i_ret );
return VLC_EGENERIC;
goto end;
}
msg_Dbg( p_dec, "%s %s text, granule rate %f, granule shift %d",
......@@ -579,24 +541,15 @@ static int ProcessHeaders( decoder_t *p_dec )
p_sys->ki.granule_shift);
/* parse all remaining header packets */
for( i_headeridx = 1; i_headeridx < p_sys->ki.num_headers; ++i_headeridx )
for( unsigned i_headeridx = 1; i_headeridx < i_count; i_headeridx++ )
{
kp.nbytes = *(p_extra++) << 8;
kp.nbytes |= (*(p_extra++) & 0xFF);
kp.data = p_extra;
p_extra += kp.nbytes;
i_extra -= (kp.nbytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header %d data corrupted", i_headeridx );
return VLC_EGENERIC;
}
kp.nbytes = pi_size[i_headeridx];
kp.data = pp_data[i_headeridx];
i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( i_ret < 0 )
{
msg_Err( p_dec, "Kate header %d is corrupted: %d", i_headeridx, i_ret );
return VLC_EGENERIC;
goto end;
}
/* header 1 is comments */
......@@ -631,7 +584,10 @@ static int ProcessHeaders( decoder_t *p_dec )
}
#endif
return VLC_SUCCESS;
end:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return i_ret < 0 ? VLC_EGENERIC : VLC_SUCCESS;
}
/*****************************************************************************
......@@ -668,12 +624,10 @@ static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
else
#endif
{
if( p_sys->i_headers >= p_sys->i_num_headers && p_sys->i_num_headers > 0)
p_buf = DecodePacket( p_dec, p_kp, p_block );
else
p_buf = NULL;
p_buf = DecodePacket( p_dec, p_kp, p_block );
if( p_block ) block_Release( p_block );
if( p_block )
block_Release( p_block );
}
return p_buf;
......
......@@ -33,6 +33,7 @@
#include <vlc_input.h>
#include <vlc_codec.h>
#include <vlc_aout.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
#include <speex/speex.h>
......@@ -150,7 +151,7 @@ struct decoder_sys_t
/*
* Input properties
*/
int i_headers;
bool b_has_headers;
int i_frame_in_packet;
/*
......@@ -214,6 +215,7 @@ static int OpenDecoder( vlc_object_t *p_this )
p_dec->p_sys->bits.buf_size = 0;
p_dec->p_sys->b_packetizer = false;
p_dec->p_sys->rtp_rate = p_dec->fmt_in.audio.i_rate;
p_dec->p_sys->b_has_headers = false;
date_Set( &p_sys->end_date, 0 );
......@@ -242,7 +244,6 @@ static int OpenDecoder( vlc_object_t *p_this )
p_dec->pf_packetize = (block_t *(*)(decoder_t *, block_t **))
DecodeBlock;
p_sys->i_headers = 0;
p_sys->p_state = NULL;
p_sys->p_header = NULL;
p_sys->i_frame_in_packet = 0;
......@@ -298,38 +299,14 @@ static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
oggpacket.packetno = 0;
/* Check for headers */
if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
if( !p_sys->b_has_headers )
{
p_sys->i_headers = 2;
}
else if( oggpacket.bytes && p_sys->i_headers < 2 )
{
uint8_t *p_extra;
p_dec->fmt_in.p_extra = xrealloc( p_dec->fmt_in.p_extra,
p_dec->fmt_in.i_extra + oggpacket.bytes + 2 );
p_extra = ((uint8_t *)p_dec->fmt_in.p_extra) + p_dec->fmt_in.i_extra;
*(p_extra++) = oggpacket.bytes >> 8;
*(p_extra++) = oggpacket.bytes & 0xFF;
memcpy( p_extra, oggpacket.packet, oggpacket.bytes );
p_dec->fmt_in.i_extra += oggpacket.bytes + 2;
block_Release( *pp_block );
p_sys->i_headers++;
return NULL;
}
if( p_sys->i_headers == 2 )
{
if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
if( ProcessHeaders( p_dec ) )
{
p_sys->i_headers = 0;
p_dec->fmt_in.i_extra = 0;
block_Release( *pp_block );
return NULL;
}
else p_sys->i_headers++;
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &oggpacket, pp_block );
......@@ -342,50 +319,34 @@ static int ProcessHeaders( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
uint8_t *p_extra;
int i_extra;
if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
if( i_count < 2 )
goto error;
oggpacket.granulepos = -1;
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
p_extra = p_dec->fmt_in.p_extra;
i_extra = p_dec->fmt_in.i_extra;
/* Take care of the initial Vorbis header */
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
/* Take care of the initial Speex header */
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.bytes = pi_size[0];
oggpacket.packet = pp_data[0];
if( ProcessInitialHeader( p_dec, &oggpacket ) != VLC_SUCCESS )
{
msg_Err( p_dec, "initial Speex header is corrupted" );
return VLC_EGENERIC;
goto error;
}
/* The next packet in order is the comments header */
oggpacket.b_o_s = 0;
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
oggpacket.bytes = pi_size[1];
oggpacket.packet = pp_data[1];
ParseSpeexComments( p_dec, &oggpacket );
if( p_sys->b_packetizer )
......@@ -397,7 +358,14 @@ static int ProcessHeaders( decoder_t *p_dec )
p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
}
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_SUCCESS;
error:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_EGENERIC;
}
/*****************************************************************************
......@@ -591,14 +559,10 @@ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
}
else
{
aout_buffer_t *p_aout_buffer;
if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
p_aout_buffer = DecodePacket( p_dec, p_oggpacket );
else
p_aout_buffer = NULL; /* Skip headers */
aout_buffer_t *p_aout_buffer = DecodePacket( p_dec, p_oggpacket );
if( p_block ) block_Release( p_block );
if( p_block )
block_Release( p_block );
return p_aout_buffer;
}
}
......@@ -796,15 +760,10 @@ static block_t *SendPacket( decoder_t *p_dec, block_t *p_block )
/* Date management */
p_block->i_dts = p_block->i_pts = date_Get( &p_sys->end_date );
if( p_sys->i_headers >= p_sys->p_header->extra_headers + 2 )
{
p_block->i_length =
date_Increment( &p_sys->end_date,
p_sys->p_header->frame_size ) -
p_block->i_pts;
}
else
p_block->i_length = 0;
p_block->i_length =
date_Increment( &p_sys->end_date,
p_sys->p_header->frame_size ) -
p_block->i_pts;
return p_block;
}
......
......@@ -33,6 +33,8 @@
#include <vlc_codec.h>
#include <vlc_sout.h>
#include <vlc_input.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
#include <theora/theora.h>
......@@ -48,7 +50,7 @@ struct decoder_sys_t
/*
* Input properties
*/
int i_headers;
bool b_has_headers;
/*
* Theora properties
......@@ -143,7 +145,7 @@ static int OpenDecoder( vlc_object_t *p_this )
if( ( p_dec->p_sys = p_sys = malloc(sizeof(*p_sys)) ) == NULL )
return VLC_ENOMEM;
p_dec->p_sys->b_packetizer = false;
p_sys->b_has_headers = false;
p_sys->i_pts = VLC_TS_INVALID;
p_sys->b_decoded_first_keyframe = false;
......@@ -161,8 +163,6 @@ static int OpenDecoder( vlc_object_t *p_this )
theora_comment_init( &p_sys->tc );
theora_info_init( &p_sys->ti );
p_sys->i_headers = 0;
return VLC_SUCCESS;
}
......@@ -205,40 +205,14 @@ static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
oggpacket.packetno = 0;
/* Check for headers */
if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
{
/* Headers already available as extra data */
p_sys->i_headers = 3;
}
else if( oggpacket.bytes && p_sys->i_headers < 3 )
if( !p_sys->b_has_headers )
{
/* Backup headers as extra data */
uint8_t *p_extra;
p_dec->fmt_in.p_extra = xrealloc( p_dec->fmt_in.p_extra,
p_dec->fmt_in.i_extra + oggpacket.bytes + 2 );
p_extra = ((uint8_t *)p_dec->fmt_in.p_extra) + p_dec->fmt_in.i_extra;
*(p_extra++) = oggpacket.bytes >> 8;
*(p_extra++) = oggpacket.bytes & 0xFF;
memcpy( p_extra, oggpacket.packet, oggpacket.bytes );
p_dec->fmt_in.i_extra += oggpacket.bytes + 2;
block_Release( *pp_block );
p_sys->i_headers++;
return NULL;
}
if( p_sys->i_headers == 3 )
{
if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
if( ProcessHeaders( p_dec ) )
{
p_sys->i_headers = 0;
p_dec->fmt_in.i_extra = 0;
block_Release( *pp_block );
return NULL;
}
else p_sys->i_headers++;
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &oggpacket, pp_block );
......@@ -251,34 +225,28 @@ static int ProcessHeaders( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
uint8_t *p_extra;
int i_extra;
if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
if( i_count < 3 )
goto error;
oggpacket.granulepos = -1;
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
p_extra = p_dec->fmt_in.p_extra;
i_extra = p_dec->fmt_in.i_extra;
/* Take care of the initial Vorbis header */
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.bytes = pi_size[0];
oggpacket.packet = pp_data[0];
if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "this bitstream does not contain Theora video data" );
return VLC_EGENERIC;
goto error;
}
/* Set output properties */
......@@ -351,23 +319,13 @@ static int ProcessHeaders( decoder_t *p_dec )
}
/* The next packet in order is the comments header */
oggpacket.b_o_s = 0;
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
/* The next packet in order is the comments header */
oggpacket.b_o_s = 0;
oggpacket.bytes = pi_size[1];
oggpacket.packet = pp_data[1];
if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "2nd Theora header is corrupted" );
return VLC_EGENERIC;
goto error;
}
ParseTheoraComments( p_dec );
......@@ -375,23 +333,13 @@ static int ProcessHeaders( decoder_t *p_dec )
/* The next packet in order is the codebooks header
* We need to watch out that this packet is not missing as a
* missing or corrupted header is fatal. */
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
/* The next packet in order is the codebooks header
* We need to watch out that this packet is not missing as a
* missing or corrupted header is fatal */
oggpacket.b_o_s = 0;
oggpacket.bytes = pi_size[2];
oggpacket.packet = pp_data[2];
if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "3rd Theora header is corrupted" );
return VLC_EGENERIC;
goto error;
}
if( !p_sys->b_packetizer )
......@@ -408,7 +356,14 @@ static int ProcessHeaders( decoder_t *p_dec )
p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
}
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_SUCCESS;
error:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_EGENERIC;
}
/*****************************************************************************
......@@ -442,21 +397,15 @@ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
/* Date management */
p_block->i_dts = p_block->i_pts = p_sys->i_pts;
if( p_sys->i_headers >= 3 )
p_block->i_length = p_sys->i_pts - p_block->i_pts;
else
p_block->i_length = 0;
p_block->i_length = p_sys->i_pts - p_block->i_pts;
p_buf = p_block;
}
else
{
if( p_sys->i_headers >= 3 )
p_buf = DecodePacket( p_dec, p_oggpacket );
else
p_buf = NULL;
if( p_block ) block_Release( p_block );
p_buf = DecodePacket( p_dec, p_oggpacket );
if( p_block )
block_Release( p_block );
}
/* Date management */
......
......@@ -37,6 +37,7 @@
#include <vlc_aout.h>
#include <vlc_input.h>
#include <vlc_sout.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
......@@ -60,10 +61,7 @@ struct decoder_sys_t
/* Module mode */
bool b_packetizer;
/*
* Input properties
*/
int i_headers;
bool b_has_headers;
/*
* Vorbis properties
......@@ -236,7 +234,7 @@ static int OpenDecoder( vlc_object_t *p_this )
date_Set( &p_sys->end_date, 0 );
p_sys->i_last_block_size = 0;
p_sys->b_packetizer = false;
p_sys->i_headers = 0;
p_sys->b_has_headers = false;
/* Take care of vorbis init */
vorbis_info_init( &p_sys->vi );
......@@ -307,41 +305,14 @@ static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
oggpacket.packetno = 0;
/* Check for headers */
if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
{
/* Headers already available as extra data */
msg_Dbg( p_dec, "headers already available as extra data" );
p_sys->i_headers = 3;
}
else if( oggpacket.bytes && p_sys->i_headers < 3 )
{
/* Backup headers as extra data */
uint8_t *p_extra;
p_dec->fmt_in.p_extra = xrealloc( p_dec->fmt_in.p_extra,
p_dec->fmt_in.i_extra + oggpacket.bytes + 2 );
p_extra = (uint8_t *)p_dec->fmt_in.p_extra + p_dec->fmt_in.i_extra;
*(p_extra++) = oggpacket.bytes >> 8;
*(p_extra++) = oggpacket.bytes & 0xFF;
memcpy( p_extra, oggpacket.packet, oggpacket.bytes );
p_dec->fmt_in.i_extra += oggpacket.bytes + 2;
block_Release( *pp_block );
p_sys->i_headers++;
return NULL;
}
if( p_sys->i_headers == 3 )
if( !p_sys->b_has_headers )
{
if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
if( ProcessHeaders( p_dec ) )
{
p_sys->i_headers = 0;
p_dec->fmt_in.i_extra = 0;
block_Release( *pp_block );
return NULL;
}
else p_sys->i_headers++;
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &oggpacket, pp_block );
......@@ -354,34 +325,28 @@ static int ProcessHeaders( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
uint8_t *p_extra;
int i_extra;
if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
if( i_count < 3 )
goto error;
oggpacket.granulepos = -1;
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
p_extra = p_dec->fmt_in.p_extra;
i_extra = p_dec->fmt_in.i_extra;
/* Take care of the initial Vorbis header */
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.bytes = pi_size[0];
oggpacket.packet = pp_data[0];
if( vorbis_synthesis_headerin( &p_sys->vi, &p_sys->vc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "this bitstream does not contain Vorbis audio data");
return VLC_EGENERIC;
goto error;
}
/* Setup the format */
......@@ -392,7 +357,7 @@ static int ProcessHeaders( decoder_t *p_dec )
{
msg_Err( p_dec, "invalid number of channels (not between 1 and 9): %i",
p_dec->fmt_out.audio.i_channels );
return VLC_EGENERIC;
goto error;
}
p_dec->fmt_out.audio.i_physical_channels =
......@@ -406,38 +371,22 @@ static int ProcessHeaders( decoder_t *p_dec )
p_sys->vi.channels, p_sys->vi.rate, p_sys->vi.bitrate_nominal );
/* The next packet in order is the comments header */
oggpacket.b_o_s = 0;
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
p_extra += oggpacket.bytes;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
oggpacket.b_o_s = 0;
oggpacket.bytes = pi_size[1];
oggpacket.packet = pp_data[1];
if( vorbis_synthesis_headerin( &p_sys->vi, &p_sys->vc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "2nd Vorbis header is corrupted" );
return VLC_EGENERIC;
goto error;
}
ParseVorbisComments( p_dec );
/* The next packet in order is the codebooks header
* We need to watch out that this packet is not missing as a
* missing or corrupted header is fatal. */
oggpacket.bytes = *(p_extra++) << 8;
oggpacket.bytes |= (*(p_extra++) & 0xFF);
oggpacket.packet = p_extra;
i_extra -= (oggpacket.bytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header data corrupted");
return VLC_EGENERIC;
}
oggpacket.b_o_s = 0;
oggpacket.bytes = pi_size[2];
oggpacket.packet = pp_data[2];
if( vorbis_synthesis_headerin( &p_sys->vi, &p_sys->vc, &oggpacket ) < 0 )
{
msg_Err( p_dec, "3rd Vorbis header is corrupted" );
......@@ -454,7 +403,7 @@ static int ProcessHeaders( decoder_t *p_dec )
{
p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra;
p_dec->fmt_out.p_extra = xrealloc( p_dec->fmt_out.p_extra,
p_dec->fmt_out.i_extra );
p_dec->fmt_out.i_extra );
memcpy( p_dec->fmt_out.p_extra,
p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
}
......@@ -462,7 +411,14 @@ static int ProcessHeaders( decoder_t *p_dec )
ConfigureChannelOrder(p_sys->pi_chan_table, p_sys->vi.channels,
p_dec->fmt_out.audio.i_physical_channels, true);
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_SUCCESS;
error:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return VLC_EGENERIC;
}
/*****************************************************************************
......@@ -496,14 +452,9 @@ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
}
else
{
aout_buffer_t *p_aout_buffer;
if( p_sys->i_headers >= 3 )
p_aout_buffer = DecodePacket( p_dec, p_oggpacket );
else
p_aout_buffer = NULL;
if( p_block ) block_Release( p_block );
aout_buffer_t *p_aout_buffer = DecodePacket( p_dec, p_oggpacket );
if( p_block )
block_Release( p_block );
return p_aout_buffer;
}
}
......@@ -586,10 +537,7 @@ static block_t *SendPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
/* Date management */
p_block->i_dts = p_block->i_pts = date_Get( &p_sys->end_date );
if( p_sys->i_headers >= 3 )
p_block->i_length = date_Increment( &p_sys->end_date, i_samples ) - p_block->i_pts;
else
p_block->i_length = 0;
p_block->i_length = date_Increment( &p_sys->end_date, i_samples ) - p_block->i_pts;
return p_block;
}
......@@ -730,7 +678,7 @@ static void CloseDecoder( vlc_object_t *p_this )
decoder_t *p_dec = (decoder_t *)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
if( !p_sys->b_packetizer && p_sys->i_headers > 3 )
if( !p_sys->b_packetizer && p_sys->b_has_headers )
{
vorbis_block_clear( &p_sys->vb );
vorbis_dsp_clear( &p_sys->vd );
......
......@@ -431,73 +431,6 @@ static int Open( vlc_object_t * p_this )
fmt.p_extra = malloc( fmt.i_extra );
if( !fmt.p_extra ) goto error;
memcpy( fmt.p_extra, &p_auds->p_wf[1], fmt.i_extra );
/* Rewrite the vorbis headers from Xiph-like format
* to VLC internal format
*
* Xiph format:
* - 1st byte == N, is the number of packets - 1
* - Following bytes are the size of the N first packets:
* while( *p == 0xFF ) { size += 0xFF; p++ } size += *p;
* (the size of the last packet is the size of remaining
* data in the buffer)
* - Finally, all the packets concatenated
*
* VLC format:
* - Size of the packet on 16 bits (big endian) FIXME: should be 32 bits to be safe
* - The packet itself
* - Size of the next packet, and so on ...
*/
if( tk->i_codec == VLC_CODEC_VORBIS )
{
uint8_t *p_extra = fmt.p_extra;
size_t i_extra = fmt.i_extra;
if( i_extra <= 1 ) break;
if( *p_extra++ != 2 ) break; /* 3 packets - 1 = 2 */
i_extra--;
size_t i_identifier_len = 0;
while( *p_extra == 0xFF )
{
i_identifier_len += 0xFF;
p_extra++;
if( --i_extra <= 1 ) break;
}
i_identifier_len += *p_extra++;
if( i_identifier_len > --i_extra ) break;
size_t i_comment_len = 0;
while( *p_extra == 0xFF )
{
i_comment_len += 0xFF;
p_extra++;
if( --i_extra <= 1 ) break;
}
i_comment_len += *p_extra++;
if( i_comment_len > --i_extra ) break;
size_t i_cookbook_len = i_extra;
size_t i_headers_size = 3 * 2 + i_identifier_len +
i_comment_len + i_cookbook_len;
uint8_t *p_out = malloc( i_headers_size );
if( !p_out ) goto error;
free( fmt.p_extra );
fmt.p_extra = p_out;
fmt.i_extra = i_headers_size;
#define copy_packet( len ) \
*p_out++ = len >> 8; \
*p_out++ = len & 0xFF; \
memcpy( p_out, p_extra, len ); \
p_out += len; \
p_extra += len;
copy_packet( i_identifier_len );
copy_packet( i_comment_len );
copy_packet( i_cookbook_len );
#undef copy_packet
break;
}
break;
case( AVIFOURCC_vids ):
......
......@@ -679,73 +679,10 @@ bool matroska_segment_c::Select( mtime_t i_start_time )
}
else if( !strncmp( tracks[i_track]->psz_codec, "V_THEORA", 8 ) )
{
uint8_t *p_data = tracks[i_track]->p_extra_data;
tracks[i_track]->fmt.i_codec = VLC_CODEC_THEORA;
if( tracks[i_track]->i_extra_data >= 4 ) {
if( p_data[0] == 2 ) {
int i = 1;
int i_size1 = 0, i_size2 = 0;
p_data++;
/* read size of first header packet */
while( *p_data == 0xFF &&
i < tracks[i_track]->i_extra_data )
{
i_size1 += *p_data;
p_data++;
i++;
}
i_size1 += *p_data;
p_data++;
i++;
msg_Dbg( &sys.demuxer, "first theora header size %d", i_size1 );
/* read size of second header packet */
while( *p_data == 0xFF &&
i < tracks[i_track]->i_extra_data )
{
i_size2 += *p_data;
p_data++;
i++;
}
i_size2 += *p_data;
p_data++;
i++;
int i_size3 = tracks[i_track]->i_extra_data - i - i_size1
- i_size2;
msg_Dbg( &sys.demuxer, "second theora header size %d", i_size2 );
msg_Dbg( &sys.demuxer, "third theora header size %d", i_size3 );
tracks[i_track]->fmt.i_extra = i_size1 + i_size2 + i_size3
+ 6;
if( i_size1 > 0 && i_size2 > 0 && i_size3 > 0 ) {
tracks[i_track]->fmt.p_extra =
xmalloc( tracks[i_track]->fmt.i_extra );
uint8_t *p_out = (uint8_t*)tracks[i_track]->fmt.p_extra;
*p_out++ = (i_size1>>8) & 0xFF;
*p_out++ = i_size1 & 0xFF;
memcpy( p_out, p_data, i_size1 );
p_data += i_size1;
p_out += i_size1;
*p_out++ = (i_size2>>8) & 0xFF;
*p_out++ = i_size2 & 0xFF;
memcpy( p_out, p_data, i_size2 );
p_data += i_size2;
p_out += i_size2;
*p_out++ = (i_size3>>8) & 0xFF;
*p_out++ = i_size3 & 0xFF;
memcpy( p_out, p_data, i_size3 );
p_data += i_size3;
p_out += i_size3;
}
else
{
msg_Err( &sys.demuxer, "inconsistent theora extradata" );
}
}
else {
msg_Err( &sys.demuxer, "Wrong number of ogg packets with theora headers (%d)", p_data[0] + 1 );
}
}
tracks[i_track]->fmt.i_extra = tracks[i_track]->i_extra_data;
tracks[i_track]->fmt.p_extra = xmalloc( tracks[i_track]->i_extra_data );
memcpy( tracks[i_track]->fmt.p_extra,tracks[i_track]->p_extra_data, tracks[i_track]->i_extra_data );
}
else if( !strncmp( tracks[i_track]->psz_codec, "V_REAL/RV", 9 ) )
{
......@@ -879,41 +816,10 @@ bool matroska_segment_c::Select( mtime_t i_start_time )
}
else if( !strcmp( tracks[i_track]->psz_codec, "A_VORBIS" ) )
{
int i, i_offset = 1, i_size[3], i_extra;
uint8_t *p_extra;
tracks[i_track]->fmt.i_codec = VLC_CODEC_VORBIS;
/* Split the 3 headers */
if( tracks[i_track]->p_extra_data[0] != 0x02 )
msg_Err( &sys.demuxer, "invalid vorbis header" );
for( i = 0; i < 2; i++ )
{
i_size[i] = 0;
while( i_offset < tracks[i_track]->i_extra_data )
{
i_size[i] += tracks[i_track]->p_extra_data[i_offset];
if( tracks[i_track]->p_extra_data[i_offset++] != 0xff ) break;
}
}
i_size[0] = __MIN(i_size[0], tracks[i_track]->i_extra_data - i_offset);
i_size[1] = __MIN(i_size[1], tracks[i_track]->i_extra_data -i_offset -i_size[0]);
i_size[2] = tracks[i_track]->i_extra_data - i_offset - i_size[0] - i_size[1];
tracks[i_track]->fmt.i_extra = 3 * 2 + i_size[0] + i_size[1] + i_size[2];
tracks[i_track]->fmt.p_extra = xmalloc( tracks[i_track]->fmt.i_extra );
p_extra = (uint8_t *)tracks[i_track]->fmt.p_extra; i_extra = 0;
for( i = 0; i < 3; i++ )
{
*(p_extra++) = i_size[i] >> 8;
*(p_extra++) = i_size[i] & 0xFF;
memcpy( p_extra, tracks[i_track]->p_extra_data + i_offset + i_extra,
i_size[i] );
p_extra += i_size[i];
i_extra += i_size[i];
}
tracks[i_track]->fmt.i_extra = tracks[i_track]->i_extra_data;
tracks[i_track]->fmt.p_extra = xmalloc( tracks[i_track]->i_extra_data );
memcpy( tracks[i_track]->fmt.p_extra,tracks[i_track]->p_extra_data, tracks[i_track]->i_extra_data );
}
else if( !strncmp( tracks[i_track]->psz_codec, "A_AAC/MPEG2/", strlen( "A_AAC/MPEG2/" ) ) ||
!strncmp( tracks[i_track]->psz_codec, "A_AAC/MPEG4/", strlen( "A_AAC/MPEG4/" ) ) )
......@@ -1035,47 +941,9 @@ bool matroska_segment_c::Select( mtime_t i_start_time )
tracks[i_track]->fmt.i_codec = VLC_CODEC_KATE;
tracks[i_track]->fmt.subs.psz_encoding = strdup( "UTF-8" );
/* Recover the number of headers to expect */
num_headers = tracks[i_track]->p_extra_data[0]+1;
msg_Dbg( &sys.demuxer, "kate in mkv detected: %d headers in %u bytes",
num_headers, tracks[i_track]->i_extra_data);
/* this won't overflow the stack as is can allocate only 1020 bytes max */
uint16_t pi_size[num_headers];
/* Split the headers */
size_so_far = 0;
for( i = 0; i < num_headers-1; i++ )
{
pi_size[i] = 0;
while( i_offset < tracks[i_track]->i_extra_data )
{
pi_size[i] += tracks[i_track]->p_extra_data[i_offset];
if( tracks[i_track]->p_extra_data[i_offset++] != 0xff ) break;
}
msg_Dbg( &sys.demuxer, "kate header %d is %d bytes", i, pi_size[i]);
size_so_far += pi_size[i];
}
pi_size[num_headers-1] = tracks[i_track]->i_extra_data - (size_so_far+i_offset);
msg_Dbg( &sys.demuxer, "kate last header (%d) is %d bytes", num_headers-1, pi_size[num_headers-1]);
tracks[i_track]->fmt.i_extra = 1 + num_headers * 2 + size_so_far + pi_size[num_headers-1];
tracks[i_track]->fmt.p_extra = xmalloc( tracks[i_track]->fmt.i_extra );
p_extra = (uint8_t *)tracks[i_track]->fmt.p_extra;
i_extra = 0;
*(p_extra++) = num_headers;
++i_extra;
for( i = 0; i < num_headers; i++ )
{
*(p_extra++) = pi_size[i] >> 8;
*(p_extra++) = pi_size[i] & 0xFF;
memcpy( p_extra, tracks[i_track]->p_extra_data + i_offset + i_extra-1,
pi_size[i] );
p_extra += pi_size[i];
i_extra += pi_size[i];
}
tracks[i_track]->fmt.i_extra = tracks[i_track]->i_extra_data;
tracks[i_track]->fmt.p_extra = xmalloc( tracks[i_track]->i_extra_data );
memcpy( tracks[i_track]->fmt.p_extra,tracks[i_track]->p_extra_data, tracks[i_track]->i_extra_data );
}
else if( !strcmp( tracks[i_track]->psz_codec, "S_TEXT/ASCII" ) )
{
......
......@@ -39,6 +39,7 @@
#include <vlc_codecs.h>
#include <vlc_bits.h>
#include "xiph.h"
#include "vorbis.h"
#include "kate_categories.h"
......@@ -78,7 +79,7 @@ typedef struct logical_stream_s
* them to the decoder. */
int b_force_backup;
int i_packets_backup;
uint8_t *p_headers;
void *p_headers;
int i_headers;
/* program clock reference (in units of 90kHz) derived from the previous
......@@ -624,10 +625,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
if( p_stream->b_force_backup )
{
uint8_t *p_sav;
bool b_store_size = true;
bool b_store_num_headers = false;
bool b_xiph;
p_stream->i_packets_backup++;
switch( p_stream->fmt.i_codec )
{
......@@ -635,6 +633,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
case VLC_CODEC_SPEEX:
case VLC_CODEC_THEORA:
if( p_stream->i_packets_backup == 3 ) p_stream->b_force_backup = 0;
b_xiph = true;
break;
case VLC_CODEC_FLAC:
......@@ -652,44 +651,45 @@ static void Ogg_DecodePacket( demux_t *p_demux,
p_oggpacket->bytes -= 9;
}
}
b_store_size = false;
b_xiph = false;
break;
case VLC_CODEC_KATE:
if( p_stream->i_packets_backup == 1)
b_store_num_headers = true;
if( p_stream->i_packets_backup == p_stream->i_kate_num_headers ) p_stream->b_force_backup = 0;
b_xiph = true;
break;
default:
p_stream->b_force_backup = 0;
b_xiph = false;
break;
}
/* Backup the ogg packet (likely an header packet) */
p_stream->p_headers =
realloc( p_sav = p_stream->p_headers, p_stream->i_headers +
p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0) );
if( !p_stream->p_headers )
p_stream->p_headers = p_sav;
else
if( !b_xiph )
{
uint8_t *p_extra = p_stream->p_headers + p_stream->i_headers;
if( b_store_num_headers )
void *p_org = p_stream->p_headers;
p_stream->i_headers += p_oggpacket->bytes;
p_stream->p_headers = realloc( p_stream->p_headers, p_stream->i_headers );
if( p_stream->p_headers )
{
/* Kate streams store the number of headers in the first header,
so we can't just test for 3 as Vorbis/Theora */
*(p_extra++) = p_stream->i_kate_num_headers;
memcpy( p_stream->p_headers, p_oggpacket->packet, p_stream->i_headers );
}
if( b_store_size )
else
{
*(p_extra++) = p_oggpacket->bytes >> 8;
*(p_extra++) = p_oggpacket->bytes & 0xFF;
p_stream->i_headers = 0;
p_stream->p_headers = NULL;
free( p_org );
}
memcpy( p_extra, p_oggpacket->packet, p_oggpacket->bytes );
p_stream->i_headers += p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0);
}
else if( xiph_AppendHeaders( &p_stream->i_headers, &p_stream->p_headers,
p_oggpacket->bytes, p_oggpacket->packet ) )
{
p_stream->i_headers = 0;
p_stream->p_headers = NULL;
}
if( p_stream->i_headers > 0 )
{
if( !p_stream->b_force_backup )
{
/* Last header received, commit changes */
......@@ -1464,33 +1464,34 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
*/
static bool Ogg_IsVorbisFormatCompatible( const es_format_t *p_new, const es_format_t *p_old )
{
int i_new = 0;
int i_old = 0;
int i;
for( i = 0; i < 3; i++ )
unsigned pi_new_size[XIPH_MAX_HEADER_COUNT];
void *pp_new_data[XIPH_MAX_HEADER_COUNT];
unsigned i_new_count;
if( xiph_SplitHeaders(pi_new_size, pp_new_data, &i_new_count, p_new->i_extra, p_new->p_extra ) )
i_new_count = 0;
unsigned pi_old_size[XIPH_MAX_HEADER_COUNT];
void *pp_old_data[XIPH_MAX_HEADER_COUNT];
unsigned i_old_count;
if( xiph_SplitHeaders(pi_old_size, pp_old_data, &i_old_count, p_old->i_extra, p_old->p_extra ) )
i_old_count = 0;
bool b_match = i_new_count == i_old_count;
for( unsigned i = 0; i < i_new_count && b_match; i++ )
{
const uint8_t *p_new_extra = ( const uint8_t*)p_new->p_extra + i_new;
const uint8_t *p_old_extra = ( const uint8_t*)p_old->p_extra + i_old;
if( p_new->i_extra < i_new+2 || p_old->i_extra < i_old+2 )
return false;
const int i_new_size = GetWBE( &p_new_extra[0] );
const int i_old_size = GetWBE( &p_old_extra[0] );
if( i != 1 ) /* Ignore vorbis comment */
{
if( i_new_size != i_old_size )
return false;
if( memcmp( &p_new_extra[2], &p_old_extra[2], i_new_size ) )
return false;
}
i_new += 2 + i_new_size;
i_old += 2 + i_old_size;
/* Ignore vorbis comment */
if( i == 1 )
continue;
if( pi_new_size[i] != pi_old_size[i] ||
memcmp( pp_new_data[i], pp_old_data[i], pi_new_size[i] ) )
b_match = false;
}
return true;
for( unsigned i = 0; i < i_new_count; i++ )
free( pp_new_data[i] );
for( unsigned i = 0; i < i_old_count; i++ )
free( pp_old_data[i] );
return b_match;
}
static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream )
{
......@@ -1507,44 +1508,22 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *
return !b_compatible;
}
static void Ogg_ExtractXiphMeta( demux_t *p_demux, const uint8_t *p_headers, int i_headers, int i_skip, bool b_has_num_headers )
static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsigned i_headers, unsigned i_skip )
{
demux_sys_t *p_ogg = p_demux->p_sys;
if (b_has_num_headers)
{
if (i_headers <= 0)
return;
/* number of headers on a byte, we're interested in the second header, so should be at least 2 to go on */
if (*p_headers++ < 2)
return;
--i_headers;
}
if( i_headers <= 2 )
return;
/* Skip first packet */
const int i_tmp = GetWBE( &p_headers[0] );
if( i_tmp > i_headers-2 )
return;
p_headers += 2 + i_tmp;
i_headers -= 2 + i_tmp;
if( i_headers <= 2 )
return;
/* */
int i_comment = GetWBE( &p_headers[0] );
const uint8_t *p_comment = &p_headers[2];
if( i_comment > i_headers - 2 )
return;
if( i_comment <= i_skip )
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count, i_headers, p_headers ) )
return;
/* TODO how to handle multiple comments properly ? */
vorbis_ParseComment( &p_ogg->p_meta, &p_comment[i_skip], i_comment - i_skip );
if( i_count >= 2 && pi_size[1] > i_skip )
vorbis_ParseComment( &p_ogg->p_meta, (uint8_t*)pp_data[1] + i_skip, pi_size[1] - i_skip );
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
}
static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers )
{
......@@ -1554,19 +1533,19 @@ static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8
{
/* 3 headers with the 2° one being the comments */
case VLC_CODEC_VORBIS:
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6, false );
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 );
break;
case VLC_CODEC_THEORA:
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6, false );
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 );
break;
case VLC_CODEC_SPEEX:
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 0, false );
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 0 );
break;
/* N headers with the 2° one being the comments */
case VLC_CODEC_KATE:
/* 1 byte for header type, 7 bit for magic, 1 reserved zero byte */
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+7+1, true );
/* 1 byte for header type, 7 bytes for magic, 1 reserved zero byte */
Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+7+1 );
break;
/* TODO */
......
/*****************************************************************************
* xiph.h: Xiph helpers
*****************************************************************************
* Copyright (C) 2010 Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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 <assert.h>
#define XIPH_MAX_HEADER_COUNT (256)
static inline int xiph_SplitHeaders(unsigned packet_size[], void *packet[], unsigned *packet_count,
unsigned extra_size, const void *extra)
{
const uint8_t *current = (const uint8_t*)extra;
const uint8_t *end = &current[extra_size];
if (extra_size < 1)
return VLC_EGENERIC;
/* Parse the packet count and their sizes */
const unsigned count = 1 + *current++;
if (packet_count)
*packet_count = count;
unsigned size = 0;
for (unsigned i = 0; i < count - 1; i++) {
packet_size[i] = 0;
for (;;) {
if (current >= end)
return VLC_EGENERIC;
packet_size[i] += *current;
if (*current++ != 255)
break;
}
size += packet_size[i];
}
if (end - current < size)
return VLC_EGENERIC;
packet_size[count - 1] = end - current - size;
/* Copy the payloads */
for (unsigned i = 0; i < count; i++) {
packet[i] = malloc(packet_size[i]);
if (!packet[i]) {
for (unsigned j = 0; j < i; j++)
free(packet[j]);
return VLC_ENOMEM;
}
if (packet_size[i] > 0) {
memcpy(packet[i], current, packet_size[i]);
current += packet_size[i];
}
}
return VLC_SUCCESS;
}
static inline int xiph_PackHeaders(int *extra_size, void **extra,
unsigned packet_size[], void *packet[], unsigned packet_count )
{
if (packet_count <= 0 || packet_count > XIPH_MAX_HEADER_COUNT)
return VLC_EGENERIC;
/* Compute the size needed for the whole extra data */
unsigned payload_size = 0;
unsigned header_size = 1;
for (unsigned i = 0; i < packet_count; i++) {
payload_size += packet_size[i];
if (i < packet_count - 1)
header_size += 1 + packet_size[i] / 255;
}
/* */
*extra_size = header_size + payload_size;
*extra = malloc(*extra_size);
if (*extra == NULL)
return VLC_ENOMEM;
/* Write the header */
uint8_t *current = (uint8_t*)*extra;
*current++ = packet_count - 1;
for (unsigned i = 0; i < packet_count - 1; i++) {
unsigned t = packet_size[i];
for (;;) {
if (t >= 255) {
*current++ = 255;
t -= 255;
} else {
*current++ = t;
break;
}
}
}
/* Copy the payloads */
for (unsigned i = 0; i < packet_count; i++) {
if (packet_size[i] > 0) {
memcpy(current, packet[i], packet_size[i]);
current += packet_size[i];
}
}
assert(current == (uint8_t*)*extra + *extra_size);
return VLC_SUCCESS;
}
static inline int xiph_AppendHeaders(int *extra_size, void **extra,
unsigned size, const void *data)
{
unsigned packet_size[XIPH_MAX_HEADER_COUNT];
void *packet[XIPH_MAX_HEADER_COUNT];
unsigned count;
if (*extra_size > 0 && *extra) {
if (xiph_SplitHeaders(packet_size, packet, &count, *extra_size, *extra))
return VLC_EGENERIC;
} else {
count = 0;
}
if (count >= XIPH_MAX_HEADER_COUNT)
return VLC_EGENERIC;
free(*extra);
packet_size[count] = size;
packet[count] = (void*)data;
if (xiph_PackHeaders(extra_size, extra, packet_size, packet, count + 1)) {
*extra_size = 0;
*extra = NULL;
}
for (unsigned i = 0; i < count; i++)
free(packet[i]);
if (*extra_size <= 0)
return VLC_EGENERIC;
return VLC_SUCCESS;
}
......@@ -35,6 +35,7 @@
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_codecs.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
......@@ -562,8 +563,7 @@ static block_t *OggCreateHeader( sout_mux_t *p_mux )
block_t *p_hdr = NULL;
block_t *p_og = NULL;
ogg_packet op;
uint8_t *p_extra;
int i, i_extra;
int i;
/* Write header for each stream. All b_o_s (beginning of stream) packets
* must appear first in the ogg stream so we take care of them first. */
......@@ -590,19 +590,22 @@ static block_t *OggCreateHeader( sout_mux_t *p_mux )
p_stream->i_fourcc == VLC_CODEC_THEORA )
{
/* First packet in order: vorbis/speex/theora info */
p_extra = p_input->p_fmt->p_extra;
i_extra = p_input->p_fmt->i_extra;
op.bytes = *(p_extra++) << 8;
op.bytes |= (*(p_extra++) & 0xFF);
op.packet = p_extra;
i_extra -= (op.bytes + 2);
if( i_extra < 0 )
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
{
msg_Err( p_mux, "header data corrupted");
op.bytes += i_extra;
i_count = 0;
pi_size[0] = 0;
pp_data[0] = NULL;
}
op.bytes = pi_size[0];
op.packet = pp_data[0];
if( pi_size[0] <= 0 )
msg_Err( p_mux, "header data corrupted");
op.b_o_s = 1;
op.e_o_s = 0;
op.granulepos = 0;
......@@ -616,6 +619,9 @@ static block_t *OggCreateHeader( sout_mux_t *p_mux )
p_stream->i_keyframe_granule_shift =
( (op.packet[40] & 0x03) << 3 ) | ( (op.packet[41] & 0xe0) >> 5 );
}
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
}
else if( p_stream->i_fourcc == VLC_CODEC_DIRAC )
{
......@@ -667,34 +673,21 @@ static block_t *OggCreateHeader( sout_mux_t *p_mux )
p_stream->i_fourcc == VLC_CODEC_SPEEX ||
p_stream->i_fourcc == VLC_CODEC_THEORA )
{
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
i_count = 0;
/* Special case, headers are already there in the incoming stream.
* We need to gather them an mark them as headers. */
int j = 2;
if( p_stream->i_fourcc == VLC_CODEC_SPEEX ) j = 1;
p_extra = p_input->p_fmt->p_extra;
i_extra = p_input->p_fmt->i_extra;
/* Skip 1 header */
op.bytes = *(p_extra++) << 8;
op.bytes |= (*(p_extra++) & 0xFF);
op.packet = p_extra;
p_extra += op.bytes;
i_extra -= (op.bytes + 2);
while( j-- )
for( unsigned i = 1; i < i_count; i++ )
{
op.bytes = *(p_extra++) << 8;
op.bytes |= (*(p_extra++) & 0xFF);
op.packet = p_extra;
p_extra += op.bytes;
i_extra -= (op.bytes + 2);
if( i_extra < 0 )
{
op.bytes = pi_size[i];
op.packet = pp_data[i];
if( pi_size[i] <= 0 )
msg_Err( p_mux, "header data corrupted");
op.bytes += i_extra;
}
op.b_o_s = 0;
op.e_o_s = 0;
......@@ -702,13 +695,15 @@ static block_t *OggCreateHeader( sout_mux_t *p_mux )
op.packetno = p_stream->i_packet_no++;
ogg_stream_packetin( &p_stream->os, &op );
if( j == 0 )
if( i == i_count - 1 )
p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
else
p_og = OggStreamPageOut( p_mux, &p_stream->os, 0 );
if( p_og )
block_ChainAppend( &p_hdr, p_og );
}
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
}
else if( p_stream->i_fourcc != VLC_CODEC_FLAC &&
p_stream->i_fourcc != VLC_CODEC_DIRAC )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment