]> git.sesse.net Git - vlc/blobdiff - modules/demux/ogg.c
MKV: the Block duration is for the whole Block, not each frame
[vlc] / modules / demux / ogg.c
index b6de5df6e28c393f546554329833a79beeef20cc..8326b4e0ef35fdbbf59332ff1730990f2c4bd894 100644 (file)
@@ -44,6 +44,7 @@
 #include "xiph_metadata.h"
 #include "ogg.h"
 #include "oggseek.h"
+#include "opus.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -106,8 +107,11 @@ typedef struct
 } stream_header_t;
 
 #define VORBIS_HEADER_IDENTIFICATION 1
-#define VORBIS_HEADER_COMMENT 2
-#define VORBIS_HEADER_SETUP 3
+#define VORBIS_HEADER_COMMENT        2
+#define VORBIS_HEADER_SETUP          3
+#define VORBIS_HEADER_TO_FLAG(i)     (1 << (i - 1))
+#define VORBIS_HEADERS_VALID(p_stream) \
+    ((p_stream->special.vorbis.i_headers_flags & 0x07) == 0x07) // 0b111
 
 /*****************************************************************************
  * Local prototypes
@@ -119,7 +123,7 @@ static int  Control( demux_t *, int, va_list );
 static int  Ogg_ReadPage     ( demux_t *, ogg_page * );
 static void Ogg_UpdatePCR    ( demux_t *, logical_stream_t *, ogg_packet * );
 static void Ogg_DecodePacket ( demux_t *, logical_stream_t *, ogg_packet * );
-static int  Ogg_OpusPacketDuration( logical_stream_t *, ogg_packet * );
+static int  Ogg_OpusPacketDuration( ogg_packet * );
 static void Ogg_SendOrQueueBlocks( demux_t *, logical_stream_t *, block_t * );
 
 static void Ogg_CreateES( demux_t *p_demux );
@@ -136,6 +140,7 @@ static void Ogg_ResetStream( logical_stream_t *p_stream );
 static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers );
 
 /* Logical bitstream headers */
+static bool Ogg_ReadDaalaHeader( logical_stream_t *, ogg_packet * );
 static bool Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * );
 static bool Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * );
 static bool Ogg_ReadSpeexHeader( logical_stream_t *, ogg_packet * );
@@ -204,7 +209,19 @@ static int Open( vlc_object_t * p_this )
     if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC;
     if( !p_demux->b_force && memcmp( p_peek, "OggS", 4 ) )
     {
-        return VLC_EGENERIC;
+        char *psz_mime = stream_ContentType( p_demux->s );
+        if( !psz_mime )
+        {
+            return VLC_EGENERIC;
+        }
+        else if ( strcmp( psz_mime, "application/ogg" ) &&
+                  strcmp( psz_mime, "video/ogg" ) &&
+                  strcmp( psz_mime, "audio/ogg" ) )
+        {
+            free( psz_mime );
+            return VLC_EGENERIC;
+        }
+        free( psz_mime );
     }
 
     /* */
@@ -290,7 +307,7 @@ static int Demux( demux_t * p_demux )
         }
 
         if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS )
-            return 0;
+            return VLC_DEMUXER_EOF;
 
         msg_Dbg( p_demux, "beginning of a group of logical streams" );
 
@@ -333,7 +350,7 @@ static int Demux( demux_t * p_demux )
          * Demux an ogg page from the stream
          */
         if( Ogg_ReadPage( p_demux, &p_sys->current_page ) != VLC_SUCCESS )
-            return 0; /* EOF */
+            return VLC_DEMUXER_EOF; /* EOF */
         /* Test for End of Stream */
         if( ogg_page_eos( &p_sys->current_page ) )
         {
@@ -382,6 +399,8 @@ static int Demux( demux_t * p_demux )
             {
                 msg_Err( p_demux, "Broken Ogg stream (serialno) mismatch" );
                 Ogg_ResetStream( p_stream );
+                p_sys->i_nzpcr_offset = (p_sys->i_pcr >= VLC_TS_INVALID) ?
+                                         p_sys->i_pcr - VLC_TS_0 : 0;
                 ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) );
             }
 
@@ -406,6 +425,9 @@ static int Demux( demux_t * p_demux )
                     p_sys->current_page.body_len )
         );
 
