Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-good.git] / gst / matroska / matroska-demux.c
index f278afc..0939164 100644 (file)
@@ -53,6 +53,8 @@
 #include <string.h>
 #include <glib/gprintf.h>
 
+#include <gst/base/base.h>
+
 /* For AVI compatibility mode
    and for fourcc stuff */
 #include <gst/riff/riff-read.h>
@@ -83,11 +85,13 @@ enum
   PROP_0,
   PROP_METADATA,
   PROP_STREAMINFO,
-  PROP_MAX_GAP_TIME
+  PROP_MAX_GAP_TIME,
+  PROP_MAX_BACKTRACK_DISTANCE
 };
 
-#define  DEFAULT_MAX_GAP_TIME      (2 * GST_SECOND)
-#define  INVALID_DATA_THRESHOLD    (2 * 1024 * 1024)
+#define DEFAULT_MAX_GAP_TIME           (2 * GST_SECOND)
+#define DEFAULT_MAX_BACKTRACK_DISTANCE 30
+#define INVALID_DATA_THRESHOLD         (2 * 1024 * 1024)
 
 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -147,6 +151,8 @@ static gboolean gst_matroska_demux_handle_src_query (GstPad * pad,
 
 static gboolean gst_matroska_demux_handle_sink_event (GstPad * pad,
     GstObject * parent, GstEvent * event);
+static gboolean gst_matroska_demux_handle_sink_query (GstPad * pad,
+    GstObject * parent, GstQuery * query);
 static GstFlowReturn gst_matroska_demux_chain (GstPad * pad,
     GstObject * object, GstBuffer * buffer);
 
@@ -169,6 +175,9 @@ static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext
 static GstCaps
     * gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
     subtitlecontext, const gchar * codec_id, gpointer data, guint size);
+static const gchar *gst_matroska_track_encryption_algorithm_name (gint val);
+static const gchar *gst_matroska_track_encryption_cipher_mode_name (gint val);
+static const gchar *gst_matroska_track_encoding_scope_name (gint val);
 
 /* stream methods */
 static void gst_matroska_demux_reset (GstElement * element);
@@ -215,6 +224,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
           "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
           DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_MAX_BACKTRACK_DISTANCE,
+      g_param_spec_uint ("max-backtrack-distance",
+          "Maximum backtrack distance",
+          "Maximum backtrack distance in seconds when seeking without "
+          "and index in pull mode and search for a keyframe "
+          "(0 = disable backtracking).",
+          0, G_MAXUINT, DEFAULT_MAX_BACKTRACK_DISTANCE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
   gstelement_class->send_event =
@@ -255,6 +273,8 @@ gst_matroska_demux_init (GstMatroskaDemux * demux)
       GST_DEBUG_FUNCPTR (gst_matroska_demux_chain));
   gst_pad_set_event_function (demux->common.sinkpad,
       GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
+  gst_pad_set_query_function (demux->common.sinkpad,
+      GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_query));
   gst_element_add_pad (GST_ELEMENT (demux), demux->common.sinkpad);
 
   /* init defaults for common read context */
@@ -262,6 +282,7 @@ gst_matroska_demux_init (GstMatroskaDemux * demux)
 
   /* property defaults */
   demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
+  demux->max_backtrack_distance = DEFAULT_MAX_BACKTRACK_DISTANCE;
 
   GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);
 
@@ -283,6 +304,7 @@ gst_matroska_demux_reset (GstElement * element)
   demux->num_a_streams = 0;
   demux->num_t_streams = 0;
   demux->num_v_streams = 0;
+  demux->have_nonintraonly_v_streams = FALSE;
 
   demux->have_group_id = FALSE;
   demux->group_id = G_MAXUINT;
@@ -306,7 +328,11 @@ gst_matroska_demux_reset (GstElement * element)
   demux->to_time = GST_CLOCK_TIME_NONE;
   demux->cluster_time = GST_CLOCK_TIME_NONE;
   demux->cluster_offset = 0;
+  demux->cluster_prevsize = 0;
+  demux->seen_cluster_prevsize = FALSE;
   demux->next_cluster_offset = 0;
+  demux->stream_last_time = GST_CLOCK_TIME_NONE;
+  demux->last_cluster_offset = 0;
   demux->index_offset = 0;
   demux->seekable = FALSE;
   demux->need_segment = FALSE;
@@ -331,6 +357,11 @@ gst_matroska_demux_reset (GstElement * element)
 
   demux->cached_length = G_MAXUINT64;
 
+  if (demux->deferred_seek_event)
+    gst_event_unref (demux->deferred_seek_event);
+  demux->deferred_seek_event = NULL;
+  demux->deferred_seek_pad = NULL;
+
   gst_flow_combiner_clear (demux->flowcombiner);
 }
 
@@ -340,12 +371,13 @@ gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
   GstMapInfo map;
   gpointer data;
   gsize size;
+  GstBuffer *out_buf = buf;
 
   g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);
 
   GST_DEBUG ("decoding buffer %p", buf);
 
-  gst_buffer_map (buf, &map, GST_MAP_READ);
+  gst_buffer_map (out_buf, &map, GST_MAP_READ);
   data = map.data;
   size = map.size;
 
@@ -353,15 +385,57 @@ gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
 
   if (gst_matroska_decode_data (context->encodings, &data, &size,
           GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) {
-    gst_buffer_unmap (buf, &map);
-    gst_buffer_unref (buf);
-    return gst_buffer_new_wrapped (data, size);
+    if (data != map.data) {
+      gst_buffer_unmap (out_buf, &map);
+      gst_buffer_unref (out_buf);
+      out_buf = gst_buffer_new_wrapped (data, size);
+    } else {
+      gst_buffer_unmap (out_buf, &map);
+    }
   } else {
     GST_DEBUG ("decode data failed");
-    gst_buffer_unmap (buf, &map);
-    gst_buffer_unref (buf);
+    gst_buffer_unmap (out_buf, &map);
+    gst_buffer_unref (out_buf);
     return NULL;
   }
+  /* Encrypted stream */
+  if (context->protection_info) {
+
+    GstStructure *info_protect = gst_structure_copy (context->protection_info);
+    gboolean encrypted = FALSE;
+
+    gst_buffer_map (out_buf, &map, GST_MAP_READ);
+    data = map.data;
+    size = map.size;
+
+    if (gst_matroska_parse_protection_meta (&data, &size, info_protect,
+            &encrypted)) {
+      if (data != map.data) {
+        GstBuffer *tmp_buf;
+
+        gst_buffer_unmap (out_buf, &map);
+        tmp_buf = out_buf;
+        out_buf = gst_buffer_copy_region (tmp_buf, GST_BUFFER_COPY_ALL,
+            gst_buffer_get_size (tmp_buf) - size, size);
+        gst_buffer_unref (tmp_buf);
+        if (encrypted)
+          gst_buffer_add_protection_meta (out_buf, info_protect);
+        else
+          gst_structure_free (info_protect);
+      } else {
+        gst_buffer_unmap (out_buf, &map);
+        gst_structure_free (info_protect);
+      }
+    } else {
+      GST_WARNING ("Adding protection metadata failed");
+      gst_buffer_unmap (out_buf, &map);
+      gst_buffer_unref (out_buf);
+      gst_structure_free (info_protect);
+      return NULL;
+    }
+  }
+
+  return out_buf;
 }
 
 static void
@@ -390,21 +464,199 @@ gst_matroska_demux_add_stream_headers_to_caps (GstMatroskaDemux * demux,
 }
 
 static GstFlowReturn
-gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
+gst_matroska_demux_parse_colour (GstMatroskaDemux * demux, GstEbmlRead * ebml,
+    GstMatroskaTrackVideoContext * video_context)
+{
+  GstFlowReturn ret;
+  GstVideoColorimetry colorimetry;
+  guint32 id;
+  guint64 num;
+
+  colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
+  colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
+  colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN;
+  colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
+
+  DEBUG_ELEMENT_START (demux, ebml, "TrackVideoColour");
+
+  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
+    goto beach;
+
+  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
+      goto beach;
+
+    switch (id) {
+      case GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS:{
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          goto beach;
+
+        switch (num) {
+          case 0:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
+            break;
+          case 1:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
+            break;
+          case 2:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
+            break;
+          case 4:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_FCC;
+            break;
+            /* FIXME: "5: BT470BG" is undefined in GstVideoColorMatrix
+             * but it's functionally same as "6: BT601" */
+          case 5:
+          case 6:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
+            break;
+          case 7:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_SMPTE240M;
+            break;
+          case 9:
+            colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
+            break;
+          default:
+            GST_FIXME_OBJECT (demux, "Unsupported color matrix coefficients  %"
+                G_GUINT64_FORMAT, num);
+            break;
+        }
+        break;
+      }
+
+      case GST_MATROSKA_ID_VIDEORANGE:{
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          goto beach;
+
+        switch (num) {
+          case 0:
+            colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
+            break;
+          case 1:
+            colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
+            break;
+          case 2:
+            colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
+            break;
+          default:
+            GST_FIXME_OBJECT (demux, "Unsupported color range  %"
+                G_GUINT64_FORMAT, num);
+            break;
+        }
+        break;
+      }
+
+      case GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS:{
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          goto beach;
+
+        switch (num) {
+            /* FIXME: "6: BT601" and "14: BT2020_10" are undefined in
+             * GstVideoTransferFunction, but functionally same as "1: BT709" */
+          case 1:
+          case 6:
+          case 14:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
+            break;
+          case 2:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN;
+            break;
+          case 4:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA22;
+            break;
+          case 5:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA28;
+            break;
+          case 7:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE240M;
+            break;
+          case 8:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10;
+            break;
+          case 9:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_LOG100;
+            break;
+          case 10:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_LOG316;
+            break;
+          case 13:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB;
+            break;
+          case 15:
+            colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_12;
+            break;
+          default:
+            GST_FIXME_OBJECT (demux,
+                "Unsupported color transfer characteristics  %"
+                G_GUINT64_FORMAT, num);
+            break;
+        }
+        break;
+      }
+
+      case GST_MATROSKA_ID_VIDEOPRIMARIES:{
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          goto beach;
+
+        switch (num) {
+          case 1:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
+            break;
+          case 2:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
+            break;
+          case 4:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470M;
+            break;
+          case 5:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG;
+            break;
+          case 6:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
+            break;
+          case 7:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE240M;
+            break;
+          case 8:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_FILM;
+            break;
+          case 9:
+            colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
+            break;
+          default:
+            GST_FIXME_OBJECT (demux, "Unsupported color primaries  %"
+                G_GUINT64_FORMAT, num);
+            break;
+        }
+        break;
+      }
+
+      default:
+        GST_FIXME_OBJECT (demux, "Unsupported subelement 0x%x in Colour", id);
+        ret = gst_ebml_read_skip (ebml);
+        break;
+    }
+  }
+
+  memcpy (&video_context->colorimetry, &colorimetry,
+      sizeof (GstVideoColorimetry));
+
+beach:
+  DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideoColour", ret);
+  return ret;
+}
+
+static GstFlowReturn
+gst_matroska_demux_parse_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml,
+    GstMatroskaTrackContext ** dest_context)
 {
-  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
   GstMatroskaTrackContext *context;
-  GstPadTemplate *templ = NULL;
-  GstStreamFlags stream_flags;
   GstCaps *caps = NULL;
   GstTagList *cached_taglist;
-  gchar *padname = NULL;
   GstFlowReturn ret;
   guint32 id, riff_fourcc = 0;
   guint16 riff_audio_fmt = 0;
-  GstEvent *stream_start;
   gchar *codec = NULL;
-  gchar *stream_id;
 
   DEBUG_ELEMENT_START (demux, ebml, "TrackEntry");
 
@@ -417,8 +669,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   /* allocate generic... if we know the type, we'll g_renew()
    * with the precise type */
   context = g_new0 (GstMatroskaTrackContext, 1);
-  g_ptr_array_add (demux->common.src, context);
-  context->index = demux->common.num_streams;
   context->index_writer_id = -1;
   context->type = 0;            /* no type yet */
   context->default_duration = 0;
@@ -435,10 +685,11 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   context->dts_only = FALSE;
   context->intra_only = FALSE;
   context->tags = gst_tag_list_new_empty ();
-  demux->common.num_streams++;
-  g_assert (demux->common.src->len == demux->common.num_streams);
+  g_queue_init (&context->protection_event_queue);
+  context->protection_info = NULL;
 
-  GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
+  GST_DEBUG_OBJECT (demux, "Parsing a TrackEntry (%d tracks parsed so far)",
+      demux->common.num_streams);
 
   /* try reading the trackentry headers */
   while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
@@ -457,12 +708,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
           GST_ERROR_OBJECT (demux, "Invalid TrackNumber 0");
           ret = GST_FLOW_ERROR;
           break;
-        } else if (!gst_matroska_read_common_tracknumber_unique (&demux->common,
-                num)) {
-          GST_ERROR_OBJECT (demux, "TrackNumber %" G_GUINT64_FORMAT
-              " is not unique", num);
-          ret = GST_FLOW_ERROR;
-          break;
         }
 
         GST_DEBUG_OBJECT (demux, "TrackNumber: %" G_GUINT64_FORMAT, num);
@@ -529,8 +774,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
             context->type = 0;
             break;
         }
-        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
-            = context;
         break;
       }
 
@@ -549,8 +792,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
           break;
         }
         videocontext = (GstMatroskaTrackVideoContext *) context;
-        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
-            = context;
 
         while (ret == GST_FLOW_OK &&
             gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
@@ -677,13 +918,18 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
               if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
                 break;
 
-              if (num)
-                context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
+              if (num == 1)
+                videocontext->interlace_mode =
+                    GST_MATROSKA_INTERLACE_MODE_INTERLACED;
+              else if (num == 2)
+                videocontext->interlace_mode =
+                    GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE;
               else
-                context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED;
-              GST_DEBUG_OBJECT (demux, "TrackVideoInterlaced: %d",
-                  (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 :
-                  0);
+                videocontext->interlace_mode =
+                    GST_MATROSKA_INTERLACE_MODE_UNKNOWN;
+
+              GST_DEBUG_OBJECT (demux, "video track interlacing mode: %d",
+                  videocontext->interlace_mode);
               break;
             }
 
@@ -732,6 +978,13 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
               g_free (data);
               break;
             }
+
+              /* color info */
+            case GST_MATROSKA_ID_VIDEOCOLOUR:{
+              ret = gst_matroska_demux_parse_colour (demux, ebml, videocontext);
+              break;
+            }
+
             case GST_MATROSKA_ID_VIDEOSTEREOMODE:
             {
               guint64 num;
@@ -819,8 +1072,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
           break;
 
         audiocontext = (GstMatroskaTrackAudioContext *) context;
-        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
-            = context;
 
         while (ret == GST_FLOW_OK &&
             gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
@@ -1149,11 +1400,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
     if (ret == GST_FLOW_OK || ret == GST_FLOW_EOS)
       GST_WARNING_OBJECT (ebml, "Unknown stream/codec in track entry header");
 
-    demux->common.num_streams--;
-    g_ptr_array_remove_index (demux->common.src, demux->common.num_streams);
-    g_assert (demux->common.src->len == demux->common.num_streams);
     gst_matroska_track_free (context);
-
+    context = NULL;
+    *dest_context = NULL;
     return ret;
   }
 
@@ -1164,14 +1413,12 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   if (cached_taglist)
     gst_tag_list_insert (context->tags, cached_taglist, GST_TAG_MERGE_APPEND);
 
-  /* now create the GStreamer connectivity */
+  /* compute caps */
   switch (context->type) {
     case GST_MATROSKA_TRACK_TYPE_VIDEO:{
       GstMatroskaTrackVideoContext *videocontext =
           (GstMatroskaTrackVideoContext *) context;
 
-      padname = g_strdup_printf ("video_%u", demux->num_v_streams++);
-      templ = gst_element_class_get_pad_template (klass, "video_%u");
       caps = gst_matroska_demux_video_caps (videocontext,
           context->codec_id, context->codec_priv,
           context->codec_priv_size, &codec, &riff_fourcc);
@@ -1189,8 +1436,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
       GstMatroskaTrackAudioContext *audiocontext =
           (GstMatroskaTrackAudioContext *) context;
 
-      padname = g_strdup_printf ("audio_%u", demux->num_a_streams++);
-      templ = gst_element_class_get_pad_template (klass, "audio_%u");
       caps = gst_matroska_demux_audio_caps (audiocontext,
           context->codec_id, context->codec_priv, context->codec_priv_size,
           &codec, &riff_audio_fmt);
@@ -1208,8 +1453,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
       GstMatroskaTrackSubtitleContext *subtitlecontext =
           (GstMatroskaTrackSubtitleContext *) context;
 
-      padname = g_strdup_printf ("subtitle_%u", demux->num_t_streams++);
-      templ = gst_element_class_get_pad_template (klass, "subtitle_%u");
       caps = gst_matroska_demux_subtitle_caps (subtitlecontext,
           context->codec_id, context->codec_priv, context->codec_priv_size);
       break;
@@ -1238,6 +1481,11 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
     lang = gst_tag_get_language_code (context->language);
     gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
         GST_TAG_LANGUAGE_CODE, (lang) ? lang : context->language, NULL);
+
+    if (context->name) {
+      gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
+          GST_TAG_TITLE, context->name, NULL);
+    }
     context->tags_changed = TRUE;
   }
 
@@ -1276,9 +1524,84 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
         context->stream_headers, caps);
   }
 
