matroskademux: extract cluster prevsize if available
[platform/upstream/gst-plugins-good.git] / gst / matroska / matroska-demux.c
index 489c00a..8241104 100644 (file)
@@ -80,13 +80,14 @@ GST_DEBUG_CATEGORY_STATIC (matroskademux_debug);
 
 enum
 {
-  ARG_0,
-  ARG_METADATA,
-  ARG_STREAMINFO,
-  ARG_MAX_GAP_TIME
+  PROP_0,
+  PROP_METADATA,
+  PROP_STREAMINFO,
+  PROP_MAX_GAP_TIME
 };
 
 #define  DEFAULT_MAX_GAP_TIME      (2 * GST_SECOND)
+#define  INVALID_DATA_THRESHOLD    (2 * 1024 * 1024)
 
 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -172,7 +173,7 @@ static GstCaps
 /* stream methods */
 static void gst_matroska_demux_reset (GstElement * element);
 static gboolean perform_seek_to_offset (GstMatroskaDemux * demux,
-    gdouble rate, guint64 offset, guint32 seqnum);
+    gdouble rate, guint64 offset, guint32 seqnum, GstSeekFlags flags);
 
 /* gobject functions */
 static void gst_matroska_demux_set_property (GObject * object,
@@ -208,7 +209,7 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
   gobject_class->get_property = gst_matroska_demux_get_property;
   gobject_class->set_property = gst_matroska_demux_set_property;
 
-  g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME,
+  g_object_class_install_property (gobject_class, PROP_MAX_GAP_TIME,
       g_param_spec_uint64 ("max-gap-time", "Maximum gap time",
           "The demuxer sends out segment events for skipping "
           "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
@@ -227,19 +228,18 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
       GST_DEBUG_FUNCPTR (gst_matroska_demux_get_index);
 #endif
 
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&video_src_templ));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&audio_src_templ));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&subtitle_src_templ));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&sink_templ));
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &video_src_templ);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &audio_src_templ);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &subtitle_src_templ);
+  gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
 
   gst_element_class_set_static_metadata (gstelement_class, "Matroska demuxer",
       "Codec/Demuxer",
       "Demuxes Matroska/WebM streams into video/audio/subtitles",
-      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
+      "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
 }
 
 static void
@@ -306,7 +306,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;
@@ -390,6 +394,189 @@ gst_matroska_demux_add_stream_headers_to_caps (GstMatroskaDemux * demux,
 }
 
 static GstFlowReturn
+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_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
@@ -397,11 +584,11 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   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;
-  GstTagList *list = NULL;
   GstEvent *stream_start;
   gchar *codec = NULL;
   gchar *stream_id;
@@ -433,6 +620,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   context->to_offset = G_MAXINT64;
   context->alignment = 1;
   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);
 
@@ -675,13 +864,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;
             }
 
@@ -731,11 +925,67 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
               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;
+
+              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+                break;
+
+              GST_DEBUG_OBJECT (demux, "StereoMode: %" G_GUINT64_FORMAT, num);
+
+              switch (num) {
+                case GST_MATROSKA_STEREO_MODE_SBS_RL:
+                  videocontext->multiview_flags =
+                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
+                  /* fall through */
+                case GST_MATROSKA_STEREO_MODE_SBS_LR:
+                  videocontext->multiview_mode =
+                      GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
+                  break;
+                case GST_MATROSKA_STEREO_MODE_TB_RL:
+                  videocontext->multiview_flags =
+                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
+                  /* fall through */
+                case GST_MATROSKA_STEREO_MODE_TB_LR:
+                  videocontext->multiview_mode =
+                      GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM;
+                  break;
+                case GST_MATROSKA_STEREO_MODE_CHECKER_RL:
+                  videocontext->multiview_flags =
+                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
+                  /* fall through */
+                case GST_MATROSKA_STEREO_MODE_CHECKER_LR:
+                  videocontext->multiview_mode =
+                      GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD;
+                  break;
+                case GST_MATROSKA_STEREO_MODE_FBF_RL:
+                  videocontext->multiview_flags =
+                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
+                  /* fall through */
+                case GST_MATROSKA_STEREO_MODE_FBF_LR:
+                  videocontext->multiview_mode =
+                      GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
+                  /* FIXME: In frame-by-frame mode, left/right frame buffers are
+                   * laced within one block, and we'll need to apply FIRST_IN_BUNDLE
+                   * accordingly. See http://www.matroska.org/technical/specs/index.html#StereoMode */
+                  GST_FIXME_OBJECT (demux,
+                      "Frame-by-frame stereoscopic mode not fully implemented");
+                  break;
+              }
+              break;
+            }
+
             default:
               GST_WARNING_OBJECT (demux,
                   "Unknown TrackVideo subelement 0x%x - ignoring", id);
               /* fall through */
-            case GST_MATROSKA_ID_VIDEOSTEREOMODE:
             case GST_MATROSKA_ID_VIDEODISPLAYUNIT:
             case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM:
             case GST_MATROSKA_ID_VIDEOPIXELCROPTOP:
@@ -889,6 +1139,34 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
         break;
       }
 
