]> git.sesse.net Git - vlc/blobdiff - modules/demux/ogg.c
arm_neon: Fix a minor typo in a comment
[vlc] / modules / demux / ogg.c
index 8afaeb6ba81d1cc36b0123e7f937247d6546d972..d6ced6296188b9e8e6303a12d1eb618aa2be2ccd 100644 (file)
@@ -40,8 +40,7 @@
 #include <vlc_codecs.h>
 #include <vlc_bits.h>
 #include "xiph.h"
-#include "vorbis.h"
-#include "kate_categories.h"
+#include "xiph_metadata.h"
 #include "ogg.h"
 #include "oggseek.h"
 
@@ -134,7 +133,7 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
 static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream );
 
 /* */
-static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers );
+static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers );
 static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream, double f_rate );
 
 /* Logical bitstream headers */
@@ -147,6 +146,34 @@ static void Ogg_ReadFlacHeader( demux_t *, logical_stream_t *, ogg_packet * );
 static void Ogg_ReadAnnodexHeader( demux_t *, logical_stream_t *, ogg_packet * );
 static bool Ogg_ReadDiracHeader( logical_stream_t *, ogg_packet * );
 
+static void fill_channels_info(audio_format_t *audio)
+{
+    static const int pi_channels_map[9] =
+    {
+        0,
+        AOUT_CHAN_CENTER,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
+        AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
+            | AOUT_CHAN_REARRIGHT,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+            | AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT
+            | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
+        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
+            | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
+            | AOUT_CHAN_LFE,
+    };
+
+    unsigned chans = audio->i_channels;
+    if (chans < sizeof(pi_channels_map) / sizeof(pi_channels_map[0]))
+        audio->i_physical_channels =
+        audio->i_original_channels = pi_channels_map[chans];
+}
+
 /*****************************************************************************
  * Open: initializes ogg demux structures
  *****************************************************************************/
@@ -478,7 +505,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
                 return VLC_EGENERIC;
 
             *pi_int = p_sys->i_attachments;
-            *ppp_attach = xmalloc( sizeof(input_attachment_t**) * p_sys->i_attachments );
+            *ppp_attach = xmalloc( sizeof(input_attachment_t*) * p_sys->i_attachments );
             for( int i = 0; i < p_sys->i_attachments; i++ )
                 (*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] );
             return VLC_SUCCESS;
@@ -514,7 +541,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             if( p_sys->i_seekpoints > 0 )
             {
                 *pi_int = 1;
-                *ppp_title = malloc( sizeof( input_title_t**) );
+                *ppp_title = malloc( sizeof( input_title_t* ) );
                 input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New();
                 for( int i = 0; i < p_sys->i_seekpoints; i++ )
                 {
@@ -543,9 +570,29 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             }
 
             Ogg_ResetStreamHelper( p_sys );
-            int64_t i_block = p_sys->pp_seekpoints[i_seekpoint]->i_time_offset * p_sys->i_bitrate / INT64_C(8000000);
-            if( stream_Seek( p_demux->s, i_block ) )
-                return VLC_EGENERIC;
+
+            if ( p_sys->i_bitrate == 0 || p_sys->b_partial_bitrate )
+            {
+                /* we won't be able to find block by time
+                 * we'll need to bisect search from here
+                 * or use skeleton index if any (FIXME)
+                */
+                if ( p_sys->pp_stream[0]->fmt.i_codec == VLC_CODEC_OPUS )
+                {
+                    /* Granule = Freq * T + pre-skip */
+                    oggseek_find_frame ( p_demux, p_sys->pp_stream[0],
+                        ( p_sys->pp_seekpoints[i_seekpoint]->i_time_offset * 0.048 + p_sys->pp_stream[0]->i_pre_skip ) );
+                }
+                else return VLC_EGENERIC;
+            }
+            else
+            {
+                int64_t i_block = p_sys->pp_seekpoints[i_seekpoint]->i_time_offset * p_sys->i_bitrate / INT64_C(8000000);
+                if( stream_Seek( p_demux->s, i_block ) )
+                    return VLC_EGENERIC;
+            }
+            p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
+            p_demux->info.i_seekpoint = i_seekpoint;
             return VLC_SUCCESS;
         }
 
@@ -801,7 +848,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
                                     p_stream->p_es, &p_stream->fmt );
 
                 if( p_stream->i_headers > 0 )
