From b5ad5b6152423762fe4c6a0849f7307678223da2 Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Wed, 17 Feb 2010 01:30:45 +0100 Subject: [PATCH] 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. --- modules/access/rtp/xiph.c | 37 +++--- modules/codec/kate.c | 108 +++++------------ modules/codec/speex.c | 109 ++++++----------- modules/codec/theora.c | 127 ++++++-------------- modules/codec/vorbis.c | 130 +++++++-------------- modules/demux/avi/avi.c | 67 ----------- modules/demux/mkv/matroska_segment.cpp | 150 ++---------------------- modules/demux/ogg.c | 155 +++++++++++-------------- modules/demux/xiph.h | 149 ++++++++++++++++++++++++ modules/mux/ogg.c | 69 +++++------ 10 files changed, 414 insertions(+), 687 deletions(-) create mode 100644 modules/demux/xiph.h diff --git a/modules/access/rtp/xiph.c b/modules/access/rtp/xiph.c index 16aae1cc01..83b3eb592c 100644 --- a/modules/access/rtp/xiph.c +++ b/modules/access/rtp/xiph.c @@ -36,6 +36,7 @@ #include #include +#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; } diff --git a/modules/codec/kate.c b/modules/codec/kate.c index e912e035af..4cd7e8993a 100644 --- a/modules/codec/kate.c +++ b/modules/codec/kate.c @@ -33,6 +33,7 @@ #include #include #include +#include "../demux/xiph.h" #include #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; diff --git a/modules/codec/speex.c b/modules/codec/speex.c index 7a171f5109..b8a99a3781 100644 --- a/modules/codec/speex.c +++ b/modules/codec/speex.c @@ -33,6 +33,7 @@ #include #include #include +#include "../demux/xiph.h" #include #include @@ -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; } diff --git a/modules/codec/theora.c b/modules/codec/theora.c index d9345003ce..57d90d5e6a 100644 --- a/modules/codec/theora.c +++ b/modules/codec/theora.c @@ -33,6 +33,8 @@ #include #include #include +#include "../demux/xiph.h" + #include #include @@ -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 */ diff --git a/modules/codec/vorbis.c b/modules/codec/vorbis.c index 3b8fba2d69..4fe9b356b3 100644 --- a/modules/codec/vorbis.c +++ b/modules/codec/vorbis.c @@ -37,6 +37,7 @@ #include #include #include +#include "../demux/xiph.h" #include @@ -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 ); diff --git a/modules/demux/avi/avi.c b/modules/demux/avi/avi.c index 523537624f..edc38fac90 100644 --- a/modules/demux/avi/avi.c +++ b/modules/demux/avi/avi.c @@ -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 ): diff --git a/modules/demux/mkv/matroska_segment.cpp b/modules/demux/mkv/matroska_segment.cpp index abb32c8b63..a9c0faf978 100644 --- a/modules/demux/mkv/matroska_segment.cpp +++ b/modules/demux/mkv/matroska_segment.cpp @@ -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" ) ) { diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index b92ed954c9..4ccc546210 100644 --- a/modules/demux/ogg.c +++ b/modules/demux/ogg.c @@ -39,6 +39,7 @@ #include #include +#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 */ diff --git a/modules/demux/xiph.h b/modules/demux/xiph.h new file mode 100644 index 0000000000..dc1df97582 --- /dev/null +++ b/modules/demux/xiph.h @@ -0,0 +1,149 @@ +/***************************************************************************** + * xiph.h: Xiph helpers + ***************************************************************************** + * Copyright (C) 2010 Laurent Aimar + * $Id$ + * + * Authors: Laurent Aimar + * + * 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 +#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 = ¤t[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; +} + diff --git a/modules/mux/ogg.c b/modules/mux/ogg.c index 2aaa3693bf..7e31984988 100644 --- a/modules/mux/ogg.c +++ b/modules/mux/ogg.c @@ -35,6 +35,7 @@ #include #include #include +#include "../demux/xiph.h" #include @@ -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 ) -- 2.39.2