+        /* codec delay */
+      case GST_MATROSKA_ID_CODECDELAY:{
+        guint64 num;
+
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          break;
+
+        context->codec_delay = num;
+
+        GST_DEBUG_OBJECT (demux, "CodecDelay: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (num));
+        break;
+      }
+
+        /* codec delay */
+      case GST_MATROSKA_ID_SEEKPREROLL:{
+        guint64 num;
+
+        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+          break;
+
+        context->seek_preroll = num;
+
+        GST_DEBUG_OBJECT (demux, "SeekPreroll: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (num));
+        break;
+      }
+
         /* name of this track */
       case GST_MATROSKA_ID_TRACKNAME:{
         gchar *text;
@@ -1073,13 +1351,18 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
     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);
-    if (context) {
-      gst_matroska_track_free (context);
-    }
+    gst_matroska_track_free (context);
 
     return ret;
   }
 
+  /* check for a cached track taglist  */
+  cached_taglist =
+      (GstTagList *) g_hash_table_lookup (demux->common.cached_track_taglists,
+      GUINT_TO_POINTER (context->uid));
+  if (cached_taglist)
+    gst_tag_list_insert (context->tags, cached_taglist, GST_TAG_MERGE_APPEND);
+
   /* now create the GStreamer connectivity */
   switch (context->type) {
     case GST_MATROSKA_TRACK_TYPE_VIDEO:{
@@ -1093,7 +1376,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
           context->codec_priv_size, &codec, &riff_fourcc);
 
       if (codec) {
-        list = gst_tag_list_new (GST_TAG_VIDEO_CODEC, codec, NULL);
+        gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
+            GST_TAG_VIDEO_CODEC, codec, NULL);
+        context->tags_changed = TRUE;
         g_free (codec);
       }
       break;
@@ -1110,7 +1395,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
           &codec, &riff_audio_fmt);
 
       if (codec) {
-        list = gst_tag_list_new (GST_TAG_AUDIO_CODEC, codec, NULL);
+        gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
+            GST_TAG_AUDIO_CODEC, codec, NULL);
+        context->tags_changed = TRUE;
         g_free (codec);
       }
       break;
@@ -1146,13 +1433,16 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   if (context->language) {
     const gchar *lang;
 
-    if (!list)
-      list = gst_tag_list_new_empty ();
-
     /* Matroska contains ISO 639-2B codes, we want ISO 639-1 */
     lang = gst_tag_get_language_code (context->language);
-    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+    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;
   }
 
   if (caps == NULL) {
@@ -1202,8 +1492,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
   GST_INFO_OBJECT (demux, "Adding pad '%s' with caps %" GST_PTR_FORMAT,
       padname, caps);
 
-  context->pending_tags = list;
-
   gst_pad_set_element_private (context->pad, context);
 
   gst_pad_use_fixed_caps (context->pad);
@@ -1211,7 +1499,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
 
   stream_id =
       gst_pad_create_stream_id_printf (context->pad, GST_ELEMENT_CAST (demux),
-      "%03" G_GUINT64_FORMAT, context->uid);
+      "%03" G_GUINT64_FORMAT ":%03" G_GUINT64_FORMAT,
+      context->num, context->uid);
   stream_start =
       gst_pad_get_sticky_event (demux->common.sinkpad, GST_EVENT_STREAM_START,
       0);
@@ -1235,10 +1524,36 @@ 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);
 
+
+  if (demux->common.global_tags) {
+    GstEvent *tag_event;
+
+    gst_tag_list_add (demux->common.global_tags, GST_TAG_MERGE_REPLACE,
+        GST_TAG_CONTAINER_FORMAT, "Matroska", NULL);
+    GST_DEBUG_OBJECT (context->pad, "Sending global_tags %p: %" GST_PTR_FORMAT,
+        demux->common.global_tags, demux->common.global_tags);
+
+    tag_event =
+        gst_event_new_tag (gst_tag_list_copy (demux->common.global_tags));
+
+    gst_pad_push_event (context->pad, tag_event);
+  }
+
+  if (G_UNLIKELY (context->tags_changed)) {
+    GST_DEBUG_OBJECT (context->pad, "Sending tags %p: %"
+        GST_PTR_FORMAT, context->tags, context->tags);
+    gst_pad_push_event (context->pad,
+        gst_event_new_tag (gst_tag_list_copy (context->tags)));
+    context->tags_changed = FALSE;
+  }
+
   gst_element_add_pad (GST_ELEMENT (demux), context->pad);
   gst_flow_combiner_add_pad (demux->flowcombiner, context->pad);
 
@@ -1423,14 +1738,15 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux)
 {
   gint i;
 
-  if (G_UNLIKELY (demux->common.global_tags != NULL)) {
+  if (G_UNLIKELY (demux->common.global_tags_changed)) {
     GstEvent *tag_event;
     gst_tag_list_add (demux->common.global_tags, GST_TAG_MERGE_REPLACE,
         GST_TAG_CONTAINER_FORMAT, "Matroska", NULL);
     GST_DEBUG_OBJECT (demux, "Sending global_tags %p : %" GST_PTR_FORMAT,
         demux->common.global_tags, demux->common.global_tags);
 
-    tag_event = gst_event_new_tag (demux->common.global_tags);
+    tag_event =
+        gst_event_new_tag (gst_tag_list_copy (demux->common.global_tags));
 
     for (i = 0; i < demux->common.src->len; i++) {
       GstMatroskaTrackContext *stream;
@@ -1440,7 +1756,7 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux)
     }
 
     gst_event_unref (tag_event);
-    demux->common.global_tags = NULL;
+    demux->common.global_tags_changed = FALSE;
   }
 
   g_assert (demux->common.src->len == demux->common.num_streams);
@@ -1449,13 +1765,13 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux)
 
     stream = g_ptr_array_index (demux->common.src, i);
 
