matroskamux: Write CodecDelay, DiscardPadding and SeekPreroll for Opus
authorSebastian Dröge <sebastian@centricular.com>
Tue, 3 Nov 2015 10:18:19 +0000 (12:18 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 3 Nov 2015 18:35:27 +0000 (20:35 +0200)
And also adjust timestamps and durations according to the codec delay, both
should include it for whatever reason.

https://bugzilla.gnome.org/show_bug.cgi?id=727305

gst/matroska/matroska-ids.h
gst/matroska/matroska-mux.c

index 661a4a1..f3063b8 100644 (file)
 #define GST_MATROSKA_ID_CODECDOWNLOADURL           0x26B240
 /* semi-draft */
 #define GST_MATROSKA_ID_CODECDECODEALL             0xAA
+#define GST_MATROSKA_ID_SEEKPREROLL                0x56BB
+#define GST_MATROSKA_ID_CODECDELAY                 0x56AA
 
 /* IDs in the TrackTranslate master */
 #define GST_MATROSKA_ID_TRACKTRANSLATEEDITIONUID   0x66FC
 /* semi-draft */
 #define GST_MATROSKA_ID_CODECSTATE                 0xA4
 #define GST_MATROSKA_ID_SLICES                     0x8E
+#define GST_MATROSKA_ID_DISCARDPADDING             0x75A2
 
 /* IDs in the BlockAdditions master */
 #define GST_MATROSKA_ID_BLOCKMORE                  0xA6
@@ -530,6 +533,8 @@ struct _GstMatroskaTrackContext {
   guint64       default_duration;
   guint64       pos;
   gdouble       timecodescale;
+  guint64       seek_preroll;
+  guint64       codec_delay;
 
   gboolean      set_discont; /* TRUE = set DISCONT flag on next buffer */
 
index babf2c9..b669242 100644 (file)
@@ -1636,6 +1636,12 @@ opus_streamheader_to_codecdata (const GValue * streamheader,
   context->codec_priv = g_malloc0 (context->codec_priv_size);
   gst_buffer_extract (buf, 0, context->codec_priv, -1);
 
+  context->codec_delay =
+      GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
+  context->codec_delay =
+      gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
+  context->seek_preroll = 80 * GST_MSECOND;
+
   return TRUE;
 
 /* ERRORS */
@@ -2510,6 +2516,7 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux,
         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
             audiocontext->bitdepth);
       }
+
       gst_ebml_write_master_finish (ebml, master);
 
       break;
@@ -2527,6 +2534,16 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux,
   if (context->codec_priv)
     gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
         context->codec_priv, context->codec_priv_size);
+
+  if (context->seek_preroll) {
+    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
+        context->seek_preroll);
+  }
+
+  if (context->codec_delay) {
+    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
+        context->codec_delay);
+  }
 }
 
 #if 0
@@ -3428,12 +3445,13 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
   gboolean write_duration;
   gint16 relative_timestamp;
   gint64 relative_timestamp64;
-  guint64 block_duration;
+  guint64 block_duration, duration_diff = 0;
   gboolean is_video_keyframe = FALSE;
   gboolean is_video_invisible = FALSE;
   GstMatroskamuxPad *pad;
   gint flags = 0;
   GstClockTime buffer_timestamp;
+  GstAudioClippingMeta *cmeta = NULL;
 
   /* write data */
   pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
@@ -3469,6 +3487,17 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
     return GST_FLOW_OK;
   }
 
+  if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
+      && collect_pad->track->codec_delay) {
+    /* All timestamps should include the codec delay */
+    if (buffer_timestamp > collect_pad->track->codec_delay) {
+      buffer_timestamp += collect_pad->track->codec_delay;
+    } else {
+      buffer_timestamp = 0;
+      duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
+    }
+  }
+
   /* set the timestamp for outgoing buffers */
   ebml->timestamp = buffer_timestamp;
 
@@ -3571,8 +3600,8 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
   write_duration = FALSE;
   block_duration = 0;
   if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
-    block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
-        1, mux->time_scale);
+    block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
+    block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
 
     /* small difference should be ok. */
     if (block_duration > collect_pad->default_duration_scaled + 1 ||
@@ -3601,7 +3630,16 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
   if (is_video_invisible)
     flags |= 0x08;
 
-  if (mux->doctype_version > 1 && !write_duration) {
+  if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
+    cmeta = gst_buffer_get_audio_clipping_meta (buf);
+    g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
+
+    /* Start clipping is done via header and CodecDelay */
+    if (cmeta && !cmeta->end)
+      cmeta = NULL;
+  }
+
+  if (mux->doctype_version > 1 && !write_duration && !cmeta) {
     if (is_video_keyframe)
       flags |= 0x80;
 
@@ -3626,6 +3664,17 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
         relative_timestamp, flags);
     if (write_duration)
       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
+
+    if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
+        && cmeta) {
+      /* Start clipping is done via header and CodecDelay */
+      if (cmeta->end) {
+        guint64 end =
+            gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
+        gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
+      }
+    }
+
     gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
         gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
     gst_ebml_write_buffer (ebml, hdr);