+  if (context->encodings) {
+    GstMatroskaTrackEncoding *enc;
+    guint i;
+
+    for (i = 0; i < context->encodings->len; i++) {
+      enc = &g_array_index (context->encodings, GstMatroskaTrackEncoding, i);
+      if (enc->type == GST_MATROSKA_ENCODING_ENCRYPTION /* encryption */ ) {
+        GstStructure *s = gst_caps_get_structure (caps, 0);
+        if (!gst_structure_has_name (s, "application/x-webm-enc")) {
+          gst_structure_set (s, "original-media-type", G_TYPE_STRING,
+              gst_structure_get_name (s), NULL);
+          gst_structure_set (s, "encryption-algorithm", G_TYPE_STRING,
+              gst_matroska_track_encryption_algorithm_name (enc->enc_algo),
+              NULL);
+          gst_structure_set (s, "encoding-scope", G_TYPE_STRING,
+              gst_matroska_track_encoding_scope_name (enc->scope), NULL);
+          gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
+              gst_matroska_track_encryption_cipher_mode_name
+              (enc->enc_cipher_mode), NULL);
+          gst_structure_set_name (s, "application/x-webm-enc");
+        }
+      }
+    }
+  }
+
+  context->caps = caps;
+
+  /* tadaah! */
+  *dest_context = context;
+  return ret;
+}
+
+static void
+gst_matroska_demux_add_stream (GstMatroskaDemux * demux,
+    GstMatroskaTrackContext * context)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
+  gchar *padname = NULL;
+  GstPadTemplate *templ = NULL;
+  GstStreamFlags stream_flags;
+
+  GstEvent *stream_start;
+
+  gchar *stream_id;
+
+  g_ptr_array_add (demux->common.src, context);
+  context->index = demux->common.num_streams++;
+  g_assert (demux->common.src->len == demux->common.num_streams);
+  g_ptr_array_index (demux->common.src, demux->common.num_streams - 1) =
+      context;
+
+  /* now create the GStreamer connectivity */
+  switch (context->type) {
+    case GST_MATROSKA_TRACK_TYPE_VIDEO:
+      padname = g_strdup_printf ("video_%u", demux->num_v_streams++);
+      templ = gst_element_class_get_pad_template (klass, "video_%u");
+
+      if (!context->intra_only)
+        demux->have_nonintraonly_v_streams = TRUE;
+      break;
+
+    case GST_MATROSKA_TRACK_TYPE_AUDIO:
+      padname = g_strdup_printf ("audio_%u", demux->num_a_streams++);
+      templ = gst_element_class_get_pad_template (klass, "audio_%u");
+      break;
+
+    case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
+      padname = g_strdup_printf ("subtitle_%u", demux->num_t_streams++);
+      templ = gst_element_class_get_pad_template (klass, "subtitle_%u");
+      break;
+
+    default:
+      /* we should already have quit by now */
+      g_assert_not_reached ();
+  }
+
   /* the pad in here */
   context->pad = gst_pad_new_from_template (templ, padname);
-  context->caps = caps;
 
   gst_pad_set_event_function (context->pad,
       GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_event));
@@ -1286,7 +1609,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
       GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_query));
 
   GST_INFO_OBJECT (demux, "Adding pad '%s' with caps %" GST_PTR_FORMAT,
-      padname, caps);
+      padname, context->caps);
 
   gst_pad_set_element_private (context->pad, context);
 
@@ -1320,6 +1643,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
     stream_flags |= GST_STREAM_FLAG_SPARSE;
   if (context->flags & GST_MATROSKA_TRACK_DEFAULT)
     stream_flags |= GST_STREAM_FLAG_SELECT;
+  else if (!(context->flags & GST_MATROSKA_TRACK_ENABLED))
+    stream_flags |= GST_STREAM_FLAG_UNSELECT;
+
   gst_event_set_stream_flags (stream_start, stream_flags);
   gst_pad_push_event (context->pad, stream_start);
   gst_pad_set_caps (context->pad, context->caps);
@@ -1351,9 +1677,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   gst_flow_combiner_add_pad (demux->flowcombiner, context->pad);
 
   g_free (padname);
-
-  /* tadaah! */
-  return ret;
 }
 
 static gboolean
@@ -1580,9 +1903,13 @@ gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event)
   if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
     /* no seeking until we are (safely) ready */
     if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
-      GST_DEBUG_OBJECT (demux, "not ready for seeking yet");
-      gst_event_unref (event);
-      return FALSE;
+      GST_DEBUG_OBJECT (demux,
+          "not ready for seeking yet, deferring seek: %" GST_PTR_FORMAT, event);
+      if (demux->deferred_seek_event)
+        gst_event_unref (demux->deferred_seek_event);
+      demux->deferred_seek_event = event;
+      demux->deferred_seek_pad = NULL;
+      return TRUE;
     }
     res = gst_matroska_demux_handle_seek_event (demux, NULL, event);
   } else {
@@ -1654,12 +1981,13 @@ gst_matroska_cluster_compare (gint64 * i1, gint64 * i2)
 /* searches for a cluster start from @pos,
  * return GST_FLOW_OK and cluster position in @pos if found */
 static GstFlowReturn
-gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
+gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos,
+    gboolean forward)
 {
   gint64 newpos = *pos;
   gint64 orig_offset;
   GstFlowReturn ret = GST_FLOW_OK;
-  const guint chunk = 64 * 1024;
+  const guint chunk = 128 * 1024;
   GstBuffer *buf = NULL;
   GstMapInfo map;
   gpointer data = NULL;
@@ -1671,8 +1999,8 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
 
   orig_offset = demux->common.offset;
 
-  GST_LOG_OBJECT (demux, "searching cluster following offset %" G_GINT64_FORMAT,
-      *pos);
+  GST_LOG_OBJECT (demux, "searching cluster %s offset %" G_GINT64_FORMAT,
+      forward ? "following" : "preceding", *pos);
 
   if (demux->clusters) {
     gint64 *cpos;
@@ -1680,7 +2008,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
     cpos = gst_util_array_binary_search (demux->clusters->data,
         demux->clusters->len, sizeof (gint64),
         (GCompareDataFunc) gst_matroska_cluster_compare,
-        GST_SEARCH_MODE_AFTER, pos, NULL);
+        forward ? GST_SEARCH_MODE_AFTER : GST_SEARCH_MODE_BEFORE, pos, NULL);
     /* sanity check */
     if (cpos) {
       GST_DEBUG_OBJECT (demux,
@@ -1700,13 +2028,23 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
   while (1) {
     GstByteReader reader;
     gint cluster_pos;
+    guint toread = chunk;
 
+    if (!forward) {
+      /* never read beyond the requested target */
+      if (G_UNLIKELY (newpos < chunk)) {
+        toread = newpos;
+        newpos = 0;
+      } else {
+        newpos -= chunk;
+      }
+    }
     if (buf != NULL) {
       gst_buffer_unmap (buf, &map);
       gst_buffer_unref (buf);
       buf = NULL;
     }
-    ret = gst_pad_pull_range (demux->common.sinkpad, newpos, chunk, &buf);
+    ret = gst_pad_pull_range (demux->common.sinkpad, newpos, toread, &buf);
     if (ret != GST_FLOW_OK)
       break;
     GST_DEBUG_OBJECT (demux,
@@ -1725,16 +2063,25 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
     }
 
     gst_byte_reader_init (&reader, data, size);
-  resume:
-    cluster_pos = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
-        GST_MATROSKA_ID_CLUSTER, 0, gst_byte_reader_get_remaining (&reader));
+    cluster_pos = -1;
+    while (1) {
+      gint found = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
+          GST_MATROSKA_ID_CLUSTER, 0, gst_byte_reader_get_remaining (&reader));
+      if (forward) {
+        cluster_pos = found;
+        break;
+      }
+      /* need last occurrence when searching backwards */
+      if (found >= 0) {
+        cluster_pos = gst_byte_reader_get_pos (&reader) + found;
+        gst_byte_reader_skip (&reader, found + 4);
+      } else {
+        break;
+      }
+    }
+
     if (cluster_pos >= 0) {
       newpos += cluster_pos;
-      /* prepare resuming at next byte */
-      if (!gst_byte_reader_skip (&reader, cluster_pos + 1)) {
-        GST_DEBUG_OBJECT (demux, "Need more data -> continue");
-        continue;
-      }
       GST_DEBUG_OBJECT (demux,
           "found cluster ebml id at offset %" G_GINT64_FORMAT, newpos);
       /* extra checks whether we really sync'ed to a cluster:
@@ -1752,7 +2099,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
           GST_ELEMENT_CAST (demux), &id, &length, &needed);
       if (ret != GST_FLOW_OK) {
         GST_DEBUG_OBJECT (demux, "need more data -> continue");
-        continue;
+        goto next;
       }
       g_assert (id == GST_MATROSKA_ID_CLUSTER);
       GST_DEBUG_OBJECT (demux, "cluster size %" G_GUINT64_FORMAT ", prefix %d",
@@ -1766,26 +2113,19 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
       demux->common.offset += length + needed;
       ret = gst_matroska_read_common_peek_id_length_pull (&demux->common,
           GST_ELEMENT_CAST (demux), &id, &length, &needed);
-      if (ret != GST_FLOW_OK) {
-        /* we skipped one byte in the reader above, need to accomodate for
-         * that when resuming skipping from the reader instead of reading a
-         * new chunk */
-        newpos += 1;
-        goto resume;
-      }
+      if (ret != GST_FLOW_OK)
+        goto next;
       GST_DEBUG_OBJECT (demux, "next element is %scluster",
           id == GST_MATROSKA_ID_CLUSTER ? "" : "not ");
       if (id == GST_MATROSKA_ID_CLUSTER)
         break;
-      /* not ok, resume
-       * we skipped one byte in the reader above, need to accomodate for
-       * that when resuming skipping from the reader instead of reading a
-       * new chunk */
-      newpos += 1;
-      goto resume;
+    next:
+      if (forward)
+        newpos += 1;
     } else {
       /* partial cluster id may have been in tail of buffer */
-      newpos += MAX (gst_byte_reader_get_remaining (&reader), 4) - 3;
+      newpos +=
+          forward ? MAX (gst_byte_reader_get_remaining (&reader), 4) - 3 : 3;
     }
   }
 
