[vlc-commits] [Git][videolan/vlc][master] 5 commits: demux: ogg: update opus channel mapping

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Wed Jan 26 10:47:33 UTC 2022



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
87b33462 by Francois Cartegnie at 2022-01-26T08:22:48+00:00
demux: ogg: update opus channel mapping

- - - - -
7c411c9c by Francois Cartegnie at 2022-01-26T08:22:48+00:00
codec: opus: simplify dummy header code

- - - - -
f083c0ff by Francois Cartegnie at 2022-01-26T08:22:48+00:00
codec: opus_header: add init/clean cycle methods

- - - - -
f582f5c5 by Francois Cartegnie at 2022-01-26T08:22:48+00:00
codec: opus_header: read & store decoding matrix

- - - - -
d717dae8 by Francois Cartegnie at 2022-01-26T08:22:48+00:00
codec: opus: add support for projection decoder

- - - - -


6 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
- modules/demux/ogg.c


Changes:

=====================================
modules/access/live555.cpp
=====================================
@@ -1040,11 +1040,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
=====================================
@@ -37,6 +37,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"
 
@@ -87,6 +90,9 @@ typedef struct
      */
     OpusHeader header;
     OpusMSDecoder *p_st;
+#ifdef OPUS_HAVE_OPUS_PROJECTION_H
+    OpusProjectionDecoder *p_pr;
+#endif
 
     /*
      * Common properties
@@ -159,6 +165,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
@@ -175,6 +279,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_sys->b_has_headers = false;
+    opus_header_init(&p_sys->header);
 
     date_Set( &p_sys->end_date, VLC_TICK_INVALID );
 
@@ -185,6 +290,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;
 }
@@ -238,6 +346,7 @@ static int DecodeAudio( decoder_t *p_dec, block_t *p_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];
@@ -245,34 +354,32 @@ 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;
 
     /* 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 */
     {
         OpusHeader header;
+        opus_header_init(&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() ) )
+        int ret = opus_write_header( &p_alloc, &i_extra, &header,
+                                     opus_get_version_string() );
+        opus_header_clean(&header);
+        if(ret != 0)
+        {
+            free( p_alloc );
             return VLC_ENOMEM;
-        b_dummy_header = true;
+        }
+        p_extra = p_alloc;
     }
 
     if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
-                           i_extra, p_extra ) )
-    {
-        if( b_dummy_header )
-            free( p_extra );
-        return VLC_EGENERIC;
-    }
-
-    if( i_count < 2 )
+                           i_extra, p_extra ) || i_count < 2 )
     {
-        if( b_dummy_header )
-            free( p_extra );
+        free( p_alloc );
         return VLC_EGENERIC;
     }
 
@@ -287,10 +394,13 @@ static int ProcessHeaders( decoder_t *p_dec )
     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;
 }
@@ -300,11 +410,7 @@ static int ProcessHeaders( decoder_t *p_dec )
  *****************************************************************************/
 static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
 {
-    int err;
-    unsigned char* p_stream_map;
-    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) )
@@ -317,15 +423,17 @@ static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
     if((p_header->channels>2 && p_header->channel_mapping==0) ||
         (p_header->channels>8 && p_header->channel_mapping==1) ||
         (p_header->channels>18 && p_header->channel_mapping==2) ||
-        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)
+    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" );
@@ -341,47 +449,24 @@ static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
     {
         p_dec->fmt_out.audio.i_physical_channels =
             pi_channels_maps[p_header->channels];
-
-        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,
-                                      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_stream_map = new_stream_map;
-        }
-        else
-            p_stream_map = p_header->stream_map;
     }
-    else //p_header->channel_mapping == 2
+    else //p_header->channel_mapping >= 2
     {
         p_dec->fmt_out.audio.channel_type = AUDIO_CHANNEL_TYPE_AMBISONICS;
-        p_stream_map = p_header->stream_map;
     }
 
     /* Opus decoder init */
-    p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels,
-                    p_header->nb_streams, p_header->nb_coupled,
-                    p_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
@@ -475,8 +560,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 );
 
     int i_end_trim = 0;
     if( i_duration > 0 && spp > 0 &&
@@ -529,8 +613,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 );
 }
 
@@ -668,6 +753,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);
@@ -742,6 +828,7 @@ static int OpenEncoder(vlc_object_t *p_this)
         sys->padding = NULL;
     }
 
+    opus_header_clean(&header);
 
     if (status != VLC_SUCCESS)
         return status;
@@ -755,6 +842,7 @@ static int OpenEncoder(vlc_object_t *p_this)
     return VLC_SUCCESS;
 
 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
=====================================
@@ -1049,6 +1049,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 };
@@ -1130,6 +1131,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;
     }
 
@@ -1152,10 +1154,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");
 }
 


=====================================
modules/demux/ogg.c
=====================================
@@ -2873,10 +2873,30 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
     /* Cheat and get additional info ;) */
     oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
     oggpack_adv( &opb, 64 );
-    oggpack_adv( &opb, 8 ); /* version_id */
-    p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
-    fill_channels_info(&p_stream->fmt.audio);
-    p_stream->i_pre_skip = oggpack_read( &opb, 16 );
+    if( oggpack_read( &opb, 8 ) <= 1 ) /* version_id */
+    {
+        p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
+        p_stream->i_pre_skip = oggpack_read( &opb, 16 );
+        oggpack_adv( &opb, 48 );
+        switch( oggpack_read( &opb, 8 ) ) /* mapping family */
+        {
+            case 0: /* RFC7587 */
+                if( p_stream->fmt.audio.i_channels > 2 )
+                    break;
+                /* fallthrough */
+            case 1: /* Vorbis */
+                if( p_stream->fmt.audio.i_channels > 8 )
+                    break;
+                fill_channels_info( &p_stream->fmt.audio );
+                break;
+            case 2: /* Ambisonic */
+            case 3: /* Ambisonic with mixing matrix */
+                p_stream->fmt.audio.channel_type = AUDIO_CHANNEL_TYPE_AMBISONICS;
+                break;
+            default:
+                break;
+        }
+    }
     /* For Opus, trash the first 80 ms of decoded output as
            well, to avoid blowing out speakers if we get unlucky.
            Opus predicts content from prior frames, which can go



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6896b5611a5be62cbd5c798f9993e5a62052fa5e...d717dae8fd1d15c131d8606957918771bd882942

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6896b5611a5be62cbd5c798f9993e5a62052fa5e...d717dae8fd1d15c131d8606957918771bd882942
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list