+        const int i_page_packets = ogg_page_packets( &p_sys->current_page );
+        bool b_doprepcr = false;
+
         if ( p_stream->i_pcr < VLC_TS_0 && ogg_page_granulepos( &p_sys->current_page ) > 0 )
         {
             // PASS 0
@@ -414,14 +436,32 @@ static int Demux( demux_t * p_demux )
                  p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
                  p_stream->fmt.i_cat == VIDEO_ES )
             {
-                assert( p_stream->p_prepcr_blocks == NULL );
-                p_stream->i_prepcr_blocks = 0;
-                p_stream->p_prepcr_blocks = malloc( sizeof(block_t *) * ogg_page_packets( &p_sys->current_page ) );
+                assert( p_stream->prepcr.pp_blocks == NULL );
+                b_doprepcr = true;
             }
         }
 
+        int i_real_page_packets = 0;
         while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
         {
+            i_real_page_packets++;
+            int i_max_packets = __MAX(i_page_packets, i_real_page_packets);
+            if ( b_doprepcr && p_stream->prepcr.i_size < i_max_packets )
+            {
+                /* always double alloc for performance */
+                i_max_packets = __MAX( i_max_packets << 1, 255 );
+                /* alloc or realloc */
+                block_t **pp_realloc = realloc( p_stream->prepcr.pp_blocks,
+                                                sizeof(block_t *) * i_max_packets );
+                if ( !pp_realloc )
+                {
+                    /* drop it then */
+                    continue;
+                }
+                p_stream->prepcr.i_size = i_max_packets;
+                p_stream->prepcr.pp_blocks = pp_realloc;
+            }
+
             /* Read info from any secondary header packets, if there are any */
             if( p_stream->i_secondary_header_packets > 0 )
             {
@@ -432,6 +472,13 @@ static int Demux( demux_t * p_demux )
                     Ogg_ReadTheoraHeader( p_stream, &oggpacket );
                     p_stream->i_secondary_header_packets = 0;
                 }
+                else if( p_stream->fmt.i_codec == VLC_CODEC_DAALA &&
+                        oggpacket.bytes >= 6 &&
+                        ! memcmp( oggpacket.packet, "\x80""daala", 6 ) )
+                {
+                    Ogg_ReadDaalaHeader( p_stream, &oggpacket );
+                    p_stream->i_secondary_header_packets = 0;
+                }
                 else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
                         oggpacket.bytes >= 7 &&
                         ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) )
@@ -463,10 +510,9 @@ static int Demux( demux_t * p_demux )
             }
 
             Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
-
         }
 
-        if ( p_stream->p_prepcr_blocks )
+        if ( p_stream->prepcr.pp_blocks )
         {
             int64_t pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, ogg_page_granulepos(  &p_sys->current_page ), false );
             p_stream->i_previous_pcr = pagestamp;
@@ -474,13 +520,12 @@ static int Demux( demux_t * p_demux )
             int i_prev_blocksize = 0;
 #endif
             // PASS 1