@@ -1801,6 +2141,273 @@ exit:
   return ret;
 }
 
+/* Three states to express: starts with I-frame, starts with delta, don't know */
+typedef enum
+{
+  CLUSTER_STATUS_NONE = 0,
+  CLUSTER_STATUS_STARTS_WITH_KEYFRAME,
+  CLUSTER_STATUS_STARTS_WITH_DELTAUNIT,
+} ClusterStatus;
+
+typedef struct
+{
+  guint64 offset;
+  guint64 size;
+  guint64 prev_size;
+  GstClockTime time;
+  ClusterStatus status;
+} ClusterInfo;
+
+static const gchar *
+cluster_status_get_nick (ClusterStatus status)
+{
+  switch (status) {
+    case CLUSTER_STATUS_NONE:
+      return "none";
+    case CLUSTER_STATUS_STARTS_WITH_KEYFRAME:
+      return "key";
+    case CLUSTER_STATUS_STARTS_WITH_DELTAUNIT:
+      return "delta";
+  }
+  return "???";
+}
+
+/* Skip ebml-coded number:
+ *  1xxx.. = 1 byte
+ *  01xx.. = 2 bytes
+ *  001x.. = 3 bytes, etc.
+ */
+static gboolean
+bit_reader_skip_ebml_num (GstBitReader * br)
+{
+  guint8 i, v = 0;
+
+  if (!gst_bit_reader_peek_bits_uint8 (br, &v, 8))
+    return FALSE;
+
+  for (i = 0; i < 8; i++) {
+    if ((v & (0x80 >> i)) != 0)
+      break;
+  }
+  return gst_bit_reader_skip (br, (i + 1) * 8);
+}
+
+/* Don't probe more than that many bytes into the cluster for keyframe info
+ * (random value, mostly for sanity checking) */
+#define MAX_CLUSTER_INFO_PROBE_LENGTH 256
+
+static gboolean
+gst_matroska_demux_peek_cluster_info (GstMatroskaDemux * demux,
+    ClusterInfo * cluster, guint64 offset)
+{
+  demux->common.offset = offset;
+  demux->cluster_time = GST_CLOCK_TIME_NONE;
+
+  cluster->offset = offset;
+  cluster->size = 0;
+  cluster->prev_size = 0;
+  cluster->time = GST_CLOCK_TIME_NONE;
+  cluster->status = CLUSTER_STATUS_NONE;
+
+  /* parse first few elements in cluster */
+  do {
+    GstFlowReturn flow;
+    guint64 length;
+    guint32 id;
+    guint needed;
+
+    flow = gst_matroska_read_common_peek_id_length_pull (&demux->common,
+        GST_ELEMENT_CAST (demux), &id, &length, &needed);
+
+    if (flow != GST_FLOW_OK)
+      break;
+
+    GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
+        "size %" G_GUINT64_FORMAT ", needed %d", demux->common.offset, id,
+        length, needed);
+
+    /* Reached start of next cluster without finding data, stop processing */
+    if (id == GST_MATROSKA_ID_CLUSTER && cluster->offset != offset)
+      break;
+
+    /* Not going to parse into these for now, stop processing */
+    if (id == GST_MATROSKA_ID_ENCRYPTEDBLOCK
+        || id == GST_MATROSKA_ID_BLOCKGROUP || id == GST_MATROSKA_ID_BLOCK)
+      break;
+
+    /* SimpleBlock: peek at headers to check if it's a keyframe */
+    if (id == GST_MATROSKA_ID_SIMPLEBLOCK) {
+      GstBitReader br;
+      guint8 *d, hdr_len, v = 0;
+
+      GST_DEBUG_OBJECT (demux, "SimpleBlock found");
+
+      /* SimpleBlock header is max. 21 bytes */
+      hdr_len = MIN (21, length);
+
+      flow = gst_matroska_read_common_peek_bytes (&demux->common,
+          demux->common.offset, hdr_len, NULL, &d);
+
+      if (flow != GST_FLOW_OK)
+        break;
+
+      gst_bit_reader_init (&br, d, hdr_len);
+
+      /* skip prefix: ebml id (SimpleBlock) + element length */
+      if (!gst_bit_reader_skip (&br, 8 * needed))
+        break;
+
+      /* skip track number (ebml coded) */
+      if (!bit_reader_skip_ebml_num (&br))
+        break;
+
+      /* skip Timecode */
+      if (!gst_bit_reader_skip (&br, 16))
+        break;
+
+      /* read flags */
+      if (!gst_bit_reader_get_bits_uint8 (&br, &v, 8))
+        break;
+
+      if ((v & 0x80) != 0)
+        cluster->status = CLUSTER_STATUS_STARTS_WITH_KEYFRAME;
+      else
+        cluster->status = CLUSTER_STATUS_STARTS_WITH_DELTAUNIT;
+
+      break;
+    }
+
+    flow = gst_matroska_demux_parse_id (demux, id, length, needed);
+
+    if (flow != GST_FLOW_OK)
+      break;
+
+    switch (id) {
+      case GST_MATROSKA_ID_CLUSTER:
+        if (length == G_MAXUINT64)
+          cluster->size = 0;
+        else
+          cluster->size = length + needed;
+        break;
+      case GST_MATROSKA_ID_PREVSIZE:
+        cluster->prev_size = demux->cluster_prevsize;
+        break;
+      case GST_MATROSKA_ID_CLUSTERTIMECODE:
+        cluster->time = demux->cluster_time * demux->common.time_scale;
+        break;
+      case GST_MATROSKA_ID_SILENTTRACKS:
+        /* ignore and continue */
+        break;
+      default:
+        GST_WARNING_OBJECT (demux, "Unknown ebml id 0x%08x (possibly garbage), "
+            "bailing out", id);
+        goto out;
+    }
+  } while (demux->common.offset - offset < MAX_CLUSTER_INFO_PROBE_LENGTH);
+
+out:
+
+  GST_INFO_OBJECT (demux, "Cluster @ %" G_GUINT64_FORMAT ": "
+      "time %" GST_TIME_FORMAT ", size %" G_GUINT64_FORMAT ", "
+      "prev_size %" G_GUINT64_FORMAT ", %s", cluster->offset,
+      GST_TIME_ARGS (cluster->time), cluster->size, cluster->prev_size,
+      cluster_status_get_nick (cluster->status));
+
+  /* return success as long as we could extract the minimum useful information */
+  return cluster->time != GST_CLOCK_TIME_NONE;
+}
+
+/* returns TRUE if the cluster offset was updated */
+static gboolean
+gst_matroska_demux_scan_back_for_keyframe_cluster (GstMatroskaDemux * demux,
+    gint64 * cluster_offset, GstClockTime * cluster_time)
+{
+  GstClockTime stream_start_time = demux->stream_start_time;
+  guint64 first_cluster_offset = demux->first_cluster_offset;
+  gint64 off = *cluster_offset;
+  ClusterInfo cluster = { 0, };
+
+  GST_INFO_OBJECT (demux, "Checking if cluster starts with keyframe");
+  while (off > first_cluster_offset) {
+    if (!gst_matroska_demux_peek_cluster_info (demux, &cluster, off)) {
+      GST_LOG_OBJECT (demux,
+          "Couldn't get info on cluster @ %" G_GUINT64_FORMAT, off);
+      break;
+    }
+
+    /* Keyframe? Then we're done */
+    if (cluster.status == CLUSTER_STATUS_STARTS_WITH_KEYFRAME) {
+      GST_LOG_OBJECT (demux,
+          "Found keyframe at start of cluster @ %" G_GUINT64_FORMAT, off);
+      break;
+    }
+
+    /* We only scan back if we *know* we landed on a cluster that
+     * starts with a delta frame. */
+    if (cluster.status != CLUSTER_STATUS_STARTS_WITH_DELTAUNIT) {
+      GST_LOG_OBJECT (demux,
+          "No delta frame at start of cluster @ %" G_GUINT64_FORMAT, off);
+      break;
+    }
+
+    GST_DEBUG_OBJECT (demux, "Cluster starts with delta frame, backtracking");
+
+    /* Don't scan back more than this much in time from the cluster we
+     * originally landed on. This is mostly a sanity check in case a file
+     * always has keyframes in the middle of clusters and never at the
+     * beginning. Without this we would always scan back to the beginning
+     * of the file in that case. */
+    if (cluster.time != GST_CLOCK_TIME_NONE) {
+      GstClockTimeDiff distance = GST_CLOCK_DIFF (cluster.time, *cluster_time);
+
+      if (distance < 0 || distance > demux->max_backtrack_distance * GST_SECOND) {
+        GST_DEBUG_OBJECT (demux, "Haven't found cluster with keyframe within "
+            "%u secs of original seek target cluster, stopping",
+            demux->max_backtrack_distance);
+        break;
+      }
+    }
+
+    /* If we have cluster prev_size we can skip back efficiently. If not,
+     * we'll just do a brute force search for a cluster identifier */
+    if (cluster.prev_size > 0 && off >= cluster.prev_size) {
+      off -= cluster.prev_size;
+    } else {
+      GstFlowReturn flow;
+
+      GST_LOG_OBJECT (demux, "Cluster has no or invalid prev size, searching "
+          "for previous cluster instead then");
+
+      flow = gst_matroska_demux_search_cluster (demux, &off, FALSE);
+      if (flow != GST_FLOW_OK) {
+        GST_DEBUG_OBJECT (demux, "cluster search yielded flow %s, stopping",
+            gst_flow_get_name (flow));
+        break;
+      }
+    }
+
+    if (off <= first_cluster_offset) {
+      GST_LOG_OBJECT (demux, "Reached first cluster, stopping");
+      *cluster_offset = first_cluster_offset;
+      *cluster_time = stream_start_time;
+      return TRUE;
+    }
+    GST_LOG_OBJECT (demux, "Trying prev cluster @ %" G_GUINT64_FORMAT, off);
+  }
+
+  /* If we found a cluster starting with a keyframe jump to that instead,
+   * otherwise leave everything as it was before */
+  if (cluster.time != GST_CLOCK_TIME_NONE
+      && (cluster.offset == first_cluster_offset
+          || cluster.status == CLUSTER_STATUS_STARTS_WITH_KEYFRAME)) {
+    *cluster_offset = cluster.offset;
+    *cluster_time = cluster.time;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 /* bisect and scan through file for cluster starting before @time,
  * returns fake index entry with corresponding info on cluster */
 static GstMatroskaIndex *
@@ -1809,21 +2416,20 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time)
   GstMatroskaIndex *entry = NULL;
   GstMatroskaReadState current_state;
   GstClockTime otime, prev_cluster_time, current_cluster_time, cluster_time;
-  gint64 opos, newpos, startpos = 0, current_offset;
+  GstClockTime atime;
+  gint64 opos, newpos, current_offset;
   gint64 prev_cluster_offset = -1, current_cluster_offset, cluster_offset;
-  const guint chunk = 64 * 1024;
+  gint64 apos, maxpos;
+  guint64 cluster_size = 0;
   GstFlowReturn ret;
   guint64 length;
   guint32 id;
   guint needed;
 
-  /* (under)estimate new position, resync using cluster ebml id,
-   * and scan forward to appropriate cluster
-   * (and re-estimate if need to go backward) */
-
-  prev_cluster_time = GST_CLOCK_TIME_NONE;
+  /* estimate new position, resync using cluster ebml id,
+   * and bisect further or scan forward to appropriate cluster */
 
-  /* store some current state */
+  /* save some current global state which will be touched by our scanning */
   current_state = demux->common.state;
   g_return_val_if_fail (current_state == GST_MATROSKA_READ_STATE_DATA, NULL);
 
@@ -1833,77 +2439,78 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time)
 
   demux->common.state = GST_MATROSKA_READ_STATE_SCANNING;
 
