From 46d3e0faa922643094a5e46a32e4f82f774ae772 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Enrique=20Oca=C3=B1a=20Gonz=C3=A1lez?= <eocanha@igalia.com>
Date: Tue, 10 Nov 2015 13:09:00 +0100
Subject: [PATCH] Use the tfdt decode time when it's significantly different
 than the time in the last sample if always-honor-tfdt is enabled

https://bugzilla.gnome.org/show_bug.cgi?id=754230
---
 gst/isomp4/qtdemux.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 gst/isomp4/qtdemux.h |  1 +
 2 files changed, 73 insertions(+)

diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
index 880595e..d8b54f0 100644
--- a/gst/isomp4/qtdemux.c
+++ b/gst/isomp4/qtdemux.c
@@ -535,6 +535,11 @@ static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
     const gchar * id);
 static void qtdemux_gst_structure_free (GstStructure * gststructure);
 
+static void gst_qtdemux_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * spec);
+static void gst_qtdemux_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * spec);
+
 static void
 gst_qtdemux_class_init (GstQTDemuxClass * klass)
 {
@@ -546,8 +551,21 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
 
   parent_class = g_type_class_peek_parent (klass);
 
+  gobject_class->set_property = gst_qtdemux_set_property;
+  gobject_class->get_property = gst_qtdemux_get_property;
+
   gobject_class->dispose = gst_qtdemux_dispose;
 
+ /**
+   * GstQtDemux::always-honor-tfdt:
+   *
+   * Requests the demuxer to respect what the TFDT atom says in order to produce presentation timestamps. Defaults to FALSE.
+   */
+  g_object_class_install_property (gobject_class, PROP_ALWAYS_HONOR_TFDT,
+      g_param_spec_boolean ("always-honor-tfdt", "Always honor TFDT",
+          "When enabled, TFDT atom will always be respected in order to produce presentation timestamps",
+          FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
 #if 0
   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
@@ -611,6 +629,7 @@ gst_qtdemux_init (GstQTDemux * qtdemux)
   qtdemux->cenc_aux_info_sizes = NULL;
   qtdemux->cenc_aux_sample_count = 0;
   qtdemux->protection_system_ids = NULL;
+  qtdemux->always_honor_tfdt = FALSE;
   g_queue_init (&qtdemux->protection_event_queue);
   gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
   qtdemux->flowcombiner = gst_flow_combiner_new ();
@@ -639,6 +658,42 @@ gst_qtdemux_dispose (GObject * object)
 }
 
 static void
+gst_qtdemux_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstQTDemux *qtdemux = GST_QTDEMUX (object);
+
+  switch (prop_id) {
+    case PROP_ALWAYS_HONOR_TFDT:
+      GST_OBJECT_LOCK (qtdemux);
+      qtdemux->always_honor_tfdt = g_value_get_boolean (value);
+      GST_OBJECT_UNLOCK (qtdemux);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qtdemux_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstQTDemux *qtdemux = GST_QTDEMUX (object);
+
+  switch (prop_id) {
+    case PROP_ALWAYS_HONOR_TFDT:
+      GST_OBJECT_LOCK (qtdemux);
+      g_value_set_boolean (value, qtdemux->always_honor_tfdt);
+      GST_OBJECT_UNLOCK (qtdemux);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
 {
   if (qtdemux->posted_redirect) {
@@ -2995,6 +3050,16 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
           stream->samples[stream->n_samples - 1].timestamp +
           stream->samples[stream->n_samples - 1].duration;
 
+      /* If we're always honoring TFDT and there's a significative difference
+       * between the decode_ts and the timestamp, prefer decode_ts */
+      if (qtdemux->always_honor_tfdt == TRUE
+          && abs (decode_ts - timestamp) >
+          stream->samples[stream->n_samples - 1].duration) {
+        GST_INFO_OBJECT (qtdemux,
+            "decode_ts is significantly different from timestamp, using decode_ts");
+        timestamp = decode_ts;
+      }
+
       gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
       GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
           " (extends previous samples)", GST_TIME_ARGS (gst_ts));
diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h
index 53bd071..ecf0c63 100644
--- a/gst/isomp4/qtdemux.h
+++ b/gst/isomp4/qtdemux.h
@@ -154,12 +154,20 @@ struct _GstQTDemux {
   guint8 *cenc_aux_info_sizes;
   guint32 cenc_aux_sample_count;
 
+  gboolean always_honor_tfdt;
 };
 
 struct _GstQTDemuxClass {
   GstElementClass parent_class;
 };
 
+/* props */
+enum
+{
+  PROP_0,
+  PROP_ALWAYS_HONOR_TFDT
+};
+
 GType gst_qtdemux_get_type (void);
 
 G_END_DECLS
-- 
2.6.1

