]> git.sesse.net Git - vlc/blobdiff - modules/demux/ogg.c
httplive: Adjust conditions for playlist reloading
[vlc] / modules / demux / ogg.c
index 0fa65b0dec115975e9e2388e3d2b5538bbaf38d0..9c858c861123532cfd9a62e3210f5f2ba25e7452 100644 (file)
@@ -137,6 +137,7 @@ static void Ogg_ReadKateHeader( logical_stream_t *, ogg_packet * );
 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 bool Ogg_ReadVP8Header( demux_t *, logical_stream_t *, ogg_packet * );
 static void Ogg_ReadSkeletonHeader( demux_t *, logical_stream_t *, ogg_packet * );
 
 /* Skeleton */
@@ -173,6 +174,10 @@ static void fill_channels_info(audio_format_t *audio)
         audio->i_original_channels = pi_channels_map[chans];
 }
 
+/* Special TS value: don't send or derive any pts/pcr from it.
+   Represents TS state prior first known valid timestamp */
+#define VLC_TS_UNKNOWN (VLC_TS_INVALID - 1)
+
 /*****************************************************************************
  * Open: initializes ogg demux structures
  *****************************************************************************/
@@ -280,25 +285,17 @@ static int Demux( demux_t * p_demux )
             stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_canseek );
             if ( b_canseek )
                 Oggseek_ProbeEnd( p_demux );
-            es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 );
         }
         else
         {
-            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
             p_sys->b_chained_boundary = false;
         }
     }
 
-    if ( p_sys->b_preparsing_done )
+    if ( p_sys->b_preparsing_done && !p_sys->b_es_created )
     {
-        for ( int i=0; i < p_sys->i_streams; i++ )
-        {
-            if ( !p_sys->pp_stream[i]->p_es )
-            {
-                Ogg_CreateES( p_demux );
-                break;
-            }
-        }
+        Ogg_CreateES( p_demux );
+        p_sys->b_es_created = true;
     }
 
     /*
@@ -336,17 +333,7 @@ static int Demux( demux_t * p_demux )
                     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
                     {
                         logical_stream_t *p_stream = p_sys->pp_stream[i_stream];
-                        if ( p_stream->b_have_updated_format )
-                        {
-                            p_stream->b_have_updated_format = false;
-                            if ( p_stream->p_skel ) Ogg_ApplySkeleton( p_stream );
-                            if ( p_stream->p_es )
-                            {
-                                msg_Dbg( p_demux, "Resetting format for stream %d", i_stream );
-                                es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
-                                                p_stream->p_es, &p_stream->fmt );
-                            }
-                        }
+                        Ogg_ApplySkeleton( p_stream );
                     }
                 }
             }
@@ -382,8 +369,8 @@ static int Demux( demux_t * p_demux )
                 ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) );
 
                 p_stream->b_reinit = true;
-                p_stream->i_pcr = VLC_TS_0;
-                p_stream->i_interpolated_pcr = VLC_TS_0;
+                p_stream->i_pcr = VLC_TS_UNKNOWN;
+                p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
                 p_stream->i_previous_granulepos = -1;
                 es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0);
             }
@@ -504,6 +491,7 @@ static int Demux( demux_t * p_demux )
                 if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
                     p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
                     p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
+                    p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
                     p_stream->fmt.i_codec == VLC_CODEC_FLAC )
                 {
                     if( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
@@ -544,14 +532,20 @@ static int Demux( demux_t * p_demux )
     /* if a page was waiting, it's now processed */
     p_sys->b_page_waiting = false;
 
-    p_sys->b_preparsing_done = true;
+    if ( p_sys->p_skelstream && !p_sys->p_skelstream->b_finished )
+        p_sys->b_preparsing_done = false;
+    else
+        p_sys->b_preparsing_done = true;
 