-                    Ogg_ExtractMeta( p_demux, p_stream->fmt.i_codec,
+                    Ogg_ExtractMeta( p_demux, & p_stream->fmt,
                                      p_stream->p_headers, p_stream->i_headers );
 
                 /* we're not at BOS anymore for this logical stream */
@@ -921,8 +968,8 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     if( i_pts == VLC_TS_INVALID ) i_pts = VLC_TS_0;
     else if( i_pts == -1 && i_interpolated_pts == VLC_TS_INVALID )
         i_pts = VLC_TS_0;
-    else if( i_pts == -1 && p_stream->fmt.i_cat == VIDEO_ES )
-        i_pts = i_interpolated_pts;
+    else if( i_pts == -1 && (p_stream->fmt.i_cat == VIDEO_ES || p_stream->fmt.i_codec == VLC_CODEC_OPUS) )
+        i_pts = i_interpolated_pts; /* FIXME : why is this incorrect for vorbis? */
     else if( i_pts == -1 ) i_pts = VLC_TS_INVALID;
 
     if( p_stream->fmt.i_cat == AUDIO_ES )
@@ -1109,13 +1156,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 TAB_APPEND( p_ogg->i_streams, p_ogg->pp_stream, p_stream );
 
                 memset( p_stream, 0, sizeof(logical_stream_t) );
-                p_stream->p_headers = 0;
-                p_stream->i_secondary_header_packets = 0;
-
-                p_stream->i_keyframe_offset = 0;
-                p_stream->i_skip_frames = 0;
-
-                p_stream->i_data_start = 0;
 
                 es_format_Init( &p_stream->fmt, 0, 0 );
                 es_format_Init( &p_stream->fmt_old, 0, 0 );
@@ -1330,6 +1370,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         i_format_tag = GetWLE((oggpacket.packet+124));
                         p_stream->fmt.audio.i_channels =
                             GetWLE((oggpacket.packet+126));
+                        fill_channels_info(&p_stream->fmt.audio);
                         p_stream->f_rate = p_stream->fmt.audio.i_rate =
                             GetDWLE((oggpacket.packet+128));
                         p_stream->fmt.i_bitrate =
@@ -1453,6 +1494,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         p_buffer[4] = '\0';
                         i_format_tag = strtol(p_buffer,NULL,16);
                         p_stream->fmt.audio.i_channels = st->sh.audio.channels;
+                        fill_channels_info(&p_stream->fmt.audio);
                         if( st->time_unit <= 0 )
                             st->time_unit = 10000000;
                         p_stream->f_rate = p_stream->fmt.audio.i_rate = st->samples_per_unit * 10000000 / st->time_unit;
@@ -1611,7 +1653,12 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
             es_out_Control( p_demux->out, ES_OUT_SET_ES, p_stream->p_es );
         }
 
-        p_ogg->i_bitrate += p_stream->fmt.i_bitrate;
+        if ( p_stream->fmt.i_bitrate == 0  &&
+             ( p_stream->fmt.i_cat == VIDEO_ES ||
+               p_stream->fmt.i_cat == AUDIO_ES ) )
+            p_ogg->b_partial_bitrate = true;
+        else
+            p_ogg->i_bitrate += p_stream->fmt.i_bitrate;
 
         p_stream->i_pcr = p_stream->i_previous_pcr =
             p_stream->i_interpolated_pcr = -1;
@@ -1814,7 +1861,8 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *
 
     return !b_compatible;
 }
-static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsigned i_headers, unsigned i_skip )
+static void Ogg_ExtractXiphMeta( demux_t *p_demux, es_format_t *p_fmt,
+                                 const void *p_headers, unsigned i_headers, unsigned i_skip )
 {
     demux_sys_t *p_ogg = p_demux->p_sys;
 
@@ -1829,10 +1877,18 @@ static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsign
     {
         int i_cover_score = 0;
         int i_cover_idx = 0;
+        float pf_replay_gain[AUDIO_REPLAY_GAIN_MAX];
+        float pf_replay_peak[AUDIO_REPLAY_GAIN_MAX];
+        for(int i=0; i< AUDIO_REPLAY_GAIN_MAX; i++ )
+        {
+            pf_replay_gain[i] = 0;
+            pf_replay_peak[i] = 0;
+        }
         vorbis_ParseComment( &p_ogg->p_meta, (uint8_t*)pp_data[1] + i_skip, pi_size[1] - i_skip,
                              &p_ogg->i_attachments, &p_ogg->attachments,
                              &i_cover_score, &i_cover_idx,
-                             &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints );
+                             &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints,
+                             &pf_replay_gain, &pf_replay_peak );
         if( p_ogg->p_meta != NULL && i_cover_idx < p_ogg->i_attachments )
         {
             char psz_url[128];
@@ -1840,6 +1896,22 @@ static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsign
                 p_ogg->attachments[i_cover_idx]->psz_name );
             vlc_meta_Set( p_ogg->p_meta, vlc_meta_ArtworkURL, psz_url );
         }
