From 46fe18b280ed5825fd701edeedb2f5c24de001a2 Mon Sep 17 00:00:00 2001
From: Denis Charmet <typx@dinauz.org>
Date: Sat, 9 Nov 2013 16:53:06 +0100
Subject: [PATCH] Implement basic Opus support in MKV

---
 modules/codec/opus.c                         |  5 ++++
 modules/demux/mkv/matroska_segment.cpp       | 22 +++++++++++++++-
 modules/demux/mkv/matroska_segment_parse.cpp | 36 +++++++++++++++++++++++++
 modules/demux/mkv/mkv.cpp                    | 39 +++++++++++++++++++---------
 modules/demux/mkv/mkv.hpp                    |  6 +++--
 5 files changed, 94 insertions(+), 16 deletions(-)

diff --git a/modules/codec/opus.c b/modules/codec/opus.c
index d4e5f88..8802d49 100644
--- a/modules/codec/opus.c
+++ b/modules/codec/opus.c
@@ -386,6 +386,11 @@ static block_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
     if(spp>0)spp*=opus_packet_get_samples_per_frame(p_oggpacket->packet,48000);
     if(spp<120||spp>120*48)return NULL;
 
+    /* Since the information isn't always available at the demux level
+     * use the packet's sample number */
+    if(!i_nb_samples)
+        i_nb_samples = spp;
+
     block_t *p_aout_buffer=decoder_NewAudioBuffer( p_dec, spp );
     if ( !p_aout_buffer )
     {
diff --git a/modules/demux/mkv/matroska_segment.cpp b/modules/demux/mkv/matroska_segment.cpp
index 756a9c5..5e71206 100644
--- a/modules/demux/mkv/matroska_segment.cpp
+++ b/modules/demux/mkv/matroska_segment.cpp
@@ -929,10 +929,22 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
     /* now parse until key frame */
     const int es_types[3] = { VIDEO_ES, AUDIO_ES, SPU_ES };
     i_cat = es_types[0];
+    mtime_t i_seek_preroll = 0;
     for( int i = 0; i < 2; i_cat = es_types[++i] )
     {
         for( i_track = 0; i_track < tracks.size(); i_track++ )
         {
+            if( tracks[i_track]->i_seek_preroll )
+            {
+                bool b_enabled;
+                if( es_out_Control( sys.demuxer.out,
+                                    ES_OUT_GET_ES_STATE,
+                                    tracks[i_track]->p_es,
+                                    &b_enabled ) == VLC_SUCCESS &&
+                    b_enabled )
+                    i_seek_preroll = __MAX( i_seek_preroll,
+                                            tracks[i_track]->i_seek_preroll );
+            }
             if( tracks[i_track]->fmt.i_cat == i_cat )
             {
                 spoint * seekpoint = new spoint(i_track, i_seek_time, i_seek_position, i_seek_position);
@@ -968,7 +980,7 @@ void matroska_segment_c::Seek( mtime_t i_date, mtime_t i_time_offset, int64_t i_
         es_out_Control( sys.demuxer.out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_date );
         return;
     }
-
+    i_date -= i_seek_preroll;
     for(;;)
     {
         do
@@ -1201,6 +1213,7 @@ int matroska_segment_c::BlockGet( KaxBlock * & pp_block, KaxSimpleBlock * & pp_s
     *pb_key_picture         = true;
     *pb_discardable_picture = false;
     size_t i_tk;
+    *pi_duration = 0;
 
     for( ;; )
     {
@@ -1407,6 +1420,13 @@ int matroska_segment_c::BlockGet( KaxBlock * & pp_block, KaxSimpleBlock * & pp_s
                             }
                         }
                     }
+#if LIBMATROSKA_VERSION >= 0x010401
+                    else if( MKV_IS_ID( el, KaxDiscardPadding ) )
+                    {
+                        KaxDiscardPadding &dp = *(KaxDiscardPadding*) el;
+                        *pi_duration -= int64(dp);
+                    }
+#endif
                     break;
                 default:
                     msg_Err( &sys.demuxer, "invalid level = %d", i_level );
diff --git a/modules/demux/mkv/matroska_segment_parse.cpp b/modules/demux/mkv/matroska_segment_parse.cpp
index c9d2c9d..7b4c359 100644
--- a/modules/demux/mkv/matroska_segment_parse.cpp
+++ b/modules/demux/mkv/matroska_segment_parse.cpp
@@ -30,6 +30,7 @@
 
 extern "C" {
 #include "../vobsub.h"
+#include "../xiph.h"
 }
 
 #include <vlc_codecs.h>
@@ -401,6 +402,21 @@ void matroska_segment_c::ParseTrackEntry( KaxTrackEntry *m )
 
             msg_Dbg( &sys.demuxer, "|   |   |   + Track Overlay=%u", uint32( tovr ) );
         }
+#if LIBMATROSKA_VERSION >= 0x010401
+        else if( MKV_IS_ID( l, KaxCodecDelay ) )
+        {
+            KaxCodecDelay &codecdelay = *(KaxCodecDelay*)l;
+            tk->i_codec_delay = uint64_t( codecdelay ) / 1000;
+            msg_Dbg( &sys.demuxer, "|   |   |   + Track Codec Delay =%"PRIu64,
+                     tk->i_codec_delay );
+        }
+        else if( MKV_IS_ID( l, KaxSeekPreRoll ) )
+        {
+            KaxSeekPreRoll &spr = *(KaxSeekPreRoll*)l;
+            tk->i_seek_preroll = uint64_t(spr) / 1000;
+            msg_Dbg( &sys.demuxer, "|   |   |   + Track Seek Preroll =%"PRIu64, tk->i_seek_preroll );
+        }
+#endif
         else if( MKV_IS_ID( l, KaxContentEncodings ) )
         {
             EbmlMaster *cencs = static_cast<EbmlMaster*>(l);
@@ -1477,6 +1493,26 @@ int32_t matroska_segment_c::TrackInit( mkv_track_t * p_tk )
         p_tk->fmt.i_codec = VLC_CODEC_VORBIS;
         fill_extra_data( p_tk, 0 );
     }
+    else if( !strncmp( p_tk->psz_codec, "A_OPUS", 6 ) )
+    {
+        p_tk->fmt.i_codec = VLC_CODEC_OPUS;
+        if( !p_tk->fmt.audio.i_rate )
+        {
+            msg_Err( &sys.demuxer,"No sampling rate, defaulting to 48kHz");
+            p_tk->fmt.audio.i_rate = 48000;
+        }
+        const uint8_t tags[16] = {'O','p','u','s','T','a','g','s',
+                                   0, 0, 0, 0, 0, 0, 0, 0};
+        unsigned ps[2] = { p_tk->i_extra_data, 16 };
+        const void *pkt[2] = { (const void *)p_tk->p_extra_data,
+                              (const void *) tags };
+
+        if( xiph_PackHeaders( &p_tk->fmt.i_extra,
+                              &p_tk->fmt.p_extra,
+                              ps, pkt, 2 ) )
+            msg_Err( &sys.demuxer, "Couldn't pack OPUS headers");
+
+    }
     else if( !strncmp( p_tk->psz_codec, "A_AAC/MPEG2/", strlen( "A_AAC/MPEG2/" ) ) ||
              !strncmp( p_tk->psz_codec, "A_AAC/MPEG4/", strlen( "A_AAC/MPEG4/" ) ) )
     {
diff --git a/modules/demux/mkv/mkv.cpp b/modules/demux/mkv/mkv.cpp
index 6398409..7d42d83 100644
--- a/modules/demux/mkv/mkv.cpp
+++ b/modules/demux/mkv/mkv.cpp
@@ -499,10 +499,8 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
         msg_Err( p_demux, "unknown track number" );
         return;
     }
-    if( i_pts + i_duration < p_sys->i_start_pts && tk->fmt.i_cat == AUDIO_ES )
-    {
-        return; /* discard audio packets that shouldn't be rendered */
-    }
+
+    i_pts -= tk->i_codec_delay;
 
     if ( tk->fmt.i_cat != NAV_ES )
     {
@@ -592,9 +590,10 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
         {
             memcpy( p_block->p_buffer, tk->p_compression_data->GetBuffer(), tk->p_compression_data->GetSize() );
         }
-
-        if( tk->fmt.i_codec == VLC_CODEC_COOK ||
-            tk->fmt.i_codec == VLC_CODEC_ATRAC3 )
+        switch( tk->fmt.i_codec )
+        {
+        case VLC_CODEC_COOK:
+        case VLC_CODEC_ATRAC3:
         {
             handle_real_audio(p_demux, tk, p_block, i_pts);
             block_Release(p_block);
@@ -602,6 +601,27 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
                 i_pts + ( mtime_t )( tk->i_default_duration / 1000 ):
                 VLC_TS_INVALID;
             continue;
+         }
+         case VLC_CODEC_SPU:
+            if( strcmp( tk->psz_codec, "S_VOBSUB" ) )
+                p_block->i_length = i_duration * tk-> f_timecodescale *
+                    (double) p_segment->i_timescale / 1000.0;
+            break;
+         case VLC_CODEC_OPUS:
+            if( i_duration > 0 )
+            {
+                mtime_t i_length = i_duration * tk-> f_timecodescale *
+                    (double) p_segment->i_timescale / 1000.0;
+                p_block->i_nb_samples = i_length * tk->fmt.audio.i_rate
+                     / CLOCK_FREQ;
+                break;
+            }
+            else if( i_duration < 0 )
+            {
+                /* Opus uses p_block->i_length to handle discard padding */
+                p_block->i_length = -1 * i_duration * tk->fmt.audio.i_rate
+                    / CLOCK_FREQ;
+            }
         }
 
         if ( tk->fmt.i_cat == NAV_ES )
@@ -643,11 +663,6 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
 #if 0
 msg_Dbg( p_demux, "block i_dts: %"PRId64" / i_pts: %"PRId64, p_block->i_dts, p_block->i_pts);
 #endif
-        if( strcmp( tk->psz_codec, "S_VOBSUB" ) )
-        {
-            p_block->i_length = i_duration * tk-> f_timecodescale * (double) p_segment->i_timescale / 1000.0;
-        }
-
         /* FIXME remove when VLC_TS_INVALID work is done */
         if( i == 0 || p_block->i_dts > VLC_TS_INVALID )
             p_block->i_dts += VLC_TS_0;
diff --git a/modules/demux/mkv/mkv.hpp b/modules/demux/mkv/mkv.hpp
index efc88b3..7cae3e7 100644
--- a/modules/demux/mkv/mkv.hpp
+++ b/modules/demux/mkv/mkv.hpp
@@ -188,8 +188,6 @@ class PrivateTrackData
 
 struct mkv_track_t
 {
-//    ~mkv_track_t();
-
     bool         b_default;
     bool         b_enabled;
     bool         b_forced;
@@ -237,6 +235,10 @@ struct mkv_track_t
     uint32_t               i_encoding_scope;
     KaxContentCompSettings *p_compression_data;
 
+    /* Matroska 4 new elements used by Opus */
+    mtime_t i_seek_preroll;
+    mtime_t i_codec_delay;
+
 };
 
 struct mkv_index_t