-    p_sys->i_pcr = -1;
+    /* We will consider the lowest PCR among tracks, because the audio core badly
+     * handles PCR rewind (mute)
+     */
+    mtime_t i_pcr_candidate = VLC_TS_INVALID;
     for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
     {
         logical_stream_t *p_stream = p_sys->pp_stream[i_stream];
 
-        if ( p_stream->b_force_backup )
+        if ( p_sys->b_preparsing_done && p_stream->b_initializing )
         {
             /* We have 1 or more streams needing more than 1 page for preparsing */
             p_sys->b_preparsing_done = false;
@@ -559,15 +553,24 @@ static int Demux( demux_t * p_demux )
 
         if( p_stream->fmt.i_cat == SPU_ES )
             continue;
-        if( p_stream->i_interpolated_pcr < 0 )
+        if( p_stream->i_interpolated_pcr < VLC_TS_0 )
+            continue;
+        if ( p_stream->b_finished || p_stream->b_initializing )
             continue;
 
-        if( p_sys->i_pcr < 0 || p_stream->i_interpolated_pcr < p_sys->i_pcr )
-            p_sys->i_pcr = p_stream->i_interpolated_pcr;
+        if( i_pcr_candidate < VLC_TS_0
+            || p_stream->i_interpolated_pcr <= i_pcr_candidate )
+        {
+            i_pcr_candidate = p_stream->i_interpolated_pcr;
+        }
     }
 
-    if( p_sys->i_pcr >= 0 && ! b_skipping )
-        es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
+    if ( i_pcr_candidate > VLC_TS_INVALID && p_sys->i_pcr != i_pcr_candidate )
+    {
+        p_sys->i_pcr = i_pcr_candidate;
+        if( ! b_skipping )
+            es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
+    }
 
     return 1;
 }
@@ -580,8 +583,8 @@ static void Ogg_ResetStreamHelper( demux_sys_t *p_sys )
 
         /* we'll trash all the data until we find the next pcr */
         p_stream->b_reinit = true;
-        p_stream->i_pcr = -1;
-        p_stream->i_interpolated_pcr = -1;
+        p_stream->i_pcr = VLC_TS_UNKNOWN;
+        p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
         p_stream->i_previous_granulepos = -1;
         ogg_stream_reset( &p_stream->os );
     }