-  /* estimate using start and current position */
+  /* estimate using start and last known cluster */
   GST_OBJECT_LOCK (demux);
-  opos = demux->common.offset - demux->common.ebml_segment_start;
-  otime = demux->common.segment.position;
+  apos = demux->first_cluster_offset;
+  atime = demux->stream_start_time;
+  opos = demux->last_cluster_offset;
+  otime = demux->stream_last_time;
   GST_OBJECT_UNLOCK (demux);
 
   /* sanitize */
-  time = MAX (time, demux->stream_start_time);
+  time = MAX (time, atime);
+  otime = MAX (otime, atime);
+  opos = MAX (opos, apos);
 
-  /* avoid division by zero in first estimation below */
-  if (otime <= demux->stream_start_time)
-    otime = time;
+  maxpos = gst_matroska_read_common_get_length (&demux->common);
+
+  /* invariants;
+   * apos <= opos
+   * atime <= otime
+   * apos always refer to a cluster before target time;
+   * opos may or may not be after target time, but if it is once so,
+   * then also in next iteration
+   * */
 
 retry:
   GST_LOG_OBJECT (demux,
+      "apos: %" G_GUINT64_FORMAT ", atime: %" GST_TIME_FORMAT ", %"
+      GST_TIME_FORMAT " in stream time, "
       "opos: %" G_GUINT64_FORMAT ", otime: %" GST_TIME_FORMAT ", %"
       GST_TIME_FORMAT " in stream time (start %" GST_TIME_FORMAT "), time %"
-      GST_TIME_FORMAT, opos, GST_TIME_ARGS (otime),
-      GST_TIME_ARGS (otime - demux->stream_start_time),
+      GST_TIME_FORMAT, apos, GST_TIME_ARGS (atime),
+      GST_TIME_ARGS (atime - demux->stream_start_time), opos,
+      GST_TIME_ARGS (otime), GST_TIME_ARGS (otime - demux->stream_start_time),
       GST_TIME_ARGS (demux->stream_start_time), GST_TIME_ARGS (time));
 
