[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