@@ -595,6 +598,7 @@ static logical_stream_t * Ogg_GetSelectedStream( demux_t *p_demux )
     for( int i=0; i<p_sys->i_streams; i++ )
     {
         logical_stream_t *p_candidate = p_sys->pp_stream[i];
+        if ( !p_candidate->p_es ) continue;
 
         bool b_selected = false;
         es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE,
@@ -850,10 +854,17 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
     p_stream->i_end_trim = 0;
 
     /* Convert the granulepos into a pcr */
-    if( p_oggpacket->granulepos >= 0 )
+    if ( p_oggpacket->granulepos == 0 )
+    {
+        /* We're in headers, and we haven't parsed 1st data packet yet */
+        p_stream->i_pcr = VLC_TS_UNKNOWN;
+        p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
+    }
+    else if( p_oggpacket->granulepos > 0 )
     {
         if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
             p_stream->fmt.i_codec == VLC_CODEC_KATE ||
+            p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
             p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
         {
             p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
@@ -893,7 +904,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
     else
     {
         int duration;
-        p_stream->i_pcr = -1;
+        p_stream->i_pcr = VLC_TS_INVALID;
 
         /* no granulepos available, try to interpolate the pcr.
          * If we can't then don't touch the old value. */
@@ -917,7 +928,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
                 VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate;
             p_stream->i_interpolated_pcr += p_ogg->i_pcr_offset;
         }
-        else if( p_stream->fmt.i_bitrate )
+        else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN )
         {
             p_stream->i_interpolated_pcr +=
                 ( p_oggpacket->bytes * CLOCK_FREQ /
@@ -938,7 +949,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     block_t *p_block;
     bool b_selected;
     int i_header_len = 0;
-    mtime_t i_pts = -1, i_interpolated_pts;
+    mtime_t i_pts = VLC_TS_UNKNOWN, i_interpolated_pts;
     demux_sys_t *p_ogg = p_demux->p_sys;
 
     if( p_oggpacket->bytes >= 7 &&
@@ -965,6 +976,13 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         Ogg_ReadSkeletonIndex( p_demux, p_oggpacket );
         return;
     }
+    else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 &&
+             p_oggpacket->bytes >= 7 &&
+             !memcmp( p_oggpacket->packet, "OVP80\x02\x20", 7 ) )
+    {
+        Ogg_ReadVP8Header( p_demux, p_stream, p_oggpacket );
+        return;
+    }
 
     if( p_stream->fmt.i_codec == VLC_CODEC_SUBT && p_oggpacket->bytes > 0 &&
         p_oggpacket->packet[0] & PACKET_TYPE_BITS ) return;
@@ -1071,25 +1089,6 @@ static void Ogg_DecodePacket( demux_t *p_demux,
                 else
                     p_stream->fmt.i_extra = 0;
 
-                if( Ogg_LogicalStreamResetEsFormat( p_demux, p_stream ) )
-                {
-                    if ( p_ogg->p_skelstream )
-                    {
-                        /* We delay until eos is reached on skeleton.
-                         * There should only be headers, as no data page is
-                         * allowed before skeleton's eos.
-                         * Skeleton data is appended to fmt on skeleton eos.
-                         */
-                        p_stream->b_have_updated_format = true;
-                    }
-                    else
-                    {
-                        if ( p_stream->p_es )
-                        /* Otherwise we set config from first headers */
-                        es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
-                                        p_stream->p_es, &p_stream->fmt );
-                    }
-                }
                 if( p_stream->i_headers > 0 )
                     Ogg_ExtractMeta( p_demux, & p_stream->fmt,
                                      p_stream->p_headers, p_stream->i_headers );
@@ -1108,23 +1107,12 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
         p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
         p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
+        p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
         p_stream->fmt.i_codec == VLC_CODEC_FLAC )
     {
-        if( p_stream->i_pcr >= 0 )
+        if( p_stream->i_pcr > VLC_TS_INVALID )
         {
-            /* This is for streams where the granulepos of the header packets
-             * doesn't match these of the data packets (eg. ogg web radios). */
-            if( p_stream->i_previous_pcr == 0 &&
-                p_stream->i_pcr  > 3 * DEFAULT_PTS_DELAY )
-            {
-
-                /* Call the pace control */
-                es_out_Control( p_demux->out, ES_OUT_SET_PCR,
-                                VLC_TS_0 + p_stream->i_pcr );
-            }
-
             p_stream->i_previous_pcr = p_stream->i_pcr;
-
             /* The granulepos is the end date of the sample */
             i_pts = p_stream->i_pcr;
         }
@@ -1134,26 +1122,10 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     i_interpolated_pts = p_stream->i_interpolated_pcr;
     Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket );
 
-    /* SPU streams are typically discontinuous, do not mind large gaps */
-    if( p_stream->fmt.i_cat != SPU_ES )
-    {
-        if( p_stream->i_pcr >= 0 )
-        {
-            /* This is for streams where the granulepos of the header packets
-             * doesn't match these of the data packets (eg. ogg web radios). */
-            if( p_stream->i_previous_pcr == 0 &&
-                p_stream->i_pcr  > 3 * DEFAULT_PTS_DELAY )
-            {
-
-                /* Call the pace control */
-                es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_stream->i_pcr );
-            }
-        }
-    }
-
     if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
         p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
         p_stream->fmt.i_codec != VLC_CODEC_OPUS &&
+        p_stream->fmt.i_codec != VLC_CODEC_VP8 &&
         p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
         p_stream->i_pcr >= 0 )
     {
@@ -1208,14 +1180,9 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
         p_block->i_nb_samples = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
 
-
     /* Normalize PTS */
-    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 || 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( i_pts == VLC_TS_UNKNOWN )
+        i_pts = VLC_TS_INVALID;
 
     if( p_stream->fmt.i_cat == AUDIO_ES )
     {
@@ -1251,6 +1218,15 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         if( -1 != p_oggpacket->granulepos )
             p_block->i_pts = u_pnum * CLOCK_FREQ / p_stream->f_rate / 2;
     }
+    else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
+    {
+        p_block->i_pts = p_stream->i_interpolated_pcr;
+        p_block->i_dts = p_block->i_pts;
+        if( p_oggpacket->granulepos > 0 && Ogg_IsKeyFrame( p_stream, p_oggpacket ) )
+        {
+            p_block->i_flags |= BLOCK_FLAG_TYPE_I;
+        }
+    }
     else
     {
         p_block->i_dts = i_pts;
@@ -1260,6 +1236,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
         p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
         p_stream->fmt.i_codec != VLC_CODEC_OPUS &&
+        p_stream->fmt.i_codec != VLC_CODEC_VP8 &&
         p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
         p_stream->fmt.i_codec != VLC_CODEC_TARKIN &&
         p_stream->fmt.i_codec != VLC_CODEC_THEORA &&
@@ -1307,11 +1284,6 @@ static void Ogg_DecodePacket( demux_t *p_demux,
             p_block->i_buffer = 0;
     }
 
-    if ( p_stream->b_reusing )
-    {
-        p_stream->b_reusing = false;
-        p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
-    }
 
     if( p_stream->fmt.i_codec == VLC_CODEC_TARKIN )
     {
@@ -1324,6 +1296,12 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     memcpy( p_block->p_buffer, p_oggpacket->packet + i_header_len,
             p_oggpacket->bytes - i_header_len );
 
+    if ( p_ogg->i_streams == 1 && p_block->i_pts > VLC_TS_INVALID && p_ogg->i_pcr < VLC_TS_0 )
+    {
+        p_ogg->i_pcr = p_block->i_pts;
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_ogg->i_pcr );
+    }
+
     if ( p_stream->p_es )
     {
         /* Because ES creation is delayed for preparsing */
@@ -1421,6 +1399,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
 
                 es_format_Init( &p_stream->fmt, 0, 0 );
                 es_format_Init( &p_stream->fmt_old, 0, 0 );
+                p_stream->b_initializing = true;
 
                 /* Setup the logical stream */
                 p_stream->i_serial_no = ogg_page_serialno( &p_ogg->current_page );
@@ -1545,6 +1524,19 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                              "found tarkin header, bitrate: %i, rate: %f",
                              p_stream->fmt.i_bitrate, p_stream->f_rate );
                 }
+                /* Check for VP8 header */
+                else if( oggpacket.bytes >= 26 &&
+                         ! memcmp( oggpacket.packet, "OVP80", 5 ) )
+                {
+                    if ( Ogg_ReadVP8Header( p_demux, p_stream, &oggpacket ) )
+                        msg_Dbg( p_demux, "found VP8 header "
+                             "fps: %f, width:%i; height:%i",
+                             p_stream->f_rate,
+                             p_stream->fmt.video.i_width,
+                             p_stream->fmt.video.i_height );
+                    else
+                        p_stream->fmt.i_cat = UNKNOWN_ES;
+                }
                 /* Check for Annodex header */
                 else if( oggpacket.bytes >= 7 &&
                          ! memcmp( oggpacket.packet, "Annodex", 7 ) )
@@ -1826,7 +1818,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 }
 
                 /* we'll need to get all headers */