-  if (otime <= demux->stream_start_time) {
-    newpos = 0;
+  g_assert (atime <= otime);
+  g_assert (apos <= opos);
+  if (time == GST_CLOCK_TIME_NONE) {
+    GST_DEBUG_OBJECT (demux, "searching last cluster");
+    newpos = maxpos;
+    if (newpos == -1) {
+      GST_DEBUG_OBJECT (demux, "unknown file size; bailing out");
+      goto exit;
+    }
+  } else if (otime <= atime) {
+    newpos = apos;
   } else {
-    newpos =
-        gst_util_uint64_scale (opos - demux->common.ebml_segment_start,
-        time - demux->stream_start_time,
-        otime - demux->stream_start_time) - chunk;
-    if (newpos < 0)
-      newpos = 0;
+    newpos = apos +
+        gst_util_uint64_scale (opos - apos, time - atime, otime - atime);
+    if (maxpos != -1 && newpos > maxpos)
+      newpos = maxpos;
   }
-  /* favour undershoot */
-  newpos = newpos * 90 / 100;
-  newpos += demux->common.ebml_segment_start;
 
   GST_DEBUG_OBJECT (demux,
       "estimated offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT,
       GST_TIME_ARGS (time), newpos);
 
-  /* and at least start scanning before previous scan start to avoid looping */
-  startpos = startpos * 90 / 100;
-  if (startpos && startpos < newpos)
-    newpos = startpos;
-
-  /* read in at newpos and scan for ebml cluster id */
-  startpos = newpos;
-  while (1) {
-
-    ret = gst_matroska_demux_search_cluster (demux, &newpos);
-    if (ret == GST_FLOW_EOS) {
-      /* heuristic HACK */
-      newpos = startpos * 80 / 100;
-      GST_DEBUG_OBJECT (demux, "EOS; "
-          "new estimated offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT,
-          GST_TIME_ARGS (time), newpos);
-      startpos = newpos;
-      continue;
-    } else if (ret != GST_FLOW_OK) {
+  /* search backwards */
+  if (newpos > apos) {
+    ret = gst_matroska_demux_search_cluster (demux, &newpos, FALSE);
+    if (ret != GST_FLOW_OK)
       goto exit;
-    } else {
-      break;
-    }
   }
 
   /* then start scanning and parsing for cluster time,
-   * re-estimate if overshoot, otherwise next cluster and so on */
+   * re-estimate if possible, otherwise next cluster and so on */
+  /* note that each re-estimate is entered with a change in apos or opos,
+   * avoiding infinite loop */
   demux->common.offset = newpos;
   demux->cluster_time = cluster_time = GST_CLOCK_TIME_NONE;
+  cluster_size = 0;
+  prev_cluster_time = GST_CLOCK_TIME_NONE;
   while (1) {
-    guint64 cluster_size = 0;
-
     /* peek and parse some elements */
     ret = gst_matroska_read_common_peek_id_length_pull (&demux->common,
         GST_ELEMENT_CAST (demux), &id, &length, &needed);
@@ -1930,6 +2537,12 @@ retry:
       GST_DEBUG_OBJECT (demux, "found cluster at offset %" G_GINT64_FORMAT
           " with time %" GST_TIME_FORMAT, cluster_offset,
           GST_TIME_ARGS (cluster_time));
+      if (time == GST_CLOCK_TIME_NONE) {
+        GST_DEBUG_OBJECT (demux, "found last cluster");
+        prev_cluster_time = cluster_time;
+        prev_cluster_offset = cluster_offset;
+        break;
+      }
       if (cluster_time > time) {
         GST_DEBUG_OBJECT (demux, "overshot target");
         /* cluster overshoots */
@@ -1950,6 +2563,30 @@ retry:
           goto retry;
         }
       } else {
+        /* cluster undershoots */
+        GST_DEBUG_OBJECT (demux, "undershot target");
+        /* ok if close enough */
+        if (GST_CLOCK_DIFF (cluster_time, time) < 5 * GST_SECOND) {
+          GST_DEBUG_OBJECT (demux, "target close enough");
+          prev_cluster_time = cluster_time;
+          prev_cluster_offset = cluster_offset;
+          break;
+        }
+        if (otime > time) {
+          /* we are in between atime and otime => can bisect if worthwhile */
+          if (prev_cluster_time != GST_CLOCK_TIME_NONE &&
+              cluster_time > prev_cluster_time &&
+              (GST_CLOCK_DIFF (prev_cluster_time, cluster_time) * 10 <
+                  GST_CLOCK_DIFF (cluster_time, time))) {
+            /* we moved at least one cluster forward,
+             * and it looks like target is still far away,
+             * let's estimate again */
+            GST_DEBUG_OBJECT (demux, "bisecting with new apos");
+            apos = cluster_offset;
+            atime = cluster_time;
+            goto retry;
+          }
+        }
         /* cluster undershoots, goto next one */
         prev_cluster_time = cluster_time;
         prev_cluster_offset = cluster_offset;
@@ -1974,9 +2611,26 @@ retry:
     goto exit;
   }
 
+  /* In the bisect loop above we always undershoot and then jump forward
+   * cluster-by-cluster until we overshoot, so if we get here we've gone
+   * over and the previous cluster is where we need to go to. */
+  cluster_offset = prev_cluster_offset;
+  cluster_time = prev_cluster_time;
+
+  /* If we have video and can easily backtrack, check if we landed on a cluster
+   * that starts with a keyframe - and if not backtrack until we find one that
+   * does. */
+  if (demux->have_nonintraonly_v_streams && demux->max_backtrack_distance > 0) {
+    if (gst_matroska_demux_scan_back_for_keyframe_cluster (demux,
+            &cluster_offset, &cluster_time)) {
+      GST_INFO_OBJECT (demux, "Adjusted cluster to %" GST_TIME_FORMAT " @ "
+          "%" G_GUINT64_FORMAT, GST_TIME_ARGS (cluster_time), cluster_offset);
+    }
+  }
+
   entry = g_new0 (GstMatroskaIndex, 1);
-  entry->time = prev_cluster_time;
-  entry->pos = prev_cluster_offset - demux->common.ebml_segment_start;
+  entry->time = cluster_time;
+  entry->pos = cluster_offset - demux->common.ebml_segment_start;
   GST_DEBUG_OBJECT (demux, "simulated index entry; time %" GST_TIME_FORMAT
       ", pos %" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->time), entry->pos);
 
@@ -2364,9 +3018,14 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstObject * parent,
     case GST_EVENT_SEEK:
       /* no seeking until we are (safely) ready */
       if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
-        GST_DEBUG_OBJECT (demux, "not ready for seeking yet");
-        gst_event_unref (event);
-        return FALSE;
+        GST_DEBUG_OBJECT (demux,
+            "not ready for seeking yet, deferring seek event: %" GST_PTR_FORMAT,
+            event);
+        if (demux->deferred_seek_event)
+          gst_event_unref (demux->deferred_seek_event);
+        demux->deferred_seek_event = event;
+        demux->deferred_seek_pad = pad;
+        return TRUE;
       }
 
       {
@@ -2461,6 +3120,47 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstObject * parent,
   return res;
 }
 
+static gboolean
+gst_matroska_demux_handle_sink_query (GstPad * pad, GstObject * parent,
+    GstQuery * query)
+{
+  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (parent);
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_BITRATE:
+    {
+      if (G_UNLIKELY (demux->cached_length == G_MAXUINT64 ||
+              demux->common.offset >= demux->cached_length)) {
+        demux->cached_length =
+            gst_matroska_read_common_get_length (&demux->common);
+      }
+
+      if (demux->cached_length < G_MAXUINT64
+          && demux->common.segment.duration > 0) {
+        /* TODO: better results based on ranges/index tables */
+        guint bitrate =
+            gst_util_uint64_scale (8 * demux->cached_length, GST_SECOND,
+            demux->common.segment.duration);
+
+        GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
+            " duration %" GST_TIME_FORMAT " resulting in a bitrate of %u",
+            demux->cached_length,
+            GST_TIME_ARGS (demux->common.segment.duration), bitrate);
+
+        gst_query_set_bitrate (query, bitrate);
+        res = TRUE;
+      }
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, (GstObject *) demux, query);
+      break;
+  }
+
+  return res;
+}
+
 static GstFlowReturn
 gst_matroska_demux_seek_to_previous_keyframe (GstMatroskaDemux * demux)
 {
@@ -2533,9 +3233,23 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml)
 
     switch (id) {
         /* one track within the "all-tracks" header */
-      case GST_MATROSKA_ID_TRACKENTRY:
-        ret = gst_matroska_demux_add_stream (demux, ebml);
+      case GST_MATROSKA_ID_TRACKENTRY:{
+        GstMatroskaTrackContext *track;
+        ret = gst_matroska_demux_parse_stream (demux, ebml, &track);
+        if (track != NULL) {
+          if (gst_matroska_read_common_tracknumber_unique (&demux->common,
+                  track->num)) {
+            gst_matroska_demux_add_stream (demux, track);
+          } else {
+            GST_ERROR_OBJECT (demux,
+                "TrackNumber %" G_GUINT64_FORMAT " is not unique", track->num);
+            ret = GST_FLOW_ERROR;
+            gst_matroska_track_free (track);
+            track = NULL;
+          }
+        }
         break;
+      }
 
       default:
         ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
@@ -2546,6 +3260,116 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
 
   demux->tracks_parsed = TRUE;
+  GST_DEBUG_OBJECT (demux, "signaling no more pads");
+  gst_element_no_more_pads (GST_ELEMENT (demux));
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_matroska_demux_update_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint num_tracks_found = 0;
+  guint32 id;
+
+  GST_INFO_OBJECT (demux, "Reparsing Tracks element");
+
+  DEBUG_ELEMENT_START (demux, ebml, "Tracks");
+
+  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+    DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
+    return ret;
+  }
+
+  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
+      break;
+
+    switch (id) {
+        /* one track within the "all-tracks" header */
+      case GST_MATROSKA_ID_TRACKENTRY:{
+        GstMatroskaTrackContext *new_track;
+        gint old_track_index;
+        GstMatroskaTrackContext *old_track;
+        ret = gst_matroska_demux_parse_stream (demux, ebml, &new_track);
+        if (new_track == NULL)
+          break;
+        num_tracks_found++;
+
+        if (gst_matroska_read_common_tracknumber_unique (&demux->common,
+                new_track->num)) {
+          GST_ERROR_OBJECT (demux,
+              "Unexpected new TrackNumber: %" G_GUINT64_FORMAT, new_track->num);
+          goto track_mismatch_error;
+        }
+
+        old_track_index =
+            gst_matroska_read_common_stream_from_num (&demux->common,
+            new_track->num);
+        g_assert (old_track_index != -1);
+        old_track = g_ptr_array_index (demux->common.src, old_track_index);
+
+        if (old_track->type != new_track->type) {
+          GST_ERROR_OBJECT (demux,
+              "Mismatch reparsing track %" G_GUINT64_FORMAT
+              " on track type. Expected %d, found %d", new_track->num,
+              old_track->type, new_track->type);
+          goto track_mismatch_error;
+        }
+
+        if (g_strcmp0 (old_track->codec_id, new_track->codec_id) != 0) {
+          GST_ERROR_OBJECT (demux,
+              "Mismatch reparsing track %" G_GUINT64_FORMAT
+              " on codec id. Expected '%s', found '%s'", new_track->num,
+              old_track->codec_id, new_track->codec_id);
+          goto track_mismatch_error;
+        }
+
+        /* The new track matches the old track. No problems on our side.
+         * Let's make it replace the old track. */
+        new_track->pad = old_track->pad;
+        new_track->index = old_track->index;
+        new_track->pos = old_track->pos;
+        g_ptr_array_index (demux->common.src, old_track_index) = new_track;
+        gst_pad_set_element_private (new_track->pad, new_track);
+
+        if (!gst_caps_is_equal (old_track->caps, new_track->caps)) {
+          gst_pad_set_caps (new_track->pad, new_track->caps);
+        }
+        gst_caps_replace (&old_track->caps, NULL);
+
+        if (!gst_tag_list_is_equal (old_track->tags, new_track->tags)) {
+          GST_DEBUG_OBJECT (old_track->pad, "Sending tags %p: %"
+              GST_PTR_FORMAT, new_track->tags, new_track->tags);
+          gst_pad_push_event (new_track->pad,
+              gst_event_new_tag (gst_tag_list_copy (new_track->tags)));
+        }
+
+        gst_matroska_track_free (old_track);
+        break;
+
+      track_mismatch_error:
+        gst_matroska_track_free (new_track);
+        new_track = NULL;
+        ret = GST_FLOW_ERROR;
+        break;
+      }
+
+      default:
+        ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+            "Track", id);
+        break;
+    }
+  }
+  DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
+
+  if (ret != GST_FLOW_ERROR && demux->common.num_streams != num_tracks_found) {
+    GST_ERROR_OBJECT (demux,
+        "Mismatch on the number of tracks. Expected %du tracks, found %du",
+        demux->common.num_streams, num_tracks_found);
+    ret = GST_FLOW_ERROR;
+  }
 
   return ret;
 }