-    if (G_UNLIKELY (stream->pending_tags != NULL)) {
-      GST_DEBUG_OBJECT (demux, "Sending pending_tags %p for pad %s:%s : %"
-          GST_PTR_FORMAT, stream->pending_tags,
-          GST_DEBUG_PAD_NAME (stream->pad), stream->pending_tags);
+    if (G_UNLIKELY (stream->tags_changed)) {
+      GST_DEBUG_OBJECT (demux, "Sending tags %p for pad %s:%s : %"
+          GST_PTR_FORMAT, stream->tags,
+          GST_DEBUG_PAD_NAME (stream->pad), stream->tags);
       gst_pad_push_event (stream->pad,
-          gst_event_new_tag (stream->pending_tags));
-      stream->pending_tags = NULL;
+          gst_event_new_tag (gst_tag_list_copy (stream->tags)));
+      stream->tags_changed = FALSE;
     }
   }
 }
@@ -1469,6 +1785,12 @@ gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event)
   g_return_val_if_fail (event != NULL, FALSE);
 
   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;
+    }
     res = gst_matroska_demux_handle_seek_event (demux, NULL, event);
   } else {
     GST_WARNING_OBJECT (demux, "Unhandled event of type %s",
@@ -1500,6 +1822,7 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux,
 
     /* update the time */
     gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE);
+    gst_flow_combiner_reset (demux->flowcombiner);
     demux->common.segment.position = entry->time;
     demux->seek_block = entry->block;
     demux->seek_first = TRUE;
@@ -1538,12 +1861,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;
@@ -1555,8 +1879,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;
@@ -1564,7 +1888,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,
@@ -1584,13 +1908,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,
@@ -1609,16 +1943,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:
@@ -1636,7 +1979,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",
@@ -1651,16 +1994,18 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos)
       ret = gst_matroska_read_common_peek_id_length_pull (&demux->common,
           GST_ELEMENT_CAST (demux), &id, &length, &needed);
       if (ret != GST_FLOW_OK)
-        goto resume;
+        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 */
-      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;
     }
   }
 
@@ -1684,19 +2029,18 @@ 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 */
   current_state = demux->common.state;
@@ -1708,77 +2052,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);
+
+  maxpos = gst_matroska_read_common_get_length (&demux->common);
 
-  /* avoid division by zero in first estimation below */
-  if (otime <= demux->stream_start_time)
-    otime = time;
+  /* 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);
@@ -1805,6 +2150,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 */
@@ -1825,6 +2176,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;
@@ -2049,13 +2424,19 @@ next:
   }
 
 finish:
-  if (keyunit) {
+  if (keyunit && seeksegment.rate > 0) {
     GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment start from %"
         GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
         GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (entry->time));
     seeksegment.start = MAX (entry->time, demux->stream_start_time);
     seeksegment.position = seeksegment.start;
     seeksegment.time = seeksegment.start - demux->stream_start_time;
+  } else if (keyunit) {
+    GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment stop from %"
+        GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (seeksegment.stop), GST_TIME_ARGS (entry->time));
+    seeksegment.stop = MAX (entry->time, demux->stream_start_time);
+    seeksegment.position = seeksegment.stop;
   }
 
   if (demux->streaming) {
@@ -2069,7 +2450,7 @@ finish:
     /* upstream takes care of flushing and all that
      * ... and newsegment event handling takes care of the rest */
     return perform_seek_to_offset (demux, rate,
-        entry->pos + demux->common.ebml_segment_start, seqnum);
+        entry->pos + demux->common.ebml_segment_start, seqnum, flags);
   }
 
 exit:
@@ -2112,6 +2493,7 @@ exit:
     demux->to_time = demux->common.segment.position;
   else
     demux->to_time = GST_CLOCK_TIME_NONE;
+  demux->segment_seqnum = seqnum;
   GST_OBJECT_UNLOCK (demux);
 
   /* restart our task since it might have been stopped when we did the
@@ -2210,7 +2592,7 @@ gst_matroska_demux_handle_seek_push (GstMatroskaDemux * demux, GstPad * pad,
       /* seek to the first subindex or legacy index */
       GST_INFO_OBJECT (demux, "Seeking to Cues at %" G_GUINT64_FORMAT, offset);
       return perform_seek_to_offset (demux, rate, offset,
-          gst_event_get_seqnum (event));
+          gst_event_get_seqnum (event), GST_SEEK_FLAG_NONE);
     }
 
     /* well, we are handling it already */
@@ -2233,8 +2615,20 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstObject * parent,
       /* 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;
       }
+
+      {
+        guint32 seqnum = gst_event_get_seqnum (event);
+        if (seqnum == demux->segment_seqnum) {
+          GST_LOG_OBJECT (pad,
+              "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
+          gst_event_unref (event);
+          return TRUE;
+        }
+      }
+
       if (!demux->streaming)
         res = gst_matroska_demux_handle_seek_event (demux, pad, event);
       else
@@ -2475,6 +2869,7 @@ gst_matroska_ebmlnum_sint (guint8 * data, guint size, gint64 * num)
 static void
 gst_matroska_demux_sync_streams (GstMatroskaDemux * demux)
 {
+  GstClockTime gap_threshold;
   gint stream_nr;
 
   GST_OBJECT_LOCK (demux);
@@ -2492,21 +2887,22 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux)
         "Checking for resync on stream %d (%" GST_TIME_FORMAT ")", stream_nr,
         GST_TIME_ARGS (context->pos));
 
-    if (G_LIKELY (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE)) {
-      GST_LOG_OBJECT (demux, "Skipping sync on non-subtitle stream");
-      continue;
-    }
+    /* Only send gap events on non-subtitle streams if lagging way behind.
+     * The 0.5 second threshold for subtitle streams is also quite random. */
+    if (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE)
+      gap_threshold = GST_SECOND / 2;
+    else
+      gap_threshold = 3 * GST_SECOND;
 