-                p_ogg->pp_stream[i_stream]->b_initializing |= p_ogg->pp_stream[i_stream]->b_force_backup;
+                p_ogg->pp_stream[i_stream]->b_initializing &= p_ogg->pp_stream[i_stream]->b_force_backup;
 
                 if( Ogg_ReadPage( p_demux, &p_ogg->current_page ) != VLC_SUCCESS )
                     return VLC_EGENERIC;
@@ -1865,7 +1857,7 @@ static void Ogg_CreateES( demux_t *p_demux )
     {
         logical_stream_t *p_stream = p_ogg->pp_stream[i_stream];
 
-        if ( p_stream->p_es == NULL )
+        if ( p_stream->p_es == NULL && !p_stream->b_finished )
         {
             /* Better be safe than sorry when possible with ogm */
             if( p_stream->fmt.i_codec == VLC_CODEC_MPGA ||
@@ -1883,13 +1875,16 @@ static void Ogg_CreateES( demux_t *p_demux )
                 p_stream->b_finished = false;
                 p_stream->b_reinit = false;
                 p_stream->b_initializing = false;
-                p_stream->b_reusing = true;
+                bool b_resetdecoder = Ogg_LogicalStreamResetEsFormat( p_demux, p_stream );
                 es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt );
 
                 p_old_stream->p_es = NULL;
                 p_old_stream = NULL;
-                es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
-                                p_stream->p_es, &p_stream->fmt );
+                if ( b_resetdecoder )
+                {
+                    es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
+                                    p_stream->p_es, &p_stream->fmt );
+                }
             }
             else
             {
@@ -1950,7 +1945,7 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
             p_ogg->i_bitrate += p_stream->fmt.i_bitrate;
 
         p_stream->i_pcr = p_stream->i_previous_pcr =
-            p_stream->i_interpolated_pcr = -1;
+            p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
         p_stream->b_reinit = false;
     }
 