-            for( int i=0; i<p_stream->i_prepcr_blocks; i++ )
+            for( int i=0; i<p_stream->prepcr.i_used; i++ )
             {
-                block_t *p_block = p_stream->p_prepcr_blocks[i];
+                block_t *p_block = p_stream->prepcr.pp_blocks[i];
                 ogg_packet dumb_packet;
                 dumb_packet.bytes = p_block->i_buffer;
                 dumb_packet.packet = p_block->p_buffer;
-                int i_duration;
 
                 switch( p_stream->fmt.i_codec )
                 {
@@ -489,20 +534,23 @@ static int Demux( demux_t * p_demux )
                             p_stream->special.speex.i_framesperpacket;
                     break;
                 case VLC_CODEC_OPUS:
-                    i_duration = Ogg_OpusPacketDuration( p_stream, &dumb_packet );
-                    p_block->i_nb_samples = i_duration;
+                    p_block->i_nb_samples = Ogg_OpusPacketDuration( &dumb_packet );
                     break;
 #ifdef HAVE_LIBVORBIS
                 case VLC_CODEC_VORBIS:
                 {
+                    if( !VORBIS_HEADERS_VALID(p_stream) )
+                    {
+                        msg_Err( p_demux, "missing vorbis headers, can't compute block size" );
+                        break;
+                    }
                     long i_blocksize = vorbis_packet_blocksize(
                                 p_stream->special.vorbis.p_info, &dumb_packet );
                     if ( i_prev_blocksize )
-                        i_duration = ( i_blocksize + i_prev_blocksize ) / 4;
+                        p_block->i_nb_samples = ( i_blocksize + i_prev_blocksize ) / 4;
                     else
-                        i_duration = i_blocksize / 2;
+                        p_block->i_nb_samples = i_blocksize / 2;
                     i_prev_blocksize = i_blocksize;
-                    p_block->i_nb_samples = i_duration;
                 }
 #endif
                 }
@@ -510,9 +558,9 @@ static int Demux( demux_t * p_demux )
 
             // PASS 2
             bool b_fixed = false;
-            for( int i=p_stream->i_prepcr_blocks - 1; i>=0; i-- )
+            for( int i=p_stream->prepcr.i_used - 1; i>=0; i-- )
             {
-                block_t *p_block = p_stream->p_prepcr_blocks[i];
+                block_t *p_block = p_stream->prepcr.pp_blocks[i];
                 switch( p_stream->fmt.i_codec )
                 {
                 case VLC_CODEC_SPEEX:
@@ -546,8 +594,8 @@ static int Demux( demux_t * p_demux )
                 p_stream->i_previous_granulepos = ogg_page_granulepos( &p_sys->current_page );
             }
 
-            FREENULL( p_stream->p_prepcr_blocks );
-            p_stream->i_prepcr_blocks = 0;
+            FREENULL(p_stream->prepcr.pp_blocks);
+            p_stream->prepcr.i_used = 0;
 
             Ogg_SendOrQueueBlocks( p_demux, p_stream, NULL );
 
@@ -623,7 +671,7 @@ static int Demux( demux_t * p_demux )
         }
     }
 
-    return 1;
+    return VLC_DEMUXER_SUCCESS;
 }
 
 static void Ogg_ResetStream( logical_stream_t *p_stream )
@@ -640,8 +688,9 @@ static void Ogg_ResetStream( logical_stream_t *p_stream )
     p_stream->i_previous_granulepos = -1;
     p_stream->i_previous_pcr = VLC_TS_UNKNOWN;
     ogg_stream_reset( &p_stream->os );
-    FREENULL( p_stream->p_prepcr_blocks );
-    p_stream->i_prepcr_blocks = 0;
+    FREENULL( p_stream->prepcr.pp_blocks );
+    p_stream->prepcr.i_size = 0;
+    p_stream->prepcr.i_used = 0;
 }
 
 static void Ogg_ResetStreamsHelper( demux_sys_t *p_sys )