+
+        for ( int i=0; i<AUDIO_REPLAY_GAIN_MAX;i++ )
+        {
+            if ( pf_replay_gain[i] != 0 )
+            {
+                p_fmt->audio_replay_gain.pb_gain[i] = true;
+                p_fmt->audio_replay_gain.pf_gain[i] = pf_replay_gain[i];
+                msg_Dbg( p_demux, "setting replay gain %d to %f", i, pf_replay_gain[i] );
+            }
+            if ( pf_replay_peak[i] != 0 )
+            {
+                p_fmt->audio_replay_gain.pb_peak[i] = true;
+                p_fmt->audio_replay_gain.pf_peak[i] = pf_replay_peak[i];
+                msg_Dbg( p_demux, "setting replay peak %d to %f", i, pf_replay_gain[i] );
+            }
+        }
     }
 
     if( p_ogg->i_seekpoints > 1 )
@@ -1850,33 +1922,33 @@ static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsign
     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 )
+static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers )
 {
     demux_sys_t *p_ogg = p_demux->p_sys;
 
-    switch( i_codec )
+    switch( p_fmt->i_codec )
     {
     /* 3 headers with the 2° one being the comments */
     case VLC_CODEC_VORBIS:
     case VLC_CODEC_THEORA:
-        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 );
+        Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 1+6 );
         break;
     case VLC_CODEC_OPUS:
-        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 8 );
+        Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 8 );
         break;
     case VLC_CODEC_SPEEX:
-        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 0 );
+        Ogg_ExtractXiphMeta( p_demux, p_fmt, 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 bytes for magic, 1 reserved zero byte */
-        Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+7+1 );
+        Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 1+7+1 );
         break;
 
     /* TODO */
     case VLC_CODEC_FLAC:
-        msg_Warn( p_demux, "Ogg_ExtractMeta does not support %4.4s", (const char*)&i_codec );
+        msg_Warn( p_demux, "Ogg_ExtractMeta does not support %4.4s", (const char*)&p_fmt->i_codec );
         break;
 
     /* No meta data */
@@ -1988,6 +2060,7 @@ static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream,
     oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
     oggpack_adv( &opb, 88 );
     p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
+    fill_channels_info(&p_stream->fmt.audio);
     p_stream->f_rate = p_stream->fmt.audio.i_rate =
         oggpack_read( &opb, 32 );
     oggpack_adv( &opb, 32 );
@@ -2023,6 +2096,7 @@ static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
     oggpack_adv( &opb, 32 ); /* mode */
     oggpack_adv( &opb, 32 ); /* mode_bitstream_version */
     p_stream->fmt.audio.i_channels = oggpack_read( &opb, 32 );
+    fill_channels_info(&p_stream->fmt.audio);
     p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 );
 }
 
@@ -2050,6 +2124,7 @@ static void Ogg_ReadOpusHeader( demux_t *p_demux,
     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 ( p_demux->p_sys->i_length < 0 )
@@ -2069,30 +2144,29 @@ static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream,
     bs_init( &s, p_oggpacket->packet, p_oggpacket->bytes );
 
     bs_read( &s, 1 );
-    if( p_oggpacket->bytes > 0 && bs_read( &s, 7 ) == 0 )
+    if( p_oggpacket->bytes > 0 && bs_read( &s, 7 ) != 0 )
     {
-        if( bs_read( &s, 24 ) >= 34 /*size STREAMINFO*/ )
-        {
-            bs_skip( &s, 80 );
-            p_stream->f_rate = p_stream->fmt.audio.i_rate = bs_read( &s, 20 );
-            p_stream->fmt.audio.i_channels = bs_read( &s, 3 ) + 1;
+        msg_Dbg( p_demux, "Invalid FLAC STREAMINFO metadata" );
+        return;
+    }
 
-            msg_Dbg( p_demux, "FLAC header, channels: %i, rate: %i",
-                     p_stream->fmt.audio.i_channels, (int)p_stream->f_rate );
-        }
-        else
-        {
-            msg_Dbg( p_demux, "FLAC STREAMINFO metadata too short" );
-        }
+    if( bs_read( &s, 24 ) >= 34 /*size STREAMINFO*/ )
+    {
+        bs_skip( &s, 80 );
+        p_stream->f_rate = p_stream->fmt.audio.i_rate = bs_read( &s, 20 );
+        p_stream->fmt.audio.i_channels = bs_read( &s, 3 ) + 1;
+        fill_channels_info(&p_stream->fmt.audio);
 
-        /* Fake this as the last metadata block */
-        *((uint8_t*)p_oggpacket->packet) |= 0x80;
+        msg_Dbg( p_demux, "FLAC header, channels: %i, rate: %i",
+                 p_stream->fmt.audio.i_channels, (int)p_stream->f_rate );
     }
     else
     {
-        /* This ain't a STREAMINFO metadata */
-        msg_Dbg( p_demux, "Invalid FLAC STREAMINFO metadata" );
+        msg_Dbg( p_demux, "FLAC STREAMINFO metadata too short" );
     }
+
+    /* Fake this as the last metadata block */
+    *((uint8_t*)p_oggpacket->packet) |= 0x80;
 }
 
 static void Ogg_ReadKateHeader( logical_stream_t *p_stream,