@@ -1979,6 +1974,7 @@ static void Ogg_EndOfStream( demux_t *p_demux )
     p_ogg->skeleton.major = 0;
     p_ogg->skeleton.minor = 0;
     p_ogg->b_preparsing_done = false;
+    p_ogg->b_es_created = false;
     p_ogg->i_pcr_offset = p_ogg->i_pcr;
 
     /* */
@@ -2158,56 +2154,46 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *
 
     return !b_compatible;
 }
-static void Ogg_ExtractXiphMeta( demux_t *p_demux, es_format_t *p_fmt,
-                                 const void *p_headers, unsigned i_headers, unsigned i_skip )
+
+static void Ogg_ExtractComments( demux_t *p_demux, es_format_t *p_fmt,
+                                 const void *p_headers, unsigned i_headers )
 {
     demux_sys_t *p_ogg = p_demux->p_sys;
+    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, p_headers, i_headers,
+                         &p_ogg->i_attachments, &p_ogg->attachments,
+                         &i_cover_score, &i_cover_idx,
+                         &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];
+        snprintf( psz_url, sizeof(psz_url), "attachment://%s",
+                  p_ogg->attachments[i_cover_idx]->psz_name );
+        vlc_meta_Set( p_ogg->p_meta, vlc_meta_ArtworkURL, psz_url );
+    }
 
-    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 ? */
-    if( i_count >= 2 && pi_size[1] > i_skip )
+    for ( int i=0; i<AUDIO_REPLAY_GAIN_MAX;i++ )
     {
-        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,
-                             &pf_replay_gain, &pf_replay_peak );
-        if( p_ogg->p_meta != NULL && i_cover_idx < p_ogg->i_attachments )
+        if ( pf_replay_gain[i] != 0 )
         {
-            char psz_url[128];
-            snprintf( psz_url, sizeof(psz_url), "attachment://%s",
-                p_ogg->attachments[i_cover_idx]->psz_name );
-            vlc_meta_Set( p_ogg->p_meta, vlc_meta_ArtworkURL, psz_url );
+            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] );
         }
-
-        for ( int i=0; i<AUDIO_REPLAY_GAIN_MAX;i++ )
+        if ( pf_replay_peak[i] != 0 )
         {
-            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] );
-            }
+            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] );
         }
     }
 