@@ -924,6 +973,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
     else if( p_oggpacket->granulepos > 0 )
     {
         if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
+            p_stream->fmt.i_codec == VLC_CODEC_DAALA ||
             p_stream->fmt.i_codec == VLC_CODEC_KATE ||
             p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
             p_stream->fmt.i_codec == VLC_CODEC_DIRAC ||
@@ -940,7 +990,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
 
             if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s )
             {
-                int duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
+                int duration = Ogg_OpusPacketDuration( p_oggpacket );
                 if( duration > 0 )
                 {
                     ogg_int64_t end_sample = p_oggpacket->granulepos;
@@ -971,7 +1021,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
 #ifdef HAVE_LIBVORBIS
         else if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
                   p_stream->special.vorbis.p_info &&
-                  !p_stream->special.vorbis.b_invalid &&
+                  VORBIS_HEADERS_VALID(p_stream) &&
                   p_stream->i_previous_granulepos > 0 )
         {
             long i_blocksize = vorbis_packet_blocksize(
@@ -1001,7 +1051,7 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
         else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS &&
                  p_stream->i_previous_granulepos > 0 &&
                  ( i_duration =
-                     Ogg_OpusPacketDuration( p_stream, p_oggpacket ) ) > 0 )
+                     Ogg_OpusPacketDuration( p_oggpacket ) ) > 0 )
         {
             ogg_int64_t sample;
             p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
@@ -1028,13 +1078,13 @@ static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream,
                                    block_t *p_block )
 {
     demux_sys_t *p_ogg = p_demux->p_sys;
-    if ( !p_stream->p_es || p_stream->p_prepcr_blocks || p_stream->i_pcr == VLC_TS_UNKNOWN )
+    if ( !p_stream->p_es || p_stream->prepcr.pp_blocks || p_stream->i_pcr == VLC_TS_UNKNOWN )
     {
         if ( !p_block ) return;
-        if ( p_stream->p_prepcr_blocks )
+        if ( p_stream->prepcr.pp_blocks )
         {
-            assert( p_stream->p_prepcr_blocks );
-            p_stream->p_prepcr_blocks[p_stream->i_prepcr_blocks++] = p_block;
+            assert( p_stream->prepcr.i_size );
+            p_stream->prepcr.pp_blocks[p_stream->prepcr.i_used++] = p_block;
         }
         DemuxDebug( msg_Dbg( p_demux, "block prepcr append > pts %"PRId64" spcr %"PRId64" pcr %"PRId64,
                              p_block->i_pts, p_stream->i_pcr, p_ogg->i_pcr ); )
@@ -1148,6 +1198,12 @@ static void Ogg_DecodePacket( demux_t *p_demux,
             b_xiph = true;
             break;
 
+        case VLC_CODEC_DAALA:
+            if( p_stream->i_packets_backup == 3 )
+                p_stream->b_force_backup = false;
+            b_xiph = true;
+            break;
+
         case VLC_CODEC_SPEEX:
             if( p_stream->i_packets_backup == 2 + p_stream->i_extra_headers_packets )
                 p_stream->b_force_backup = false;
@@ -1263,7 +1319,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
                         p_oggpacket->granulepos, p_stream->i_pcr, p_stream->i_skip_frames); )
 
     if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
-        p_block->i_nb_samples = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
+        p_block->i_nb_samples = Ogg_OpusPacketDuration( p_oggpacket );
 
     /* may need to preroll after a seek or in case of preskip */
     if ( p_stream->i_skip_frames > 0 )
@@ -1297,6 +1353,18 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     }
     else if( p_stream->fmt.i_cat == AUDIO_ES )
     {
+        if ( p_stream->fmt.i_codec == VLC_CODEC_FLAC &&
+             p_stream->p_es && 0 >= p_oggpacket->granulepos &&
+             p_stream->fmt.b_packetized )
+        {
+            /* Handle OggFlac spec violation (multiple frame/packet
+             * by turning on packetizer */
+            msg_Warn( p_demux, "Invalid FLAC in ogg detected. Restarting ES with packetizer." );
+            p_stream->fmt.b_packetized = false;
+            es_out_Del( p_demux->out, p_stream->p_es );
+            p_stream->p_es = es_out_Add( p_demux->out, &p_stream->fmt );
+        }
+
         /* Blatant abuse of the i_length field. */
         p_block->i_length = p_stream->i_end_trim;
     }
@@ -1325,6 +1393,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
         p_stream->fmt.i_codec != VLC_CODEC_TARKIN &&
         p_stream->fmt.i_codec != VLC_CODEC_THEORA &&
+        p_stream->fmt.i_codec != VLC_CODEC_DAALA &&
         p_stream->fmt.i_codec != VLC_CODEC_CMML &&
         p_stream->fmt.i_codec != VLC_CODEC_DIRAC &&
         p_stream->fmt.i_codec != VLC_CODEC_KATE )
@@ -1339,6 +1408,13 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         i_header_len = (*p_oggpacket->packet & PACKET_LEN_BITS01) >> 6;
         i_header_len |= (*p_oggpacket->packet & PACKET_LEN_BITS2) << 1;
 