@@ -3494,6 +4318,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
     gboolean delta_unit = FALSE;
     guint64 duration = 0;
     gint64 lace_time = 0;
+    GstEvent *protect_event;
 
     stream = g_ptr_array_index (demux->common.src, stream_num);
 
@@ -3513,6 +4338,12 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
     } else {
       lace_time = GST_CLOCK_TIME_NONE;
     }
+    /* Send the GST_PROTECTION event */
+    while ((protect_event = g_queue_pop_head (&stream->protection_event_queue))) {
+      GST_TRACE_OBJECT (demux, "pushing protection event for stream %d:%s",
+          stream->index, GST_STR_NULL (stream->name));
+      gst_pad_push_event (stream->pad, protect_event);
+    }
 
     /* need to refresh segment info ASAP */
     if (GST_CLOCK_TIME_IS_VALID (lace_time) && demux->need_segment) {
@@ -3532,7 +4363,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         GST_DEBUG_OBJECT (demux,
             "using stored seek position %" GST_TIME_FORMAT,
             GST_TIME_ARGS (demux->common.segment.position));
-        clace_time = demux->common.segment.position + demux->stream_start_time;
+        clace_time = demux->common.segment.position;
         segment->position = GST_CLOCK_TIME_NONE;
       }
       segment->start = clace_time;
@@ -3577,14 +4408,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         delta_unit = TRUE;
         invisible_frame = ((flags & 0x08)) &&
             (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8) ||
-            !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9));
+            !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9) ||
+            !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_AV1));
       }
 
       /* If we're doing a keyframe-only trickmode, only push keyframes on video
        * streams */
       if (delta_unit
-          && demux->common.
-          segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) {
+          && demux->common.segment.
+          flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) {
         GST_LOG_OBJECT (demux, "Skipping non-keyframe on stream %d",
             stream->index);
         ret = GST_FLOW_OK;
@@ -3821,6 +4653,13 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
             GST_BUFFER_PTS (sub) -= stream->codec_delay;
           } else {
             GST_BUFFER_PTS (sub) = 0;
+
+            /* Opus GstAudioClippingMeta units are scaled by 48000/sample_rate.
+               That is, if a Opus track has audio encoded at 24000 Hz and 132
+               samples need to be clipped, GstAudioClippingMeta.start will be
+               set to 264. (This is also the case for buffer offsets.)
+               Opus sample rates are always divisors of 48000 Hz, which is the
+               maximum allowed sample rate. */
             start_clip =
                 gst_util_uint64_scale_round (stream->codec_delay, 48000,
                 GST_SECOND);
@@ -4176,7 +5015,8 @@ gst_matroska_demux_check_parse_error (GstMatroskaDemux * demux)
      * search for cluster mark following current pos */
     pos = demux->common.offset;
     GST_WARNING_OBJECT (demux, "parse error, looking for next cluster");
-    if ((ret = gst_matroska_demux_search_cluster (demux, &pos)) != GST_FLOW_OK) {
+    if ((ret = gst_matroska_demux_search_cluster (demux, &pos, TRUE)) !=
+        GST_FLOW_OK) {
       /* did not work, give up */
       return ret;
     } else {
@@ -4402,6 +5242,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
       break;
     case GST_MATROSKA_READ_STATE_SCANNING:
       if (id != GST_MATROSKA_ID_CLUSTER &&
+          id != GST_MATROSKA_ID_PREVSIZE &&
           id != GST_MATROSKA_ID_CLUSTERTIMECODE) {
         if (demux->common.start_resync_offset != -1) {
           /* we need to skip byte per byte if we are scanning for a new cluster
@@ -4422,6 +5263,11 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
     case GST_MATROSKA_READ_STATE_DATA:
     case GST_MATROSKA_READ_STATE_SEEK:
       switch (id) {
+        case GST_EBML_ID_HEADER:
+          GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+          demux->common.state = GST_MATROSKA_READ_STATE_SEGMENT;
+          gst_matroska_demux_check_seekability (demux);
+          break;
         case GST_MATROSKA_ID_SEGMENTINFO:
           if (!demux->common.segmentinfo_parsed) {
             GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
@@ -4434,11 +5280,11 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
           }
           break;
         case GST_MATROSKA_ID_TRACKS:
+          GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
           if (!demux->tracks_parsed) {
-            GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
             ret = gst_matroska_demux_parse_tracks (demux, &ebml);
           } else {
-            GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+            ret = gst_matroska_demux_update_tracks (demux, &ebml);
           }
           break;
         case GST_MATROSKA_ID_CLUSTER:
@@ -4452,12 +5298,62 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
                 goto no_tracks;
             }
           }
-          if (G_UNLIKELY (demux->common.state
-                  == GST_MATROSKA_READ_STATE_HEADER)) {
+          if (demux->common.state == GST_MATROSKA_READ_STATE_HEADER) {
             demux->common.state = GST_MATROSKA_READ_STATE_DATA;
             demux->first_cluster_offset = demux->common.offset;
-            GST_DEBUG_OBJECT (demux, "signaling no more pads");
-            gst_element_no_more_pads (GST_ELEMENT (demux));
+
+            if (!demux->streaming &&
+                !GST_CLOCK_TIME_IS_VALID (demux->common.segment.duration)) {
+              GstMatroskaIndex *last = NULL;
+
+              GST_DEBUG_OBJECT (demux,
+                  "estimating duration using last cluster");
+              if ((last = gst_matroska_demux_search_pos (demux,
+                          GST_CLOCK_TIME_NONE)) != NULL) {
+                demux->last_cluster_offset =
+                    last->pos + demux->common.ebml_segment_start;
+                demux->stream_last_time = last->time;
+                demux->common.segment.duration =
+                    demux->stream_last_time - demux->stream_start_time;
+                /* above estimate should not be taken all too strongly */
+                demux->invalid_duration = TRUE;
+                GST_DEBUG_OBJECT (demux,
+                    "estimated duration as %" GST_TIME_FORMAT,
+                    GST_TIME_ARGS (demux->common.segment.duration));
+
+                g_free (last);
+              }
+            }
+
+            /* Peek at second cluster in order to figure out if we have cluster
+             * prev_size or not (which is never set on the first cluster for
+             * obvious reasons). This is useful in case someone initiates a
+             * seek or direction change before we reach the second cluster. */
+            if (!demux->streaming) {
+              ClusterInfo cluster = { 0, };
+
+              if (gst_matroska_demux_peek_cluster_info (demux, &cluster,
+                      demux->first_cluster_offset) && cluster.size > 0) {
+                gst_matroska_demux_peek_cluster_info (demux, &cluster,
+                    demux->first_cluster_offset + cluster.size);
+              }
+              demux->common.offset = demux->first_cluster_offset;
+            }
+
+            if (demux->deferred_seek_event) {
+              GstEvent *seek_event;
+              GstPad *seek_pad;
+              seek_event = demux->deferred_seek_event;
+              seek_pad = demux->deferred_seek_pad;
+              demux->deferred_seek_event = NULL;
+              demux->deferred_seek_pad = NULL;
+              GST_DEBUG_OBJECT (demux,
+                  "Handling deferred seek event: %" GST_PTR_FORMAT, seek_event);
+              gst_matroska_demux_handle_seek_event (demux, seek_pad,
+                  seek_event);
+              gst_event_unref (seek_event);
+            }
+
             /* send initial segment - we wait till we know the first
                incoming timestamp, so we can properly set the start of
                the segment. */
@@ -4465,6 +5361,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
           }
           demux->cluster_time = GST_CLOCK_TIME_NONE;
           demux->cluster_offset = demux->common.offset;
+          demux->cluster_prevsize = 0;
           if (G_UNLIKELY (!demux->seek_first && demux->seek_block)) {
             GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT
                 " not found in Cluster, trying next Cluster's first block instead",
@@ -4487,6 +5384,12 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
             goto parse_failed;
           GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
           demux->cluster_time = num;
+          /* track last cluster */
+          if (demux->cluster_offset > demux->last_cluster_offset) {
+            demux->last_cluster_offset = demux->cluster_offset;
+            demux->stream_last_time =
+                demux->cluster_time * demux->common.time_scale;
+          }
 #if 0
           if (demux->common.element_index) {
             if (demux->common.element_index_writer_id == -1)
@@ -4590,9 +5493,22 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
             GST_OBJECT_UNLOCK (demux);
           }
           break;
+        case GST_MATROSKA_ID_PREVSIZE:{
+          guint64 num;
+
+          GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+          if ((ret = gst_ebml_read_uint (&ebml, &id, &num)) != GST_FLOW_OK)
+            goto parse_failed;
+          GST_LOG_OBJECT (demux, "ClusterPrevSize: %" G_GUINT64_FORMAT, num);
+          demux->cluster_prevsize = num;
+          demux->seen_cluster_prevsize = TRUE;
+          break;
+        }
         case GST_MATROSKA_ID_POSITION:
-        case GST_MATROSKA_ID_PREVSIZE:
         case GST_MATROSKA_ID_ENCRYPTEDBLOCK:
+          /* The WebM doesn't support the EncryptedBlock element.
+           * The Matroska spec doesn't give us more detail, how to parse this element,
+           * for example the field TransformID isn't specified yet.*/
         case GST_MATROSKA_ID_SILENTTRACKS:
           GST_DEBUG_OBJECT (demux,
               "Skipping Cluster subelement 0x%x - ignoring", id);
@@ -4962,6 +5878,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
       demux->common.segment.position = GST_CLOCK_TIME_NONE;
       demux->cluster_time = GST_CLOCK_TIME_NONE;
       demux->cluster_offset = 0;
+      demux->cluster_prevsize = 0;
       demux->need_segment = TRUE;
       demux->segment_seqnum = gst_event_get_seqnum (event);
       /* but keep some of the upstream segment */
@@ -5014,6 +5931,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
       demux->common.segment.duration = dur;
       demux->cluster_time = GST_CLOCK_TIME_NONE;
       demux->cluster_offset = 0;
+      demux->cluster_prevsize = 0;
       GST_OBJECT_UNLOCK (demux);
       /* fall-through */
     }
@@ -5350,6 +6268,18 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9)) {
     caps = gst_caps_new_empty_simple ("video/x-vp9");
     *codec_name = g_strdup_printf ("On2 VP9");
+  } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_AV1)) {
+    caps = gst_caps_new_empty_simple ("video/x-av1");
+    if (data) {
+      GstBuffer *priv;
+
+      priv = gst_buffer_new_wrapped (g_memdup (data, size), size);
+      gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL);
+      gst_buffer_unref (priv);
+    } else {
+      GST_WARNING ("No AV1 codec data found!");
+    }
+    *codec_name = g_strdup_printf ("AOM AV1");
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
     guint32 fourcc;
     const gchar *variant, *variant_descr = "";