-    /* does it lag? 0.5 seconds is a random threshold...
-     * lag need only be considered if we have advanced into requested segment */
+    /* Lag need only be considered if we have advanced into requested segment */
     if (GST_CLOCK_TIME_IS_VALID (context->pos) &&
         GST_CLOCK_TIME_IS_VALID (demux->common.segment.position) &&
         demux->common.segment.position > demux->common.segment.start &&
-        context->pos + (GST_SECOND / 2) < demux->common.segment.position) {
+        context->pos + gap_threshold < demux->common.segment.position) {
 
       GstEvent *event;
       guint64 start = context->pos;
-      guint64 stop = demux->common.segment.position - (GST_SECOND / 2);
+      guint64 stop = demux->common.segment.position - gap_threshold;
 
       GST_DEBUG_OBJECT (demux,
           "Synchronizing stream %d with other by advancing time from %"
@@ -2634,8 +3030,6 @@ gst_matroska_demux_push_codec_data_all (GstMatroskaDemux * demux)
 {
   gint stream_nr;
 
-  GST_OBJECT_LOCK (demux);
-
   g_assert (demux->common.num_streams == demux->common.src->len);
   for (stream_nr = 0; stream_nr < demux->common.src->len; stream_nr++) {
     GstMatroskaTrackContext *stream;
@@ -2660,7 +3054,6 @@ gst_matroska_demux_push_codec_data_all (GstMatroskaDemux * demux)
     }
   }
 
-  GST_OBJECT_UNLOCK (demux);
 }
 
 static GstFlowReturn
@@ -2761,6 +3154,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
     GST_WRITE_UINT8 (data + 11, wvh.index_no);
     GST_WRITE_UINT32_LE (data + 12, wvh.total_samples);
     GST_WRITE_UINT32_LE (data + 16, wvh.block_index);
+    gst_buffer_unmap (newbuf, &outmap);
 
     /* Append data from buf: */
     gst_buffer_copy_into (newbuf, *buf, GST_BUFFER_COPY_TIMESTAMPS |
@@ -2862,6 +3256,33 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
   return GST_FLOW_OK;
 }
 
+static GstFlowReturn
+gst_matroska_demux_add_prores_header (GstElement * element,
+    GstMatroskaTrackContext * stream, GstBuffer ** buf)
+{
+  GstBuffer *newbuf = gst_buffer_new_allocate (NULL, 8, NULL);
+  GstMapInfo map;
+  guint32 frame_size;
+
+  if (!gst_buffer_map (newbuf, &map, GST_MAP_WRITE)) {
+    GST_ERROR ("Failed to map newly allocated buffer");
+    return GST_FLOW_ERROR;
+  }
+
+  frame_size = gst_buffer_get_size (*buf);
+
+  GST_WRITE_UINT32_BE (map.data, frame_size);
+  map.data[4] = 'i';
+  map.data[5] = 'c';
+  map.data[6] = 'p';
+  map.data[7] = 'f';
+
+  gst_buffer_unmap (newbuf, &map);
+  *buf = gst_buffer_append (newbuf, *buf);
+
+  return GST_FLOW_OK;
+}
+
 /* @text must be null-terminated */
 static gboolean
 gst_matroska_demux_subtitle_chunk_has_tag (GstElement * element,
@@ -2913,25 +3334,16 @@ gst_matroska_demux_check_subtitle_buffer (GstElement * element,
   if (!gst_buffer_get_size (*buf) || !gst_buffer_map (*buf, &map, GST_MAP_READ))
     return GST_FLOW_OK;
 
-  /* Need \0-terminator at the end */
-  if (map.data[map.size - 1] != '\0') {
-    newbuf = gst_buffer_new_and_alloc (map.size + 1);
-
-    /* Copy old buffer and add a 0 at the end */
-    gst_buffer_fill (newbuf, 0, map.data, map.size);
-    gst_buffer_memset (newbuf, map.size, 0, 1);
+  /* The subtitle buffer we push out should not include a NUL terminator as
+   * part of the data. */
+  if (map.data[map.size - 1] == '\0') {
+    gst_buffer_set_size (*buf, map.size - 1);
     gst_buffer_unmap (*buf, &map);
-
-    gst_buffer_copy_into (newbuf, *buf,
-        GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS |
-        GST_BUFFER_COPY_META, 0, -1);
-    gst_buffer_unref (*buf);
-    *buf = newbuf;
     gst_buffer_map (*buf, &map, GST_MAP_READ);
   }
 
   if (!sub_stream->invalid_utf8) {
-    if (g_utf8_validate ((gchar *) map.data, map.size - 1, NULL)) {
+    if (g_utf8_validate ((gchar *) map.data, map.size, NULL)) {
       goto next;
     }
     GST_WARNING_OBJECT (element, "subtitle stream %" G_GUINT64_FORMAT
@@ -3090,6 +3502,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
   gboolean readblock = FALSE;
   guint32 id;
   guint64 block_duration = -1;
+  gint64 block_discardpadding = 0;
   GstBuffer *buf = NULL;
   GstMapInfo map;
   gint stream_num = -1, n, laces = 0;
@@ -3254,6 +3667,13 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         break;
       }
 
+      case GST_MATROSKA_ID_DISCARDPADDING:{
+        ret = gst_ebml_read_sint (ebml, &id, &block_discardpadding);
+        GST_DEBUG_OBJECT (demux, "DiscardPadding: %" GST_STIME_FORMAT,
+            GST_STIME_ARGS (block_discardpadding));
+        break;
+      }
+
       case GST_MATROSKA_ID_REFERENCEBLOCK:{
         ret = gst_ebml_read_sint (ebml, &id, &referenceblock);
         GST_DEBUG_OBJECT (demux, "ReferenceBlock: %" G_GINT64_FORMAT,
@@ -3362,7 +3782,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;
@@ -3399,15 +3819,27 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
     }
     /* else duration is diff between timecode of this and next block */
 
-    /* For SimpleBlock, look at the keyframe bit in flags. Otherwise,
-       a ReferenceBlock implies that this is not a keyframe. In either
-       case, it only makes sense for video streams. */
     if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
+      /* For SimpleBlock, look at the keyframe bit in flags. Otherwise,
+         a ReferenceBlock implies that this is not a keyframe. In either
+         case, it only makes sense for video streams. */
       if ((is_simpleblock && !(flags & 0x80)) || referenceblock) {
         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) {
+        GST_LOG_OBJECT (demux, "Skipping non-keyframe on stream %d",
+            stream->index);
+        ret = GST_FLOW_OK;
+        goto done;
       }
     }
 
@@ -3433,7 +3865,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         GST_OBJECT_LOCK (demux);
         earliest_time = videocontext->earliest_time;
         GST_OBJECT_UNLOCK (demux);
-        earliest_stream_time = gst_segment_to_position (&demux->common.segment,
+        earliest_stream_time =
+            gst_segment_position_from_running_time (&demux->common.segment,
             GST_FORMAT_TIME, earliest_time);
 
         if (GST_CLOCK_TIME_IS_VALID (lace_time) &&
@@ -3478,12 +3911,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         goto next_lace;
       }
 
-      buffer_timestamp = gst_matroska_track_get_buffer_timestamp (stream, sub);
-
-      if (!stream->dts_only)
+      if (!stream->dts_only) {
         GST_BUFFER_PTS (sub) = lace_time;
-      else
+      } else {
         GST_BUFFER_DTS (sub) = lace_time;
+        if (stream->intra_only)
+          GST_BUFFER_PTS (sub) = lace_time;
+      }
+
+      buffer_timestamp = gst_matroska_track_get_buffer_timestamp (stream, sub);
 
       if (GST_CLOCK_TIME_IS_VALID (lace_time)) {
         GstClockTime last_stop_end;
@@ -3625,9 +4061,49 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
          for 32 bit samples, etc), or bad things will happen downstream as
          elements typically assume minimal alignment.
          Therefore, create an aligned copy if necessary. */
-      g_assert (stream->alignment <= G_MEM_ALIGN);
       sub = gst_matroska_demux_align_buffer (demux, sub, stream->alignment);
 
+      if (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
+        guint64 start_clip = 0, end_clip = 0;
+
+        /* Codec delay is part of the timestamps */
+        if (GST_BUFFER_PTS_IS_VALID (sub) && stream->codec_delay) {
+          if (GST_BUFFER_PTS (sub) > stream->codec_delay) {
+            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);
+
+            if (GST_BUFFER_DURATION_IS_VALID (sub)) {
+              if (GST_BUFFER_DURATION (sub) > stream->codec_delay)
+                GST_BUFFER_DURATION (sub) -= stream->codec_delay;
+              else
+                GST_BUFFER_DURATION (sub) = 0;
+            }
+          }
+        }
+
+        if (block_discardpadding) {
+          end_clip =
+              gst_util_uint64_scale_round (block_discardpadding, 48000,
+              GST_SECOND);
+        }
+
+        if (start_clip || end_clip) {
+          gst_buffer_add_audio_clipping_meta (sub, GST_FORMAT_DEFAULT,
+              start_clip, end_clip);
+        }
+      }
+
       if (GST_BUFFER_PTS_IS_VALID (sub)) {
         stream->pos = GST_BUFFER_PTS (sub);
         if (GST_BUFFER_DURATION_IS_VALID (sub))
@@ -3650,7 +4126,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         }
       }
       /* combine flows */
-      ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
+      ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner,
+          stream->pad, ret);
 
     next_lace:
       size -= lace_size[n];
@@ -3676,7 +4153,8 @@ eos:
     stream->eos = TRUE;
     ret = GST_FLOW_OK;
     /* combine flows */
-    ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
+    ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
+        ret);
     goto done;
   }
 invalid_lacing:
@@ -3956,7 +4434,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 {
@@ -4182,8 +4661,22 @@ 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_CLUSTERTIMECODE)
+          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
+           * after invalid data is found
+           */
+          read = 1;
+        }
         goto skip;
+      } else {
+        if (demux->common.start_resync_offset != -1) {
+          GST_LOG_OBJECT (demux, "Resync done, new cluster found!");
+          demux->common.start_resync_offset = -1;
+          demux->common.state = demux->common.state_to_restore;
+        }
+      }
       /* fall-through */
     case GST_MATROSKA_READ_STATE_HEADER:
     case GST_MATROSKA_READ_STATE_DATA:
