[vlc-commits] [Git][videolan/vlc][3.0.x] 7 commits: codec: opus: simplify dummy header code
Steve Lhomme (@robUx4)
gitlab at videolan.org
Thu Jan 4 15:09:30 UTC 2024
Steve Lhomme pushed to branch 3.0.x at VideoLAN / VLC
Commits:
45a3f94a by François Cartegnie at 2023-12-22T16:12:05+01:00
codec: opus: simplify dummy header code
(cherry picked from commit 7c411c9c97f9bcb4c09d255737461b9f08ec6a7f)
- - - - -
b82e70e5 by François Cartegnie at 2023-12-22T16:12:05+01:00
codec: opus_header: add init/clean cycle methods
(cherry picked from commit f083c0ff7e02ff15d95cff77fbd898fc239cd675)
- - - - -
5e3fe0c2 by François Cartegnie at 2023-12-22T16:12:05+01:00
codec: opus: header fix needs valid defaults
(cherry picked from commit 0d0df433920d3510068890b0473a8bb4c505eea3)
- - - - -
590a0f52 by François Cartegnie at 2023-12-22T16:12:05+01:00
codec: opus: rewrite extradata handling
(cherry picked from commit c9cd7454e0c71cdd44f105cff232d4b3624c90b2)
- - - - -
93b72818 by Pierre Lamot at 2023-12-22T20:36:18+01:00
opus: support third order ambisonic with family 2 mapping
Signed-off-by: Jean-Baptiste Kempf <jb at videolan.org>
(cherry picked from commit 2b6ac91e95c0c59b2d575f720fe22ca65bc0827e)
- - - - -
18a1c0b1 by François Cartegnie at 2024-01-02T21:12:29+01:00
codec: opus_header: read & store decoding matrix
(cherry picked from commit f582f5c5e58b37654a695ccdaed4d002390158d8)
- - - - -
01b9288d by François Cartegnie at 2024-01-02T21:15:03+01:00
codec: opus: add support for projection decoder
(cherry picked from commit d717dae8fd1d15c131d8606957918771bd882942)
- - - - -
5 changed files:
- modules/access/live555.cpp
- modules/codec/opus.c
- modules/codec/opus_header.c
- modules/codec/opus_header.h
- modules/demux/mpeg/ts_psi.c
Changes:
=====================================
modules/access/live555.cpp
=====================================
@@ -1043,11 +1043,13 @@ static int SessionsSetup( demux_t *p_demux )
unsigned char *p_extra;
tk->fmt.i_codec = VLC_CODEC_OPUS;
OpusHeader header;
+ opus_header_init(&header);
// "The RTP clock rate in "a=rtpmap" MUST be 48000 and the number of channels MUST be 2."
// See: https://datatracker.ietf.org/doc/html/draft-ietf-payload-rtp-opus-11#section-7
opus_prepare_header( 2, 48000, &header );
if( opus_write_header( &p_extra, &i_extra, &header, NULL ) )
return VLC_ENOMEM;
+ opus_header_clean(&header);
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = p_extra;
}
=====================================
modules/codec/opus.c
=====================================
@@ -38,6 +38,9 @@
#include <ogg/ogg.h>
#include <opus.h>
#include <opus_multistream.h>
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+# include <opus_projection.h> /* from 1.3.0 */
+#endif
#include "opus_header.h"
@@ -89,6 +92,9 @@ struct decoder_sys_t
*/
OpusHeader header;
OpusMSDecoder *p_st;
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ OpusProjectionDecoder *p_pr;
+#endif
/*
* Common properties
@@ -162,6 +168,104 @@ static int ProcessInitialHeader ( decoder_t *, ogg_packet * );
static block_t *ProcessPacket( decoder_t *, ogg_packet *, block_t * );
static block_t *DecodePacket( decoder_t *, ogg_packet *, int, vlc_tick_t );
+/*****************************************************************************
+ * Implementation Wrappers
+ *****************************************************************************/
+
+static void DecoderDestroy( decoder_sys_t *p_sys )
+{
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ if( p_sys->p_pr )
+ {
+ opus_projection_decoder_destroy( p_sys->p_pr );
+ p_sys->p_pr = NULL;
+ }
+ else
+#endif
+ if( p_sys->p_st )
+ {
+ opus_multistream_decoder_destroy( p_sys->p_st );
+ p_sys->p_st = NULL;
+ }
+}
+
+#ifdef OPUS_SET_GAIN
+static int SetDecoderGain( decoder_sys_t *p_sys, int gain )
+{
+# ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ if( p_sys->p_pr )
+ {
+ if( opus_projection_decoder_ctl(
+ p_sys->p_pr, OPUS_SET_GAIN(gain) ) != OPUS_OK )
+ return VLC_EGENERIC;
+ }
+ else
+# endif
+ {
+ if( opus_multistream_decoder_ctl(
+ p_sys->p_st, OPUS_SET_GAIN(gain) ) != OPUS_OK )
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+#endif
+
+static int DecoderDecodeFloat( decoder_sys_t *p_sys, const ogg_packet *p_oggpacket,
+ int spp, block_t *out )
+{
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ if( p_sys->p_pr )
+ return opus_projection_decode_float(p_sys->p_pr, p_oggpacket->packet,
+ p_oggpacket->bytes,
+ (float *)out->p_buffer, spp, 0);
+ else
+#endif
+ return opus_multistream_decode_float(p_sys->p_st, p_oggpacket->packet,
+ p_oggpacket->bytes,
+ (float *)out->p_buffer, spp, 0);
+}
+
+static int DecoderCreate( decoder_sys_t *p_sys )
+{
+ int err;
+ const OpusHeader *p_header = &p_sys->header;
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ if( p_header->channel_mapping == 3 )
+ {
+ p_sys->p_pr = opus_projection_decoder_create( 48000, p_header->channels,
+ p_header->nb_streams, p_header->nb_coupled,
+ p_header->dmatrix, p_header->dmatrix_size, &err );
+ }
+ else
+#endif
+ {
+ const unsigned char* p_stream_map = p_header->stream_map;
+ unsigned char new_stream_map[8];
+ if ( p_header->channel_mapping <= 1 )
+ {
+ if( p_header->channels > 2 )
+ {
+ static const uint32_t *pi_ch[6] = { pi_3channels_in, pi_4channels_in,
+ pi_5channels_in, pi_6channels_in,
+ pi_7channels_in, pi_8channels_in };
+ uint8_t pi_chan_table[AOUT_CHAN_MAX];
+
+ aout_CheckChannelReorder( pi_ch[p_header->channels-3], NULL,
+ pi_channels_maps[p_header->channels],
+ pi_chan_table );
+ for( int i=0;i<p_header->channels;i++ )
+ new_stream_map[pi_chan_table[i]] = p_header->stream_map[i];
+
+ p_stream_map = new_stream_map;
+ }
+ }
+
+ p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels,
+ p_header->nb_streams, p_header->nb_coupled,
+ p_stream_map, &err );
+ }
+ return err == OPUS_OK ? VLC_SUCCESS : VLC_EGENERIC;
+}
/*****************************************************************************
* OpenDecoder: probe the decoder and return score
@@ -178,6 +282,7 @@ static int OpenDecoder( vlc_object_t *p_this )
if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL )
return VLC_ENOMEM;
p_dec->p_sys->b_has_headers = false;
+ opus_header_init(&p_sys->header);
date_Set( &p_sys->end_date, 0 );
@@ -189,6 +294,9 @@ static int OpenDecoder( vlc_object_t *p_this )
p_dec->pf_flush = Flush;
p_sys->p_st = NULL;
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+ p_sys->p_pr = NULL;
+#endif
return VLC_SUCCESS;
}
@@ -252,6 +360,7 @@ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
*****************************************************************************/
static int ProcessHeaders( decoder_t *p_dec )
{
+ decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
@@ -259,52 +368,63 @@ static int ProcessHeaders( decoder_t *p_dec )
unsigned i_count;
int i_extra = p_dec->fmt_in.i_extra;
- uint8_t *p_extra = p_dec->fmt_in.p_extra;
+ const uint8_t *p_extra = p_dec->fmt_in.p_extra;
+ uint8_t *p_alloc = NULL;
+ /* Xiph headers as extradata */
+ if( i_extra > 21 && !memcmp( &p_extra[2], "OpusHead", 8 ) )
+ {
+ if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
+ i_extra, p_extra ) || i_count < 2 )
+ {
+ /* Borked Xiph headers */
+ free( p_alloc );
+ return VLC_EGENERIC;
+ }
+
+ oggpacket.bytes = pi_size[0];
+ oggpacket.packet = (unsigned char *) pp_data[0];
+ }
/* If we have no header (e.g. from RTP), make one. */
- bool b_dummy_header = false;
- if( !i_extra ||
- (i_extra > 10 && memcmp( &p_extra[2], "OpusHead", 8 )) ) /* Borked muxers */
+ else if( i_extra < 19 || memcmp( p_extra, "OpusHead", 8 ) )
{
OpusHeader header;
- opus_prepare_header( p_dec->fmt_in.audio.i_channels,
- p_dec->fmt_in.audio.i_rate, &header );
- if( opus_write_header( &p_extra, &i_extra, &header,
- opus_get_version_string() ) )
+ opus_header_init(&header);
+ opus_prepare_header( p_dec->fmt_in.audio.i_channels ? p_dec->fmt_in.audio.i_channels : 2,
+ p_dec->fmt_in.audio.i_rate ? p_dec->fmt_in.audio.i_rate : 48000,
+ &header );
+ int ret = opus_write_header( &p_alloc, &i_extra, &header,
+ opus_get_version_string() );
+ opus_header_clean(&header);
+ if(ret != 0 || i_extra < 21)
+ {
+ free( p_alloc );
return VLC_ENOMEM;
- b_dummy_header = true;
- }
-
- if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
- i_extra, p_extra ) )
- {
- if( b_dummy_header )
- free( p_extra );
- return VLC_EGENERIC;
+ }
+ oggpacket.bytes = p_alloc[1]; /* Xiph header is type8/size8 */
+ oggpacket.packet = (unsigned char *) p_alloc + 2; /* Point directly to opus header start */
}
-
- if( i_count < 2 )
+ else /* raw header in extradata */
{
- if( b_dummy_header )
- free( p_extra );
- return VLC_EGENERIC;
+ oggpacket.bytes = i_extra;
+ oggpacket.packet = (unsigned char *) p_extra;
}
+ /* Take care of the initial Opus header */
oggpacket.granulepos = -1;
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
-
- /* Take care of the initial Opus header */
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
- oggpacket.bytes = pi_size[0];
- oggpacket.packet = (void *)pp_data[0];
int ret = ProcessInitialHeader( p_dec, &oggpacket );
if (ret != VLC_SUCCESS)
+ {
msg_Err( p_dec, "initial Opus header is corrupted" );
+ opus_header_clean( &p_sys->header );
+ opus_header_init( &p_sys->header );
+ }
- if( b_dummy_header )
- free( p_extra );
+ free( p_alloc );
return ret;
}
@@ -314,10 +434,7 @@ static int ProcessHeaders( decoder_t *p_dec )
*****************************************************************************/
static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
{
- int err;
- unsigned char new_stream_map[8];
decoder_sys_t *p_sys = p_dec->p_sys;
-
OpusHeader *p_header = &p_sys->header;
if( !opus_header_parse((unsigned char *)p_oggpacket->packet,p_oggpacket->bytes,p_header) )
@@ -328,48 +445,52 @@ static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
msg_Dbg( p_dec, "Opus audio with %d channels", p_header->channels);
if((p_header->channels>2 && p_header->channel_mapping==0) ||
- p_header->channels>8 ||
- p_header->channel_mapping>1)
+ (p_header->channels>8 && p_header->channel_mapping==1) ||
+ (p_header->channels>18 && p_header->channel_mapping==2) ||
+ (p_header->channels>18 && p_header->channel_mapping==3))
{
msg_Err( p_dec, "Unsupported channel mapping" );
return VLC_EGENERIC;
}
+ if (p_header->channel_mapping >= 2)
+ {
+ int i_order = floor(sqrt(p_header->channels));
+ int i_nondiegetic = p_header->channels - i_order * i_order;
+ msg_Dbg( p_dec, "Opus Ambisonic audio order=%d channels=%d+%d",
+ i_order, p_header->channels - i_nondiegetic, i_nondiegetic);
+ if (i_nondiegetic != 0 && i_nondiegetic != 2)
+ {
+ msg_Err( p_dec, "Unsupported ambisonic channel mapping" );
+ return VLC_EGENERIC;
+ }
+ }
/* Setup the format */
- p_dec->fmt_out.audio.i_physical_channels =
- pi_channels_maps[p_header->channels];
p_dec->fmt_out.audio.i_channels = p_header->channels;
p_dec->fmt_out.audio.i_rate = 48000;
- if( p_header->channels>2 )
+ if (p_header->channel_mapping <= 1)
{
- static const uint32_t *pi_ch[6] = { pi_3channels_in, pi_4channels_in,
- pi_5channels_in, pi_6channels_in,
- pi_7channels_in, pi_8channels_in };
- uint8_t pi_chan_table[AOUT_CHAN_MAX];
-
- aout_CheckChannelReorder( pi_ch[p_header->channels-3], NULL,
- p_dec->fmt_out.audio.i_physical_channels,
- pi_chan_table );
- for(int i=0;i<p_header->channels;i++)
- new_stream_map[pi_chan_table[i]]=p_header->stream_map[i];
+ p_dec->fmt_out.audio.i_physical_channels =
+ pi_channels_maps[p_header->channels];
}
+ else //p_header->channel_mapping >= 2
+ {
+ p_dec->fmt_out.audio.channel_type = AUDIO_CHANNEL_TYPE_AMBISONICS;
+ }
+
/* Opus decoder init */
- p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels,
- p_header->nb_streams, p_header->nb_coupled,
- p_header->channels>2?new_stream_map:p_header->stream_map,
- &err );
- if( !p_sys->p_st || err!=OPUS_OK )
+ if( DecoderCreate( p_sys ) != VLC_SUCCESS )
{
msg_Err( p_dec, "decoder initialization failed" );
return VLC_EGENERIC;
}
#ifdef OPUS_SET_GAIN
- if( opus_multistream_decoder_ctl( p_sys->p_st,OPUS_SET_GAIN(p_header->gain) ) != OPUS_OK )
+ if( SetDecoderGain( p_sys, p_header->gain ) != VLC_SUCCESS )
{
msg_Err( p_dec, "OPUS_SET_GAIN failed" );
- opus_multistream_decoder_destroy( p_sys->p_st );
+ DecoderDestroy( p_sys );
return VLC_EGENERIC;
}
#endif
@@ -470,8 +591,7 @@ static block_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
return NULL;
}
- spp=opus_multistream_decode_float(p_sys->p_st, p_oggpacket->packet,
- p_oggpacket->bytes, (float *)p_aout_buffer->p_buffer, spp, 0);
+ spp = DecoderDecodeFloat( p_sys, p_oggpacket, spp, p_aout_buffer );
if( spp < 0 || i_nb_samples <= 0 || i_end_trim >= i_nb_samples)
{
block_Release(p_aout_buffer);
@@ -516,8 +636,9 @@ 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->p_st ) opus_multistream_decoder_destroy(p_sys->p_st);
+ DecoderDestroy( p_sys );
+ opus_header_clean( &p_sys->header );
free( p_sys );
}
@@ -656,6 +777,7 @@ static int OpenEncoder(vlc_object_t *p_this)
enc->fmt_out.audio.i_channels = enc->fmt_in.audio.i_channels;
OpusHeader header;
+ opus_header_init(&header);
opus_prepare_header(enc->fmt_out.audio.i_channels,
enc->fmt_out.audio.i_rate, &header);
@@ -730,9 +852,12 @@ static int OpenEncoder(vlc_object_t *p_this)
sys->padding = NULL;
}
+ opus_header_clean(&header);
+
return status;
error:
+ opus_header_clean(&header);
if (sys->enc)
opus_multistream_encoder_destroy(sys->enc);
free(sys->buffer);
=====================================
modules/codec/opus_header.c
=====================================
@@ -179,7 +179,16 @@ int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
return 0;
h->channel_mapping = ch;
- if (h->channel_mapping != 0)
+ if(h->channel_mapping == 0)
+ {
+ if(h->channels>2)
+ return 0;
+ h->nb_streams = 1;
+ h->nb_coupled = h->channels>1;
+ h->stream_map[0]=0;
+ h->stream_map[1]=1;
+ }
+ else if(h->channel_mapping < 4)
{
if (!read_chars(&p, &ch, 1))
return 0;
@@ -191,26 +200,43 @@ int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
if (!read_chars(&p, &ch, 1))
return 0;
- if (ch>h->nb_streams || (ch+h->nb_streams)>255)
+ if (ch > h->nb_streams)
return 0;
h->nb_coupled = ch;
/* Multi-stream support */
- for (int i=0;i<h->channels;i++)
+ if(h->channel_mapping == 2)
+ {
+ if (h->nb_coupled + h->nb_streams > 255)
+ return 0;
+ for (int i=0;i<h->channels;i++)
+ {
+ if (!read_chars(&p, &h->stream_map[i], 1))
+ return 0;
+ if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
+ return 0;
+ }
+ }
+ else /* Decoding Matrix */
{
- if (!read_chars(&p, &h->stream_map[i], 1))
+ if (h->nb_coupled + h->nb_streams > 255)
+ return 0;
+ int matrix_entries = h->channels * (h->nb_streams + h->nb_coupled);
+ int matrix_size = len - p.pos;
+ if(matrix_size < matrix_entries * 2)
return 0;
- if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
+ h->dmatrix = malloc(matrix_size);
+ if(h->dmatrix == NULL)
return 0;
+ if(!read_chars(&p, h->dmatrix, matrix_size))
+ {
+ free(h->dmatrix);
+ return 0;
+ }
+ h->dmatrix_size = matrix_size;
}
- } else {
- if(h->channels>2)
- return 0;
- h->nb_streams = 1;
- h->nb_coupled = h->channels>1;
- h->stream_map[0]=0;
- h->stream_map[1]=1;
}
+
/*For version 0/1 we know there won't be any more data
so reject any that have data past the end.*/
if ((h->version==0 || h->version==1) && p.pos != len)
@@ -350,7 +376,7 @@ static int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int
if (!write_chars(&p, &ch, 1))
return 0;
- if (h->channel_mapping != 0)
+ if (h->channel_mapping == 1)
{
ch = h->nb_streams;
if (!write_chars(&p, &ch, 1))
@@ -421,3 +447,21 @@ int opus_write_header(uint8_t **p_extra, int *i_extra, OpusHeader *header, const
return 0;
}
+void opus_header_init(OpusHeader *h)
+{
+ h->version = 0;
+ h->channels = 0;
+ h->preskip = 3840; /* default is 80 ms */
+ h->input_sample_rate = 0; /* unknown */
+ h->gain = 0;
+ h->channel_mapping = 255; /* unknown */
+ h->nb_streams = 0;
+ h->nb_coupled = 0;
+ h->dmatrix_size = 0;
+ h->dmatrix = NULL;
+}
+
+void opus_header_clean(OpusHeader *h)
+{
+ free(h->dmatrix);
+}
=====================================
modules/codec/opus_header.h
=====================================
@@ -41,8 +41,12 @@ typedef struct {
int nb_streams;
int nb_coupled;
unsigned char stream_map[255];
+ size_t dmatrix_size;
+ unsigned char *dmatrix;
} OpusHeader;
+void opus_header_init(OpusHeader *);
+void opus_header_clean(OpusHeader *);
int opus_header_parse(const unsigned char *header, int len, OpusHeader *h);
void opus_prepare_header(unsigned channels, unsigned rate, OpusHeader *header);
int opus_write_header(uint8_t **p_extra, int *i_extra, OpusHeader *header, const char *vendor);
=====================================
modules/demux/mpeg/ts_psi.c
=====================================
@@ -850,6 +850,7 @@ static int vlc_ceil_log2( const unsigned int val )
static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt)
{
OpusHeader h;
+ opus_header_init(&h);
/* default mapping */
static const unsigned char map[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
@@ -930,6 +931,7 @@ static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt
if (!channels) {
msg_Err(demux, "Opus channel configuration 0x%.2x not supported yet", p[1]);
+ opus_header_clean(&h);
return;
}
@@ -952,10 +954,12 @@ static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt
p_fmt->audio.i_rate = 48000;
}
}
+ opus_header_clean(&h);
return;
explicit_config_too_short:
+ opus_header_clean(&h);
msg_Err(demux, "Opus descriptor too short");
}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0cb962765f889be53139b43114c68350d5320057...01b9288de1576131c830f4e9f4c6a4dbbd95fb0c
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0cb962765f889be53139b43114c68350d5320057...01b9288de1576131c830f4e9f4c6a4dbbd95fb0c
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list