+        if( i_header_len >= p_oggpacket->bytes )
+        {
+            msg_Dbg( p_demux, "discarding invalid packet" );
+            block_Release( p_block );
+            return;
+        }
+
         if( p_stream->fmt.i_codec == VLC_CODEC_SUBT)
         {
             /* But with subtitles we need to retrieve the duration first */
@@ -1384,51 +1460,9 @@ static void Ogg_DecodePacket( demux_t *p_demux,
     Ogg_SendOrQueueBlocks( p_demux, p_stream, p_block );
 }
 
-/* Re-implemented to avoid linking against libopus from the demuxer. */
-static int Ogg_OpusDataDuration( logical_stream_t *p_stream,
-                                 unsigned char *data, long i_datalen )
-{
-    static const int silk_fs_div[4] = { 6000, 3000, 1500, 1000 };
-    int toc;
-    int nframes;
-    int frame_size;
-    int nsamples;
-    int i_rate;
-    if( i_datalen < 1 )
-        return VLC_EGENERIC;
-    toc = data[0];
-    switch( toc&3 )
-    {
-        case 0:
-            nframes = 1;
-            break;
-        case 1:
-        case 2:
-            nframes = 2;
-            break;
-        default:
-            if( i_datalen < 2 )
-                return VLC_EGENERIC;
-            nframes = data[1]&0x3F;
-            break;
-    }
-    i_rate = (int)p_stream->fmt.audio.i_rate;
-    if( toc&0x80 )
-        frame_size = (i_rate << (toc >> 3 & 3)) / 400;
-    else if( ( toc&0x60 ) == 0x60 )
-        frame_size = i_rate/(100 >> (toc >> 3 & 1));
-    else
-        frame_size = i_rate*60 / silk_fs_div[toc >> 3 & 3];
-    nsamples = nframes*frame_size;
-    if( nsamples*25 > i_rate*3 )
-        return VLC_EGENERIC;
-    return nsamples;
-}
-
-static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
-                                   ogg_packet *p_oggpacket )
+static int Ogg_OpusPacketDuration( ogg_packet *p_oggpacket )
 {
-    return Ogg_OpusDataDuration( p_stream, p_oggpacket->packet, p_oggpacket->bytes );
+    return opus_frame_duration(p_oggpacket->packet, p_oggpacket->bytes);
 }
 
 /****************************************************************************
@@ -1445,7 +1479,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
 {
     demux_sys_t *p_ogg = p_demux->p_sys  ;
     ogg_packet oggpacket;
-    int i_stream = 0;
 
     p_ogg->i_total_length = stream_Size ( p_demux->s );
     msg_Dbg( p_demux, "File length is %"PRId64" bytes", p_ogg->i_total_length );
@@ -1461,16 +1494,12 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
              * We found the beginning of our first logical stream. */
             while( ogg_page_bos( &p_ogg->current_page ) )
             {
-                logical_stream_t *p_stream;
-
-                p_stream = malloc( sizeof(logical_stream_t) );
+                logical_stream_t *p_stream = calloc( 1, sizeof(logical_stream_t) );
                 if( unlikely( !p_stream ) )
                     return VLC_ENOMEM;
 
                 TAB_APPEND( p_ogg->i_streams, p_ogg->pp_stream, p_stream );
 
-                memset( p_stream, 0, sizeof(logical_stream_t) );
-
                 es_format_Init( &p_stream->fmt, 0, 0 );
                 es_format_Init( &p_stream->fmt_old, 0, 0 );
                 p_stream->b_initializing = true;
@@ -1502,6 +1531,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "found invalid vorbis header" );
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1520,6 +1550,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "found invalid Speex header" );
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1568,6 +1599,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "found invalid Flac header" );
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1583,6 +1615,23 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "found invalid Theora header" );
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
+                        p_ogg->i_streams--;
+                    }
+                }
+                /* Check for Daala header */
+                else if( oggpacket.bytes >= 6 &&
+                         ! memcmp( oggpacket.packet, "\x80""daala", 6 ) )
+                {
+                    if ( Ogg_ReadDaalaHeader( p_stream, &oggpacket ) )
+                        msg_Dbg( p_demux,
+                                 "found daala header, bitrate: %i, rate: %f",
+                                 p_stream->fmt.i_bitrate, p_stream->f_rate );
+                    else
+                    {
+                        msg_Dbg( p_demux, "found invalid Daala header" );
+                        Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1598,6 +1647,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Warn( p_demux, "found dirac header isn't decodable" );
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1635,6 +1685,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "invalid VP8 header found");
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1644,7 +1695,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 {
                     Ogg_ReadAnnodexHeader( p_demux, p_stream, &oggpacket );
                     /* kill annodex track */
-                    free( p_stream );
+                    FREENULL( p_stream );
                     p_ogg->i_streams--;
                 }
                 /* Check for Annodex header */
@@ -1663,6 +1714,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "invalid kate header found");
                         Ogg_LogicalStreamDelete( p_demux, p_stream );
+                        p_stream = NULL;
                         p_ogg->i_streams--;
                     }
                 }
@@ -1768,6 +1820,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         {
                             msg_Dbg( p_demux, "invalid oggds audio header" );
                             Ogg_LogicalStreamDelete( p_demux, p_stream );
+                            p_stream = NULL;
                             p_ogg->i_streams--;
                         }
                     }
@@ -1775,7 +1828,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "stream %d has an old header "
                             "but is of an unknown type", p_ogg->i_streams-1 );