@@ -4223,6 +4716,26 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
                   == GST_MATROSKA_READ_STATE_HEADER)) {
             demux->common.state = GST_MATROSKA_READ_STATE_DATA;
             demux->first_cluster_offset = demux->common.offset;
+            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));
+              }
+            }
             GST_DEBUG_OBJECT (demux, "signaling no more pads");
             gst_element_no_more_pads (GST_ELEMENT (demux));
             /* send initial segment - we wait till we know the first
@@ -4232,6 +4745,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",
@@ -4254,6 +4768,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)
@@ -4346,16 +4866,29 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
 
             g_assert (event);
             /* unlikely to fail, since we managed to seek to this point */
-            if (!gst_matroska_demux_handle_seek_event (demux, NULL, event))
+            if (!gst_matroska_demux_handle_seek_event (demux, NULL, event)) {
+              gst_event_unref (event);
               goto seek_failed;
+            }
+            gst_event_unref (event);
             /* resume data handling, main thread clear to seek again */
             GST_OBJECT_LOCK (demux);
             demux->common.state = GST_MATROSKA_READ_STATE_DATA;
             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:
         case GST_MATROSKA_ID_SILENTTRACKS:
           GST_DEBUG_OBJECT (demux,
@@ -4521,7 +5054,7 @@ pause:
       /* perform EOS logic */
 
       /* If we were in the headers, make sure we send no-more-pads.
-         This will ensure decodebin2 does not get stuck thinking
+         This will ensure decodebin does not get stuck thinking
          the chain is not complete yet, and waiting indefinitely. */
       if (G_UNLIKELY (demux->common.state == GST_MATROSKA_READ_STATE_HEADER)) {
         if (demux->common.src->len == 0) {
@@ -4535,6 +5068,8 @@ pause:
       }
 
       if (demux->common.segment.flags & GST_SEEK_FLAG_SEGMENT) {
+        GstEvent *event;
+        GstMessage *msg;
         gint64 stop;
 
         /* for segment playback we need to post when (in stream time)
@@ -4543,24 +5078,33 @@ pause:
           stop = demux->last_stop_end;
 
         GST_LOG_OBJECT (demux, "Sending segment done, at end of segment");
-        gst_element_post_message (GST_ELEMENT (demux),
-            gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME,
-                stop));
-        gst_matroska_demux_send_event (demux,
-            gst_event_new_segment_done (GST_FORMAT_TIME, stop));
+        msg = gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME,
+            stop);
+        if (demux->segment_seqnum)
+          gst_message_set_seqnum (msg, demux->segment_seqnum);
+        gst_element_post_message (GST_ELEMENT (demux), msg);
+
+        event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
+        if (demux->segment_seqnum)
+          gst_event_set_seqnum (event, demux->segment_seqnum);
+        gst_matroska_demux_send_event (demux, event);
       } else {
         push_eos = TRUE;
       }
     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
       /* for fatal errors we post an error message */
-      GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL),
-          ("stream stopped, reason %s", reason));
+      GST_ELEMENT_FLOW_ERROR (demux, ret);
       push_eos = TRUE;
     }
     if (push_eos) {
+      GstEvent *event;
+
       /* send EOS, and prevent hanging if no streams yet */
       GST_LOG_OBJECT (demux, "Sending EOS, at end of stream");
-      if (!gst_matroska_demux_send_event (demux, gst_event_new_eos ()) &&
+      event = gst_event_new_eos ();
+      if (demux->segment_seqnum)
+        gst_event_set_seqnum (event, demux->segment_seqnum);
+      if (!gst_matroska_demux_send_event (demux, event) &&
           (ret == GST_FLOW_EOS)) {
         GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
             (NULL), ("got eos but no streams (yet)"));
@@ -4575,7 +5119,7 @@ pause:
  */
 static gboolean
 perform_seek_to_offset (GstMatroskaDemux * demux, gdouble rate, guint64 offset,
-    guint32 seqnum)
+    guint32 seqnum, GstSeekFlags flags)
 {
   GstEvent *event;
   gboolean res = 0;
@@ -4584,8 +5128,8 @@ perform_seek_to_offset (GstMatroskaDemux * demux, gdouble rate, guint64 offset,
 
   event =
       gst_event_new_seek (rate, GST_FORMAT_BYTES,
-      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
-      GST_SEEK_TYPE_NONE, -1);
+      flags | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
+      GST_SEEK_TYPE_SET, offset, GST_SEEK_TYPE_NONE, -1);
   gst_event_set_seqnum (event, seqnum);
 
   res = gst_pad_push_event (demux->common.sinkpad, event);
@@ -4624,9 +5168,29 @@ next:
   if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)) {
     if (demux->common.ebml_segment_length != G_MAXUINT64
         && demux->common.offset >=
-        demux->common.ebml_segment_start + demux->common.ebml_segment_length)
-      ret = GST_FLOW_EOS;
-    return ret;
+        demux->common.ebml_segment_start + demux->common.ebml_segment_length) {
+      return GST_FLOW_OK;
+    } else {
+      gint64 bytes_scanned;
+      if (demux->common.start_resync_offset == -1) {
+        demux->common.start_resync_offset = demux->common.offset;
+        demux->common.state_to_restore = demux->common.state;
+      }
+      bytes_scanned = demux->common.offset - demux->common.start_resync_offset;
+      if (bytes_scanned <= INVALID_DATA_THRESHOLD) {
+        GST_WARNING_OBJECT (demux,
+            "parse error, looking for next cluster, actual offset %"
+            G_GUINT64_FORMAT ", start resync offset %" G_GUINT64_FORMAT,
+            demux->common.offset, demux->common.start_resync_offset);
+        demux->common.state = GST_MATROSKA_READ_STATE_SCANNING;
+        ret = GST_FLOW_OK;
+      } else {
+        GST_WARNING_OBJECT (demux,
+            "unrecoverable parse error, next cluster not found and threshold "
+            "exceeded, bytes scanned %" G_GINT64_FORMAT, bytes_scanned);
+        return ret;
+      }
+    }
   }
 
   GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