@@ -2216,6 +2202,23 @@ static void Ogg_ExtractXiphMeta( demux_t *p_demux, es_format_t *p_fmt,
         p_demux->info.i_update |= INPUT_UPDATE_TITLE_LIST;
     }
 }
+
+static void Ogg_ExtractXiphMeta( demux_t *p_demux, es_format_t *p_fmt,
+                                 const void *p_headers, unsigned i_headers, unsigned 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 ? */
+    if( i_count >= 2 && pi_size[1] > i_skip )
+    {
+        Ogg_ExtractComments( p_demux, p_fmt, (uint8_t*)pp_data[1] + i_skip, pi_size[1] - i_skip );
+    }
+}
+
 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;
@@ -2233,7 +2236,9 @@ static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t
     case VLC_CODEC_SPEEX:
         Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 0 );
         break;
-
+    case VLC_CODEC_VP8:
+        Ogg_ExtractComments( p_demux, p_fmt, p_headers, i_headers );
+        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 */
@@ -2345,7 +2350,8 @@ static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream,
     p_stream->f_rate = p_stream->fmt.audio.i_rate =
         oggpack_read( &opb, 32 );
     oggpack_adv( &opb, 32 );
-    p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 );
+    p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); /* is signed 32 */
+    if( p_stream->fmt.i_bitrate > INT32_MAX ) p_stream->fmt.i_bitrate = 0;
 }
 
 static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
@@ -2501,6 +2507,37 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream,
     }
 }
 
+static bool Ogg_ReadVP8Header( demux_t *p_demux, logical_stream_t *p_stream,
+                               ogg_packet *p_oggpacket )
+{
+    switch( p_oggpacket->packet[5] )
+    {
+    /* STREAMINFO */
+    case 0x01:
+        /* Mapping version */
+        if ( p_oggpacket->packet[6] != 0x01 || p_oggpacket->packet[7] != 0x00 )
+            return false;
+        p_stream->fmt.i_cat = VIDEO_ES;
+        p_stream->fmt.i_codec = VLC_CODEC_VP8;
+        p_stream->i_granule_shift = 32;
+        p_stream->fmt.video.i_width = GetWBE( &p_oggpacket->packet[8] );
+        p_stream->fmt.video.i_height = GetWBE( &p_oggpacket->packet[10] );
+        p_stream->fmt.video.i_sar_num = GetDWBE( &p_oggpacket->packet[12 - 1] ) & 0x0FFF;
+        p_stream->fmt.video.i_sar_den = GetDWBE( &p_oggpacket->packet[15 - 1] ) & 0x0FFF;
+        p_stream->fmt.video.i_frame_rate = GetDWBE( &p_oggpacket->packet[18] );
+        p_stream->fmt.video.i_frame_rate_base = GetDWBE( &p_oggpacket->packet[22] );
+        p_stream->f_rate = (double) p_stream->fmt.video.i_frame_rate / p_stream->fmt.video.i_frame_rate_base;
+        return true;
+    /* METADATA */
+    case 0x02:
+        Ogg_ExtractMeta( p_demux, & p_stream->fmt,
+                         p_oggpacket->packet + 7, p_oggpacket->bytes - 7 );
+        return true;
+    default:
+        return false;
+    }
+}
+
 static void Ogg_ApplyContentType( logical_stream_t *p_stream, const char* psz_value,
                                   bool *b_force_backup, bool *b_packet_out )
 {
@@ -2569,6 +2606,11 @@ static void Ogg_ApplyContentType( logical_stream_t *p_stream, const char* psz_va
         free( p_stream->fmt.psz_description );
         p_stream->fmt.psz_description = strdup("OGG Kate Overlay (Unsupported)");
     }
+    else if( !strncmp(psz_value, "video/x-vp8", 11) )
+    {
+        p_stream->fmt.i_cat = VIDEO_ES;
+        p_stream->fmt.i_codec = VLC_CODEC_VP8;
+    }
 }
 
 static void Ogg_ReadAnnodexHeader( demux_t *p_demux,