-                        free( p_stream );
+                        FREENULL( p_stream );
                         p_ogg->i_streams--;
                     }
                 }
@@ -1898,6 +1951,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         {
                             msg_Dbg( p_demux, "invalid oggds audio header" );
                             Ogg_LogicalStreamDelete( p_demux, p_stream );
+                            p_stream = NULL;
                             p_ogg->i_streams--;
                         }
                     }
@@ -1916,7 +1970,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     {
                         msg_Dbg( p_demux, "stream %d has a header marker "
                             "but is of an unknown type", p_ogg->i_streams-1 );
-                        free( p_stream );
+                        FREENULL( p_stream );
                         p_ogg->i_streams--;
                     }
                 }
@@ -1933,12 +1987,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 {
                     msg_Dbg( p_demux, "stream %d is of unknown type",
                              p_ogg->i_streams-1 );
-                    free( p_stream );
+                    FREENULL( p_stream );
                     p_ogg->i_streams--;
                 }
 
                 /* we'll need to get all headers */
-                p_ogg->pp_stream[i_stream]->b_initializing &= p_ogg->pp_stream[i_stream]->b_force_backup;
+                if ( p_stream )
+                    p_stream->b_initializing &= p_stream->b_force_backup;
 
                 if( Ogg_ReadPage( p_demux, &p_ogg->current_page ) != VLC_SUCCESS )
                     return VLC_EGENERIC;
@@ -1947,7 +2002,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
             /* This is the first data page, which means we are now finished
              * with the initial pages. We just need to store it in the relevant
              * bitstream. */
-            for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+            for( int i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
             {
                 if( ogg_stream_pagein( &p_ogg->pp_stream[i_stream]->os,
                                        &p_ogg->current_page ) == 0 )
@@ -2120,7 +2175,7 @@ static void Ogg_CleanSpecificData( logical_stream_t *p_stream )
     {
         FREENULL( p_stream->special.vorbis.p_info );
         FREENULL( p_stream->special.vorbis.p_comment );
-        p_stream->special.vorbis.b_invalid = false;
+        p_stream->special.vorbis.i_headers_flags = 0;
     }
 #else
     VLC_UNUSED( p_stream );
@@ -2159,7 +2214,7 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
         block_ChainRelease( p_stream->p_preparse_block );
         p_stream->p_preparse_block = NULL;
     }
-    free( p_stream->p_prepcr_blocks );
+    free( p_stream->prepcr.pp_blocks );
 
     free( p_stream );
 }
@@ -2367,6 +2422,7 @@ static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t
     /* 3 headers with the 2° one being the comments */
     case VLC_CODEC_VORBIS:
     case VLC_CODEC_THEORA:
+    case VLC_CODEC_DAALA:
         Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 1+6 );
         break;
     case VLC_CODEC_OPUS:
@@ -2470,6 +2526,68 @@ static bool Ogg_ReadTheoraHeader( logical_stream_t *p_stream,
     return true;
 }
 