@@ -4695,10 +5259,12 @@ 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 */
       demux->common.segment.rate = segment->rate;
+      demux->common.segment.flags = segment->flags;
       /* also check if need to keep some of the requested seek position */
       if (demux->seek_offset == segment->start) {
         GST_DEBUG_OBJECT (demux, "position matches requested seek");
@@ -4719,7 +5285,8 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
     }
     case GST_EVENT_EOS:
     {
-      if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
+      if (demux->common.state != GST_MATROSKA_READ_STATE_DATA
+          && demux->common.state != GST_MATROSKA_READ_STATE_SCANNING) {
         gst_event_unref (event);
         GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
             (NULL), ("got eos and didn't receive a complete header object"));
@@ -4739,11 +5306,13 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
       GST_OBJECT_LOCK (demux);
       gst_matroska_read_common_reset_streams (&demux->common,
           GST_CLOCK_TIME_NONE, TRUE);
+      gst_flow_combiner_reset (demux->flowcombiner);
       dur = demux->common.segment.duration;
       gst_segment_init (&demux->common.segment, GST_FORMAT_TIME);
       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 */
     }
@@ -4874,6 +5443,12 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
       if (caps == NULL) {
         GST_WARNING ("Unhandled RIFF fourcc %" GST_FOURCC_FORMAT,
             GST_FOURCC_ARGS (vids->compression));
+      } else {
+        static GstStaticCaps intra_caps = GST_STATIC_CAPS ("image/jpeg; "
+            "video/x-raw; image/png; video/x-dv; video/x-huffyuv; video/x-ffv; "
+            "video/x-compressed-yuv");
+        context->intra_only =
+            gst_caps_can_intersect (gst_static_caps_get (&intra_caps), caps);
       }
 
       if (buf)