@@ -5461,9 +6391,18 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
             0, 1, NULL);
       }
 
-      if (videocontext->parent.flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
-        gst_structure_set (structure, "interlace-mode", G_TYPE_STRING,
-            "mixed", NULL);
+      switch (videocontext->interlace_mode) {
+        case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE:
+          gst_structure_set (structure,
+              "interlace-mode", G_TYPE_STRING, "progressive", NULL);
+          break;
+        case GST_MATROSKA_INTERLACE_MODE_INTERLACED:
+          gst_structure_set (structure,
+              "interlace-mode", G_TYPE_STRING, "mixed", NULL);
+          break;
+        default:
+          break;
+      }
     }
     if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
       if (gst_video_multiview_guess_half_aspect (videocontext->multiview_mode,
@@ -5480,6 +6419,19 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
           GST_FLAG_SET_MASK_EXACT, NULL);
     }
 
+    if (videocontext->colorimetry.range != GST_VIDEO_COLOR_RANGE_UNKNOWN ||
+        videocontext->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN ||
+        videocontext->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN ||
+        videocontext->colorimetry.primaries !=
+        GST_VIDEO_COLOR_PRIMARIES_UNKNOWN) {
+      gchar *colorimetry =
+          gst_video_colorimetry_to_string (&videocontext->colorimetry);
+      gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, colorimetry,
+          NULL);
+      GST_DEBUG ("setting colorimetry to %s", colorimetry);
+      g_free (colorimetry);
+    }
+
     caps = gst_caps_simplify (caps);
   }
 
@@ -5750,8 +6702,8 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
         *riff_audio_fmt = auds.format;
 
       /* FIXME: Handle reorder map */
-      caps = gst_riff_create_audio_caps (auds.format, NULL, &auds, NULL,
-          codec_data, codec_name, NULL);
+      caps = gst_riff_create_audio_caps (auds.format, NULL, &auds, codec_data,
+          NULL, codec_name, NULL);
       if (codec_data)
         gst_buffer_unref (codec_data);
 
@@ -5887,7 +6839,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
       guint sample_width;
       guint extra_data_size;
 
-      GST_ERROR ("real audio raversion:%d", raversion);
+      GST_DEBUG ("real audio raversion:%d", raversion);
       if (raversion == 8) {
         /* COOK */
         flavor = GST_READ_UINT16_BE (data + 22);
@@ -5897,7 +6849,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
         sample_width = GST_READ_UINT16_BE (data + 58);
         extra_data_size = GST_READ_UINT32_BE (data + 74);
 
-        GST_ERROR
+        GST_DEBUG
             ("flavor:%d, packet_size:%d, height:%d, leaf_size:%d, sample_width:%d, extra_data_size:%d",
             flavor, packet_size, height, leaf_size, sample_width,
             extra_data_size);
@@ -6086,6 +7038,11 @@ gst_matroska_demux_set_property (GObject * object,
       demux->max_gap_time = g_value_get_uint64 (value);
       GST_OBJECT_UNLOCK (demux);
       break;
+    case PROP_MAX_BACKTRACK_DISTANCE:
+      GST_OBJECT_LOCK (demux);
+      demux->max_backtrack_distance = g_value_get_uint (value);
+      GST_OBJECT_UNLOCK (demux);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -6107,12 +7064,48 @@ gst_matroska_demux_get_property (GObject * object,
       g_value_set_uint64 (value, demux->max_gap_time);
       GST_OBJECT_UNLOCK (demux);
       break;
+    case PROP_MAX_BACKTRACK_DISTANCE:
+      GST_OBJECT_LOCK (demux);
+      g_value_set_uint (value, demux->max_backtrack_distance);
+      GST_OBJECT_UNLOCK (demux);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+static const gchar *
+gst_matroska_track_encryption_algorithm_name (gint val)
+{
+  GEnumValue *en;
+  GEnumClass *enum_class =
+      g_type_class_ref (MATROSKA_TRACK_ENCRYPTION_ALGORITHM_TYPE);
+  en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+  return en ? en->value_nick : NULL;
+}
+
+static const gchar *
+gst_matroska_track_encryption_cipher_mode_name (gint val)
+{
+  GEnumValue *en;
+  GEnumClass *enum_class =
+      g_type_class_ref (MATROSKA_TRACK_ENCRYPTION_CIPHER_MODE_TYPE);
+  en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+  return en ? en->value_nick : NULL;
+}
+
+static const gchar *
+gst_matroska_track_encoding_scope_name (gint val)
+{
+  GEnumValue *en;
+  GEnumClass *enum_class =
+      g_type_class_ref (MATROSKA_TRACK_ENCODING_SCOPE_TYPE);
+
+  en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+  return en ? en->value_nick : NULL;
+}
+
 gboolean
 gst_matroska_demux_plugin_init (GstPlugin * plugin)
 {