+static bool Ogg_ReadDaalaHeader( logical_stream_t *p_stream,
+                                 ogg_packet *p_oggpacket )
+{
+    oggpack_buffer opb;
+    uint32_t i_timebase_numerator;
+    uint32_t i_timebase_denominator;
+    int i_keyframe_frequency_force;
+    uint8_t i_major;
+    uint8_t i_minor;
+    uint8_t i_subminor;
+    int i_version;
+
+    p_stream->fmt.i_cat = VIDEO_ES;
+    p_stream->fmt.i_codec = VLC_CODEC_DAALA;
+
+    /* Signal that we want to keep a backup of the daala
+     * stream headers. They will be used when switching between
+     * audio streams. */
+    p_stream->b_force_backup = true;
+
+    /* Cheat and get additionnal info ;) */
+    oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes );
+    oggpack_adv( &opb, 48 );
+
+    i_major = oggpack_read( &opb, 8 ); /* major version num */
+    i_minor = oggpack_read( &opb, 8 ); /* minor version num */
+    i_subminor = oggpack_read( &opb, 8 ); /* subminor version num */
+
+    oggpack_adv( &opb, 32 ); /* width */
+    oggpack_adv( &opb, 32 ); /* height */
+
+    oggpack_adv( &opb, 32 ); /* aspect numerator */
+    oggpack_adv( &opb, 32 ); /* aspect denominator */
+    i_timebase_numerator = oggpack_read( &opb, 32 );
+
+    i_timebase_denominator = oggpack_read( &opb, 32 );
+    i_timebase_denominator = __MAX( i_timebase_denominator, 1 );
+
+    p_stream->fmt.video.i_frame_rate = i_timebase_numerator;
+    p_stream->fmt.video.i_frame_rate_base = i_timebase_denominator;
+
+    oggpack_adv( &opb, 32 ); /* frame duration */
+
+    i_keyframe_frequency_force = 1 << oggpack_read( &opb, 8 );
+
+    /* granule_shift = i_log( frequency_force -1 ) */
+    p_stream->i_granule_shift = 0;
+    i_keyframe_frequency_force--;
+    while( i_keyframe_frequency_force )
+    {
+        p_stream->i_granule_shift++;
+        i_keyframe_frequency_force >>= 1;
+    }
+
+    i_version = i_major * 1000000 + i_minor * 1000 + i_subminor;
+    p_stream->i_keyframe_offset = 0;
+    p_stream->f_rate = ((double)i_timebase_numerator) / i_timebase_denominator;
+    if ( p_stream->f_rate == 0 ) return false;
+
+    return true;
+}
+
 static bool Ogg_ReadVorbisHeader( logical_stream_t *p_stream,
                                   ogg_packet *p_oggpacket )
 {
@@ -2503,13 +2621,12 @@ static void Ogg_DecodeVorbisHeader( logical_stream_t *p_stream,
     switch( i_number )
     {
     case VORBIS_HEADER_IDENTIFICATION:
-        p_stream->special.vorbis.p_info = malloc( sizeof(vorbis_info) );
+        p_stream->special.vorbis.p_info = calloc( 1, sizeof(vorbis_info) );
         p_stream->special.vorbis.p_comment = malloc( sizeof(vorbis_comment) );
         if ( !p_stream->special.vorbis.p_info || !p_stream->special.vorbis.p_comment )
         {
             FREENULL( p_stream->special.vorbis.p_info );
             FREENULL( p_stream->special.vorbis.p_comment );
-            p_stream->special.vorbis.b_invalid = true;
             break;
         }
         vorbis_info_init( p_stream->special.vorbis.p_info );
@@ -2518,12 +2635,13 @@ static void Ogg_DecodeVorbisHeader( logical_stream_t *p_stream,
 
     case VORBIS_HEADER_COMMENT:
     case VORBIS_HEADER_SETUP:
-        if ( p_stream->special.vorbis.p_info && ! p_stream->special.vorbis.b_invalid )
-        {
-            p_stream->special.vorbis.b_invalid = ( 0 != vorbis_synthesis_headerin(
-                p_stream->special.vorbis.p_info,
-                p_stream->special.vorbis.p_comment, p_oggpacket ) );
-        }
+        if ( !p_stream->special.vorbis.p_info ||
+             vorbis_synthesis_headerin(
+                 p_stream->special.vorbis.p_info,
+                 p_stream->special.vorbis.p_comment, p_oggpacket ) )
+            break;
+
+        p_stream->special.vorbis.i_headers_flags |= VORBIS_HEADER_TO_FLAG(i_number);
         // ft
 
     default:
@@ -2776,6 +2894,14 @@ static void Ogg_ApplyContentType( logical_stream_t *p_stream, const char* psz_va
 
         *b_force_backup = true;
     }
+    else if( !strncmp(psz_value, "video/x-daala", 13) ||
+             !strncmp(psz_value, "video/daala", 11) )
+    {
+        p_stream->fmt.i_cat = VIDEO_ES;
+        p_stream->fmt.i_codec = VLC_CODEC_DAALA;
+
+        *b_force_backup = true;
+    }
     else if( !strncmp(psz_value, "video/x-xvid", 12) )
     {
         p_stream->fmt.i_cat = VIDEO_ES;
@@ -2981,7 +3107,7 @@ static void Ogg_ReadSkeletonIndex( demux_t *p_demux, ogg_packet *p_oggpacket )
             break;
         }
     }
-    if ( !p_stream ) return;
+    if ( !p_stream || !p_stream->p_skel ) return;
     uint64_t i_keypoints = GetQWLE( &p_oggpacket->packet[10] );
     msg_Dbg( p_demux, "%" PRIi64 " index data for %" PRIi32, i_keypoints, i_serialno );
     if ( !i_keypoints ) return;