@@ -4919,10 +5494,13 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
         return NULL;
     }
 
+    context->intra_only = TRUE;
+
     gst_video_info_set_format (&info, format, videocontext->pixel_width,
         videocontext->pixel_height);
     caps = gst_video_info_to_caps (&info);
     *codec_name = gst_pb_utils_get_codec_description (caps);
+    context->alignment = 32;
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP)) {
     caps = gst_caps_new_simple ("video/x-divx",
         "divxversion", G_TYPE_INT, 4, NULL);
@@ -4972,6 +5550,7 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG)) {
     caps = gst_caps_new_empty_simple ("image/jpeg");
     *codec_name = g_strdup ("Motion-JPEG");
+    context->intra_only = TRUE;
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC)) {
     caps = gst_caps_new_empty_simple ("video/x-h264");
     if (data) {
@@ -5067,6 +5646,62 @@ 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 codec data found, assuming output is byte-stream");
+      gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, "byte-stream",
+          NULL);
+    }
+    *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 = "";
+
+    /* Expect a fourcc in the codec private data */
+    if (!data || size < 4) {
+      GST_WARNING ("No or too small PRORESS fourcc (%d bytes)", size);
+      return NULL;
+    }
+
+    fourcc = GST_STR_FOURCC (data);
+    switch (fourcc) {
+      case GST_MAKE_FOURCC ('a', 'p', 'c', 's'):
+        variant_descr = " 4:2:2 LT";
+        variant = "lt";
+        break;
+      case GST_MAKE_FOURCC ('a', 'p', 'c', 'h'):
+        variant = "hq";
+        variant_descr = " 4:2:2 HQ";
+        break;
+      case GST_MAKE_FOURCC ('a', 'p', '4', 'h'):
+        variant = "4444";
+        variant_descr = " 4:4:4:4";
+        break;
+      case GST_MAKE_FOURCC ('a', 'p', 'c', 'o'):
+        variant = "proxy";
+        variant_descr = " 4:2:2 Proxy";
+        break;
+      case GST_MAKE_FOURCC ('a', 'p', 'c', 'n'):
+      default:
+        variant = "standard";
+        variant_descr = " 4:2:2 SD";
+        break;
+    }
+
+    GST_LOG ("Prores video, codec fourcc %" GST_FOURCC_FORMAT,
+        GST_FOURCC_ARGS (fourcc));
+
+    caps = gst_caps_new_simple ("video/x-prores",
+        "format", G_TYPE_STRING, variant, NULL);
+    *codec_name = g_strdup_printf ("Apple ProRes%s", variant_descr);
+    context->postprocess_frame = gst_matroska_demux_add_prores_header;
   } else {
     GST_WARNING ("Unknown codec '%s', cannot build Caps", codec_id);
     return NULL;
@@ -5136,9 +5771,45 @@ 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,
+              videocontext->pixel_width, videocontext->pixel_height,
+              videocontext->display_width * videocontext->pixel_height,
+              videocontext->display_height * videocontext->pixel_width)) {
+        videocontext->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
+      }
+      gst_caps_set_simple (caps,
+          "multiview-mode", G_TYPE_STRING,
+          gst_video_multiview_mode_to_caps_string
+          (videocontext->multiview_mode), "multiview-flags",
+          GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, videocontext->multiview_flags,
+          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);
@@ -5273,7 +5944,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
     /* FIXME: Channel mask and reordering */
     caps = gst_caps_new_simple ("audio/x-raw",
         "format", G_TYPE_STRING, gst_audio_format_to_string (format),
-        "layout", G_TYPE_STRING, "interleaved", NULL);
+        "layout", G_TYPE_STRING, "interleaved",
+        "channel-mask", GST_TYPE_BITMASK,
+        gst_audio_channel_get_fallback_mask (audiocontext->channels), NULL);
 
     *codec_name = g_strdup_printf ("Raw %d-bit PCM audio",
         audiocontext->bitdepth);
@@ -5288,7 +5961,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
     /* FIXME: Channel mask and reordering */
     caps = gst_caps_new_simple ("audio/x-raw",
         "format", G_TYPE_STRING, format,
-        "layout", G_TYPE_STRING, "interleaved", NULL);
+        "layout", G_TYPE_STRING, "interleaved",
+        "channel-mask", GST_TYPE_BITMASK,
+        gst_audio_channel_get_fallback_mask (audiocontext->channels), NULL);
     *codec_name = g_strdup_printf ("Raw %d-bit floating-point audio",
         audiocontext->bitdepth);
     context->alignment = audiocontext->bitdepth / 8;
@@ -5332,20 +6007,57 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
     /* FIXME: mark stream as broken and skip if there are no stream headers */
     context->send_stream_headers = TRUE;
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
-    caps = gst_caps_new_empty_simple ("audio/x-opus");
-    *codec_name = g_strdup ("Opus");
-    context->stream_headers =
-        gst_matroska_parse_opus_stream_headers (context->codec_priv,
-        context->codec_priv_size);
-    if (context->stream_headers) {
-      /* There was a valid header. Multistream headers are more than
-       * 19 bytes, as they include an extra channel mapping table. */
-      gboolean multistream = (context->codec_priv_size > 19);
-      gst_caps_set_simple (caps, "multistream", G_TYPE_BOOLEAN, multistream,
-          NULL);
+    GstBuffer *tmp;
+
+    if (context->codec_priv_size >= 19) {
+      if (audiocontext->samplerate)
+        GST_WRITE_UINT32_LE ((guint8 *) context->codec_priv + 12,
+            audiocontext->samplerate);
+      if (context->codec_delay) {
+        guint64 delay =
+            gst_util_uint64_scale_round (context->codec_delay, 48000,
+            GST_SECOND);
+        GST_WRITE_UINT16_LE ((guint8 *) context->codec_priv + 10, delay);
+      }
+
+      tmp =
+          gst_buffer_new_wrapped (g_memdup (context->codec_priv,
+              context->codec_priv_size), context->codec_priv_size);
+      caps = gst_codec_utils_opus_create_caps_from_header (tmp, NULL);
+      gst_buffer_unref (tmp);
+      *codec_name = g_strdup ("Opus");
+    } else if (context->codec_priv_size == 0) {
+      GST_WARNING ("No Opus codec data found, trying to create one");
+      if (audiocontext->channels <= 2) {
+        guint8 streams, coupled, channels;
+        guint32 samplerate;
+
+        samplerate =
+            audiocontext->samplerate == 0 ? 48000 : audiocontext->samplerate;
+        channels = audiocontext->channels == 0 ? 2 : audiocontext->channels;
+        if (channels == 1) {
+          streams = 1;
+          coupled = 0;
+        } else {
+          streams = 1;
+          coupled = 1;
+        }
+
+        caps =
+            gst_codec_utils_opus_create_caps (samplerate, channels, 0, streams,
+            coupled, NULL);
+        if (caps) {
+          *codec_name = g_strdup ("Opus");
+        } else {
+          GST_WARNING ("Failed to create Opus caps from audio context");
+        }
+      } else {
+        GST_WARNING ("No Opus codec data, and not enough info to create one");
+      }
+    } else {
+      GST_WARNING ("Invalid Opus codec data size (got %" G_GSIZE_FORMAT
+          ", expected 19)", context->codec_priv_size);
     }
-    /* FIXME: mark stream as broken and skip if there are no stream headers */
-    context->send_stream_headers = TRUE;
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_ACM)) {
     gst_riff_strf_auds auds;
 
@@ -5507,7 +6219,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);
@@ -5517,7 +6229,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);
@@ -5701,7 +6413,7 @@ gst_matroska_demux_set_property (GObject * object,
   demux = GST_MATROSKA_DEMUX (object);
 
   switch (prop_id) {
-    case ARG_MAX_GAP_TIME:
+    case PROP_MAX_GAP_TIME:
       GST_OBJECT_LOCK (demux);
       demux->max_gap_time = g_value_get_uint64 (value);
       GST_OBJECT_UNLOCK (demux);
@@ -5722,7 +6434,7 @@ gst_matroska_demux_get_property (GObject * object,
   demux = GST_MATROSKA_DEMUX (object);
 
   switch (prop_id) {
-    case ARG_MAX_GAP_TIME:
+    case PROP_MAX_GAP_TIME:
       GST_OBJECT_LOCK (demux);
       g_value_set_uint64 (value, demux->max_gap_time);
       GST_OBJECT_UNLOCK (demux);