qtdemux: push mode: only parse moov 1 once
[platform/upstream/gst-plugins-good.git] / gst / isomp4 / qtdemux.c
index 2b9053f..60349a6 100644 (file)
@@ -18,8 +18,8 @@
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
@@ -33,7 +33,7 @@
  * <refsect2>
  * <title>Example launch line</title>
  * |[
- * gst-launch filesrc location=test.mov ! qtdemux name=demux  demux.audio_0 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_0 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
+ * gst-launch-1.0 filesrc location=test.mov ! qtdemux name=demux  demux.audio_0 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
  * ]| Play (parse and decode) a .mov file and try to output it to
  * an automatically detected soundcard and videosink. If the MOV file contains
  * compressed audio or video data, this will only work if you have the
@@ -118,9 +118,6 @@ struct _QtDemuxSample
 /* timestamp + duration - dts is the duration */
 #define QTSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
     (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
-/* timestamp + offset + duration - pts is the duration */
-#define QTSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
-    (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
 
 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
 
@@ -251,6 +248,11 @@ struct _QtDemuxStream
   guint bytes_per_frame;
   guint compression;
 
+  /* allocation */
+  gboolean use_allocator;
+  GstAllocator *allocator;
+  GstAllocationParams params;
+
   /* when a discontinuity is pending */
   gboolean discont;
 
@@ -429,6 +431,10 @@ static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
     QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
     gchar ** codec_name);
+static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
+    QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
+    gchar ** codec_name);
+
 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
     QtDemuxStream * stream, guint32 n);
 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
@@ -462,7 +468,7 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
       gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
-  gst_element_class_set_details_simple (gstelement_class, "QuickTime demuxer",
+  gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
       "Codec/Demuxer",
       "Demultiplex a QuickTime file into audio and video streams",
       "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
@@ -527,10 +533,9 @@ gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
 }
 
 static void
-_gst_buffer_copy_into_mem (GstBuffer * dest, const guint8 * src,
-    gsize offset, gsize size)
+_gst_buffer_copy_into_mem (GstBuffer * dest, gsize offset, const guint8 * src,
+    gsize size)
 {
-  guint8 *bdata;
   gsize bsize;
 
   g_return_if_fail (gst_buffer_is_writable (dest));
@@ -538,9 +543,7 @@ _gst_buffer_copy_into_mem (GstBuffer * dest, const guint8 * src,
   bsize = gst_buffer_get_size (dest);
   g_return_if_fail (bsize >= offset + size);
 
-  bdata = gst_buffer_map (dest, &bsize, NULL, GST_MAP_WRITE);
-  memcpy (bdata + offset, src, size);
-  gst_buffer_unmap (dest, bdata, bsize);
+  gst_buffer_fill (dest, offset, src, size);
 }
 
 static GstBuffer *
@@ -549,9 +552,9 @@ _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
   GstBuffer *buf;
 
   buf = gst_buffer_new ();
-  gst_buffer_take_memory (buf, -1,
+  gst_buffer_append_memory (buf,
       gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
-          mem, free_func, size, 0, size));
+          mem, size, 0, size, mem, free_func));
 
   return buf;
 }
@@ -561,7 +564,7 @@ gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
     GstBuffer ** buf)
 {
   GstFlowReturn flow;
-  guint8 *bdata;
+  GstMapInfo map;
   gsize bsize;
 
   if (G_UNLIKELY (size == 0)) {
@@ -572,11 +575,11 @@ gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
     if (ret != GST_FLOW_OK)
       return ret;
 
-    bdata = gst_buffer_map (tmp, &bsize, NULL, GST_MAP_READ);
-    size = QT_UINT32 (bdata);
+    gst_buffer_map (tmp, &map, GST_MAP_READ);
+    size = QT_UINT32 (map.data);
     GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
 
-    gst_buffer_unmap (tmp, bdata, bsize);
+    gst_buffer_unmap (tmp, &map);
     gst_buffer_unref (tmp);
   }
 
@@ -721,12 +724,16 @@ gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
 
       gst_query_parse_duration (query, &fmt, NULL);
       if (fmt == GST_FORMAT_TIME) {
-        gint64 duration = -1;
-
-        gst_qtdemux_get_duration (qtdemux, &duration);
-        if (duration > 0) {
-          gst_query_set_duration (query, GST_FORMAT_TIME, duration);
-          res = TRUE;
+        /* First try to query upstream */
+        res = gst_pad_query_default (pad, parent, query);
+        if (!res) {
+          gint64 duration = -1;
+
+          gst_qtdemux_get_duration (qtdemux, &duration);
+          if (duration > 0) {
+            gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+            res = TRUE;
+          }
         }
       }
       break;
@@ -803,7 +810,7 @@ gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
       GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
           qtdemux->tag_list);
       gst_pad_push_event (stream->pad,
-          gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
+          gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
       stream->send_global_tags = FALSE;
     }
   }
@@ -1419,7 +1426,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
     qtdemux->streams[i]->last_ret = GST_FLOW_OK;
 
   gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
-      qtdemux->sinkpad);
+      qtdemux->sinkpad, NULL);
 
   GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
 
@@ -1484,8 +1491,11 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
     }
       if (qtdemux->pullbased) {
         res = gst_qtdemux_do_seek (qtdemux, pad, event);
-      } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
-          !qtdemux->fragmented) {
+      } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
+        GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
+        res = TRUE;
+      } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
+          && !qtdemux->fragmented) {
         res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
       } else {
         GST_DEBUG_OBJECT (qtdemux,
@@ -1654,7 +1664,8 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
       }
 
       /* accept upstream's notion of segment and distribute along */
-      segment.time = segment.start;
+      segment.format = GST_FORMAT_TIME;
+      segment.position = segment.time = segment.start;
       segment.duration = demux->segment.duration;
       segment.base = gst_segment_to_running_time (&demux->segment,
           GST_FORMAT_TIME, demux->segment.position);
@@ -1788,6 +1799,8 @@ gst_qtdemux_stbl_free (QtDemuxStream * stream)
 static void
 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
 {
+  if (stream->allocator)
+    gst_object_unref (stream->allocator);
   while (stream->buffers) {
     gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
     stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
@@ -1799,7 +1812,7 @@ gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
     gst_caps_unref (stream->caps);
   g_free (stream->segments);
   if (stream->pending_tags)
-    gst_tag_list_free (stream->pending_tags);
+    gst_tag_list_unref (stream->pending_tags);
   g_free (stream->redirect_uri);
   /* free stbl sub-atoms */
   gst_qtdemux_stbl_free (stream);
@@ -1842,7 +1855,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
         gst_buffer_unref (qtdemux->comp_brands);
       qtdemux->comp_brands = NULL;
       if (qtdemux->tag_list)
-        gst_tag_list_free (qtdemux->tag_list);
+        gst_tag_list_unref (qtdemux->tag_list);
       qtdemux->tag_list = NULL;
 #if 0
       if (qtdemux->element_index)
@@ -1874,20 +1887,6 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
 }
 
 static void
-qtdemux_post_global_tags (GstQTDemux * qtdemux)
-{
-  if (qtdemux->tag_list) {
-    /* all header tags ready and parsed, push them */
-    GST_INFO_OBJECT (qtdemux, "posting global tags: %" GST_PTR_FORMAT,
-        qtdemux->tag_list);
-    /* post now, send event on pads later */
-    gst_element_post_message (GST_ELEMENT (qtdemux),
-        gst_message_new_tag (GST_OBJECT (qtdemux),
-            gst_tag_list_copy (qtdemux->tag_list)));
-  }
-}
-
-static void
 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
 {
   /* counts as header data */
@@ -1901,7 +1900,7 @@ qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
     GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
         GST_FOURCC_ARGS (qtdemux->major_brand));
     buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
-    _gst_buffer_copy_into_mem (buf, buffer + 16, 0, length - 16);
+    _gst_buffer_copy_into_mem (buf, 0, buffer + 16, length - 16);
   }
 }
 
@@ -1917,7 +1916,7 @@ qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist)
     if (qtdemux->tag_list) {
       /* prioritize native tags using _KEEP mode */
       gst_tag_list_insert (qtdemux->tag_list, taglist, GST_TAG_MERGE_KEEP);
-      gst_tag_list_free (taglist);
+      gst_tag_list_unref (taglist);
     } else
       qtdemux->tag_list = taglist;
   }
@@ -2378,11 +2377,43 @@ unknown_stream:
 }
 
 static gboolean
+qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
+    guint64 * decode_time)
+{
+  guint32 version = 0;
+
+  if (!gst_byte_reader_get_uint32_be (br, &version))
+    return FALSE;
+
+  version >>= 24;
+  if (version == 1) {
+    if (!gst_byte_reader_get_uint64_be (br, decode_time))
+      goto failed;
+  } else {
+    guint32 dec_time = 0;
+    if (!gst_byte_reader_get_uint32_be (br, &dec_time))
+      goto failed;
+    *decode_time = dec_time;
+  }
+
+  GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
+      *decode_time);
+
+  return TRUE;
+
+failed:
+  {
+    GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
+    return FALSE;
+  }
+}
+
+static gboolean
 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     guint64 moof_offset, QtDemuxStream * stream)
 {
-  GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
-  GstByteReader trun_data, tfhd_data;
+  GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node;
+  GstByteReader trun_data, tfhd_data, tfdt_data;
   guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
   gint64 base_offset, running_offset;
 
@@ -2405,6 +2436,26 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
             &ds_size, &ds_flags, &base_offset))
       goto missing_tfhd;
+    tfdt_node =
+        qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
+        &tfdt_data);
+    if (tfdt_node) {
+      guint64 decode_time = 0;
+      qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
+      /* If there is a new segment pending, update the time/position */
+      if (qtdemux->pending_newsegment) {
+        GstSegment segment;
+
+        gst_segment_init (&segment, GST_FORMAT_TIME);
+        segment.time = gst_util_uint64_scale (decode_time,
+            GST_SECOND, stream->timescale);
+        gst_event_replace (&qtdemux->pending_newsegment,
+            gst_event_new_segment (&segment));
+        /* ref added when replaced, release the original _new one */
+        gst_event_unref (qtdemux->pending_newsegment);
+      }
+    }
+
     if (G_UNLIKELY (!stream)) {
       /* we lost track of offset, we'll need to regain it,
        * but can delay complaining until later or avoid doing so altogether */
@@ -2646,16 +2697,15 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
   GstBuffer *buf = NULL;
   GstFlowReturn ret = GST_FLOW_OK;
   guint64 cur_offset = qtdemux->offset;
-  guint8 *data;
-  gsize size;
+  GstMapInfo map;
 
   ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto beach;
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-  if (G_LIKELY (size >= 8))
-    extract_initial_length_and_fourcc (data, size, &length, &fourcc);
-  gst_buffer_unmap (buf, data, size);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  if (G_LIKELY (map.size >= 8))
+    extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
+  gst_buffer_unmap (buf, &map);
   gst_buffer_unref (buf);
 
   /* maybe we already got most we needed, so only consider this eof */
@@ -2689,7 +2739,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
     }
     case FOURCC_moov:
     {
-      GstBuffer *moov;
+      GstBuffer *moov = NULL;
 
       if (qtdemux->got_moov) {
         GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
@@ -2700,49 +2750,50 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
       ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
       if (ret != GST_FLOW_OK)
         goto beach;
-      data = gst_buffer_map (moov, &size, NULL, GST_MAP_READ);
-      if (length != size) {
+      gst_buffer_map (moov, &map, GST_MAP_READ);
+      if (length != map.size) {
         /* Some files have a 'moov' atom at the end of the file which contains
          * a terminal 'free' atom where the body of the atom is missing.
          * Check for, and permit, this special case.
          */
-        if (size >= 8) {
-          guint8 *final_data = data + (size - 8);
+        if (map.size >= 8) {
+          guint8 *final_data = map.data + (map.size - 8);
           guint32 final_length = QT_UINT32 (final_data);
           guint32 final_fourcc = QT_FOURCC (final_data + 4);
-          gst_buffer_unmap (moov, data, size);
-          if (final_fourcc == FOURCC_free && size + final_length - 8 == length) {
+          gst_buffer_unmap (moov, &map);
+          if (final_fourcc == FOURCC_free
+              && map.size + final_length - 8 == length) {
             /* Ok, we've found that special case. Allocate a new buffer with
              * that free atom actually present. */
             GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
-            gst_buffer_copy_into (newmoov, moov, 0, 0, size);
-            data = gst_buffer_map (newmoov, &size, NULL, GST_MAP_WRITE);
-            memset (data + length - final_length + 8, 0, final_length - 8);
+            gst_buffer_copy_into (newmoov, moov, 0, 0, map.size);
+            gst_buffer_map (newmoov, &map, GST_MAP_WRITE);
+            memset (map.data + length - final_length + 8, 0, final_length - 8);
             gst_buffer_unref (moov);
             moov = newmoov;
           }
         }
       }
 
-      if (length != size) {
+      if (length != map.size) {
         GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
             (_("This file is incomplete and cannot be played.")),
             ("We got less than expected (received %" G_GSIZE_FORMAT
-                ", wanted %u, offset %" G_GUINT64_FORMAT ")", size,
+                ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
                 (guint) length, cur_offset));
-        gst_buffer_unmap (moov, data, size);
+        gst_buffer_unmap (moov, &map);
         gst_buffer_unref (moov);
         ret = GST_FLOW_ERROR;
         goto beach;
       }
       qtdemux->offset += length;
 
-      qtdemux_parse_moov (qtdemux, data, length);
+      qtdemux_parse_moov (qtdemux, map.data, length);
       qtdemux_node_dump (qtdemux, qtdemux->moov_node);
 
       qtdemux_parse_tree (qtdemux);
       g_node_destroy (qtdemux->moov_node);
-      gst_buffer_unmap (moov, data, size);
+      gst_buffer_unmap (moov, &map);
       gst_buffer_unref (moov);
       qtdemux->moov_node = NULL;
       qtdemux->got_moov = TRUE;
@@ -2751,37 +2802,37 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
     }
     case FOURCC_ftyp:
     {
-      GstBuffer *ftyp;
+      GstBuffer *ftyp = NULL;
 
       /* extract major brand; might come in handy for ISO vs QT issues */
       ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
       if (ret != GST_FLOW_OK)
         goto beach;
       qtdemux->offset += length;
-      data = gst_buffer_map (ftyp, &size, NULL, GST_MAP_READ);
-      qtdemux_parse_ftyp (qtdemux, data, size);
-      gst_buffer_unmap (ftyp, data, size);
+      gst_buffer_map (ftyp, &map, GST_MAP_READ);
+      qtdemux_parse_ftyp (qtdemux, map.data, map.size);
+      gst_buffer_unmap (ftyp, &map);
       gst_buffer_unref (ftyp);
       break;
     }
     case FOURCC_uuid:
     {
-      GstBuffer *uuid;
+      GstBuffer *uuid = NULL;
 
       /* uuid are extension atoms */
       ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
       if (ret != GST_FLOW_OK)
         goto beach;
       qtdemux->offset += length;
-      data = gst_buffer_map (uuid, &size, NULL, GST_MAP_READ);
-      qtdemux_parse_uuid (qtdemux, data, size);
-      gst_buffer_unmap (uuid, data, size);
+      gst_buffer_map (uuid, &map, GST_MAP_READ);
+      qtdemux_parse_uuid (qtdemux, map.data, map.size);
+      gst_buffer_unmap (uuid, &map);
       gst_buffer_unref (uuid);
       break;
     }
     default:
     {
-      GstBuffer *unknown;
+      GstBuffer *unknown = NULL;
 
       GST_LOG_OBJECT (qtdemux,
           "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
@@ -2790,9 +2841,9 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
       ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
       if (ret != GST_FLOW_OK)
         goto beach;
-      data = gst_buffer_map (unknown, &size, NULL, GST_MAP_READ);
-      GST_MEMDUMP ("Unknown tag", data, size);
-      gst_buffer_unmap (unknown, data, size);
+      gst_buffer_map (unknown, &map, GST_MAP_READ);
+      GST_MEMDUMP ("Unknown tag", map.data, map.size);
+      gst_buffer_unmap (unknown, &map);
       gst_buffer_unref (unknown);
       qtdemux->offset += length;
       break;
@@ -2804,9 +2855,6 @@ beach:
     /* digested all data, show what we have */
     ret = qtdemux_expose_streams (qtdemux);
 
-    /* Only post, event on pads is done after newsegment */
-    qtdemux_post_global_tags (qtdemux);
-
     qtdemux->state = QTDEMUX_STATE_MOVIE;
     GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
         qtdemux->state);
@@ -3076,7 +3124,6 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
   rate = segment->rate * qtdemux->segment.rate;
 
   /* update the segment values used for clipping */
-  gst_segment_init (&stream->segment, GST_FORMAT_TIME);
   /* accumulate previous segments */
   if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
     stream->segment.base += (stream->segment.stop - stream->segment.start) /
@@ -3173,8 +3220,8 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
  */
 static gboolean
 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
-    QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
-    guint64 * duration, gboolean * keyframe)
+    QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * dts,
+    guint64 * pts, guint64 * duration, gboolean * keyframe)
 {
   QtDemuxSample *sample;
   guint64 time_position;
@@ -3216,10 +3263,11 @@ gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
   /* now get the info for the sample we're at */
   sample = &stream->samples[stream->sample_index];
 
-  *timestamp = QTSAMPLE_PTS (stream, sample);
+  *dts = QTSAMPLE_DTS (stream, sample);
+  *pts = QTSAMPLE_PTS (stream, sample);
   *offset = sample->offset;
   *size = sample->size;
-  *duration = QTSAMPLE_DUR_PTS (stream, sample, *timestamp);
+  *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
   *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
 
   return TRUE;
@@ -3413,14 +3461,14 @@ gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
     GstBuffer * buf)
 {
   guint64 start, stop, cstart, cstop, diff;
-  GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
-  guint size;
+  GstClockTime pts, dts, duration;
+  gsize size, osize;
   gint num_rate, denom_rate;
   gint frame_size;
   gboolean clip_data;
   guint offset;
 
-  size = gst_buffer_get_size (buf);
+  osize = size = gst_buffer_get_size (buf);
   offset = 0;
 
   /* depending on the type, setup the clip parameters */
@@ -3437,19 +3485,20 @@ gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
   } else
     goto wrong_type;
 
-  /* we can only clip if we have a valid timestamp */
-  timestamp = GST_BUFFER_TIMESTAMP (buf);
-  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
-    goto no_timestamp;
+  /* we can only clip if we have a valid pts */
+  pts = GST_BUFFER_PTS (buf);
+  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
+    goto no_pts;
 
-  if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) {
-    duration = GST_BUFFER_DURATION (buf);
-  } else {
+  dts = GST_BUFFER_DTS (buf);
+  duration = GST_BUFFER_DURATION (buf);
+
+  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
     duration =
         gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
   }
 
-  start = timestamp;
+  start = pts;
   stop = start + duration;
 
   if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
@@ -3459,7 +3508,8 @@ gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
   /* see if some clipping happened */
   diff = cstart - start;
   if (diff > 0) {
-    timestamp = cstart;
+    pts += diff;
+    dts += diff;
     duration -= diff;
 
     if (clip_data) {
@@ -3490,8 +3540,11 @@ gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
     }
   }
 
-  gst_buffer_resize (buf, offset, size);
-  GST_BUFFER_TIMESTAMP (buf) = timestamp;
+  if (offset != 0 || size != osize)
+    gst_buffer_resize (buf, offset, size);
+
+  GST_BUFFER_DTS (buf) = dts;
+  GST_BUFFER_PTS (buf) = pts;
   GST_BUFFER_DURATION (buf) = duration;
 
   return buf;
@@ -3502,9 +3555,9 @@ wrong_type:
     GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
     return buf;
   }
-no_timestamp:
+no_pts:
   {
-    GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer");
+    GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
     return buf;
   }
 clipped:
@@ -3521,9 +3574,8 @@ static GstBuffer *
 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
     GstBuffer * buf)
 {
-  guint8 *data;
+  GstMapInfo map;
   guint nsize = 0;
-  gsize size;
   gchar *str;
 
   /* not many cases for now */
@@ -3540,20 +3592,20 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
     return buf;
   }
 
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
 
-  if (G_LIKELY (size >= 2)) {
-    nsize = GST_READ_UINT16_BE (data);
-    nsize = MIN (nsize, size - 2);
+  if (G_LIKELY (map.size >= 2)) {
+    nsize = GST_READ_UINT16_BE (map.data);
+    nsize = MIN (nsize, map.size - 2);
   }
 
   GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
-      nsize, size);
+      nsize, map.size);
 
   /* takes care of UTF-8 validation or UTF-16 recognition,
    * no other encoding expected */
-  str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
-  gst_buffer_unmap (buf, data, size);
+  str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
+  gst_buffer_unmap (buf, &map);
   if (str) {
     gst_buffer_unref (buf);
     buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
@@ -3574,19 +3626,18 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
 static gboolean
 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
     QtDemuxStream * stream, GstBuffer * buf,
-    guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
-    guint64 byte_position)
+    guint64 dts, guint64 pts, guint64 duration, gboolean keyframe,
+    guint64 position, guint64 byte_position)
 {
   GstFlowReturn ret = GST_FLOW_OK;
 
   if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
     gchar *url;
-    guint8 *bdata;
-    gsize bsize;
+    GstMapInfo map;
 
-    bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-    url = g_strndup ((gchar *) bdata, bsize);
-    gst_buffer_unmap (buf, bdata, bsize);
+    gst_buffer_map (buf, &map, GST_MAP_READ);
+    url = g_strndup ((gchar *) map.data, map.size);
+    gst_buffer_unmap (buf, &map);
     if (url != NULL && strlen (url) != 0) {
       /* we have RTSP redirect now */
       gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
@@ -3634,7 +3685,8 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
   if (G_UNLIKELY (stream->need_process))
     buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
 
-  GST_BUFFER_TIMESTAMP (buf) = timestamp;
+  GST_BUFFER_DTS (buf) = dts;
+  GST_BUFFER_PTS (buf) = pts;
   GST_BUFFER_DURATION (buf) = duration;
   GST_BUFFER_OFFSET (buf) = -1;
   GST_BUFFER_OFFSET_END (buf) = -1;
@@ -3678,9 +3730,10 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
 
   GST_LOG_OBJECT (qtdemux,
-      "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
-      GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-      GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
+      "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
+      ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
+      GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
+      GST_PAD_NAME (stream->pad));
 
   ret = gst_pad_push (stream->pad, buf);
 
@@ -3696,7 +3749,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
   QtDemuxStream *stream;
   guint64 min_time;
   guint64 offset = 0;
-  guint64 timestamp = GST_CLOCK_TIME_NONE;
+  guint64 dts = GST_CLOCK_TIME_NONE;
+  guint64 pts = GST_CLOCK_TIME_NONE;
   guint64 duration = 0;
   gboolean keyframe = FALSE;
   guint size = 0;
@@ -3738,13 +3792,14 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
 
   /* fetch info for the current sample of this stream */
   if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
-              &size, &timestamp, &duration, &keyframe)))
+              &size, &dts, &pts, &duration, &keyframe)))
     goto eos_stream;
 
   GST_LOG_OBJECT (qtdemux,
       "pushing from stream %d, offset %" G_GUINT64_FORMAT
-      ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
-      index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
+      ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
+      ", duration %" GST_TIME_FORMAT, index, offset, size,
+      GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
 
   /* hmm, empty sample, skip and move to next sample */
   if (G_UNLIKELY (size <= 0))
@@ -3757,12 +3812,17 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
   GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
       offset);
 
+  if (stream->use_allocator) {
+    /* if we have a per-stream allocator, use it */
+    buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
+  }
+
   ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto beach;
 
   ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
-      timestamp, duration, keyframe, min_time, offset);
+      dts, pts, duration, keyframe, min_time, offset);
 
   /* combine flows */
   ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
@@ -3865,12 +3925,17 @@ pause:
           gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
               gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
                   GST_FORMAT_TIME, stop));
+          gst_qtdemux_push_event (qtdemux,
+              gst_event_new_segment_done (GST_FORMAT_TIME, stop));
         } else {
           /*  For Reverse Playback */
           GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
           gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
               gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
                   GST_FORMAT_TIME, qtdemux->segment.start));
+          gst_qtdemux_push_event (qtdemux,
+              gst_event_new_segment_done (GST_FORMAT_TIME,
+                  qtdemux->segment.start));
         }
       } else {
         GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
@@ -4080,10 +4145,6 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
             demux->state = QTDEMUX_STATE_MOVIE;
             demux->neededbytes = next_entry_size (demux);
             demux->mdatleft = size;
-
-            /* Only post, event on pads is done after newsegment */
-            qtdemux_post_global_tags (demux);
-
           } else {
             /* no headers yet, try to get them */
             guint bs;
@@ -4165,22 +4226,30 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
         extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
             &fourcc);
         if (fourcc == FOURCC_moov) {
-          GST_DEBUG_OBJECT (demux, "Parsing [moov]");
+          /* in usual fragmented setup we could try to scan for more
+           * and end up at the the moov (after mdat) again */
+          if (demux->got_moov && demux->n_streams > 0) {
+            GST_DEBUG_OBJECT (demux,
+                "Skipping moov atom as we have one already");
+          } else {
+            GST_DEBUG_OBJECT (demux, "Parsing [moov]");
 
-          demux->got_moov = TRUE;
+            demux->got_moov = TRUE;
 
-          /* prepare newsegment to send when streaming actually starts */
-          if (!demux->pending_newsegment)
-            demux->pending_newsegment = gst_event_new_segment (&demux->segment);
+            /* prepare newsegment to send when streaming actually starts */
+            if (!demux->pending_newsegment)
+              demux->pending_newsegment =
+                  gst_event_new_segment (&demux->segment);
 
-          qtdemux_parse_moov (demux, data, demux->neededbytes);
-          qtdemux_node_dump (demux, demux->moov_node);
-          qtdemux_parse_tree (demux);
-          qtdemux_expose_streams (demux);
+            qtdemux_parse_moov (demux, data, demux->neededbytes);
+            qtdemux_node_dump (demux, demux->moov_node);
+            qtdemux_parse_tree (demux);
+            qtdemux_expose_streams (demux);
 
-          g_node_destroy (demux->moov_node);
-          demux->moov_node = NULL;
-          GST_DEBUG_OBJECT (demux, "Finished parsing the header");
+            g_node_destroy (demux->moov_node);
+            demux->moov_node = NULL;
+            GST_DEBUG_OBJECT (demux, "Finished parsing the header");
+          }
         } else if (fourcc == FOURCC_moof) {
           if (demux->got_moov && demux->fragmented) {
             GST_DEBUG_OBJECT (demux, "Parsing [moof]");
@@ -4222,10 +4291,6 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
           demux->neededbytes = next_entry_size (demux);
           demux->state = QTDEMUX_STATE_MOVIE;
           demux->mdatleft = gst_adapter_available (demux->adapter);
-
-          /* Only post, event on pads is done after newsegment */
-          qtdemux_post_global_tags (demux);
-
         } else {
           GST_DEBUG_OBJECT (demux, "Carrying on normally");
           gst_adapter_flush (demux->adapter, demux->neededbytes);
@@ -4260,7 +4325,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
         GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
             GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
         if (demux->mdatbuffer)
-          demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
+          demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
         else
           demux->mdatbuffer = buf;
         demux->offset += demux->neededbytes;
@@ -4275,7 +4340,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
         QtDemuxStream *stream = NULL;
         QtDemuxSample *sample;
         int i = -1;
-        guint64 timestamp, duration, position;
+        guint64 dts, pts, duration;
         gboolean keyframe;
 
         GST_DEBUG_OBJECT (demux,
@@ -4354,13 +4419,13 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
 
         sample = &stream->samples[stream->sample_index];
 
-        position = QTSAMPLE_DTS (stream, sample);
-        timestamp = QTSAMPLE_PTS (stream, sample);
-        duration = QTSAMPLE_DUR_DTS (stream, sample, position);
+        dts = QTSAMPLE_DTS (stream, sample);
+        pts = QTSAMPLE_PTS (stream, sample);
+        duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
         keyframe = QTSAMPLE_KEYFRAME (stream, sample);
 
         ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
-            timestamp, duration, keyframe, position, demux->offset);
+            dts, pts, duration, keyframe, dts, demux->offset);
 
         /* combine flows */
         ret = gst_qtdemux_combine_flows (demux, stream, ret);
@@ -4443,7 +4508,8 @@ qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
     goto activate_push;
   }
 
-  pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
+  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
+      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
   gst_query_unref (query);
 
   if (!pull_mode)
@@ -4475,7 +4541,7 @@ qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
       if (active) {
         demux->pullbased = TRUE;
         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
-            sinkpad);
+            sinkpad, NULL);
       } else {
         res = gst_pad_stop_task (sinkpad);
       }
@@ -4672,19 +4738,19 @@ qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
     switch (type) {
       case FOURCC_tCtH:
         buffer = gst_buffer_new_and_alloc (size);
-        _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+        _gst_buffer_copy_into_mem (buffer, 0, buf, size);
         stream->buffers = g_slist_append (stream->buffers, buffer);
         GST_LOG_OBJECT (qtdemux, "parsing theora header");
         break;
       case FOURCC_tCt_:
         buffer = gst_buffer_new_and_alloc (size);
-        _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+        _gst_buffer_copy_into_mem (buffer, 0, buf, size);
         stream->buffers = g_slist_append (stream->buffers, buffer);
         GST_LOG_OBJECT (qtdemux, "parsing theora comment");
         break;
       case FOURCC_tCtC:
         buffer = gst_buffer_new_and_alloc (size);
-        _gst_buffer_copy_into_mem (buffer, buf, 0, size);
+        _gst_buffer_copy_into_mem (buffer, 0, buf, size);
         stream->buffers = g_slist_append (stream->buffers, buffer);
         GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
         break;
@@ -4805,6 +4871,11 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
       case FOURCC_MP4V:
       case FOURCC_fmp4:
       case FOURCC_FMP4:
+      case FOURCC_apcs:
+      case FOURCC_apch:
+      case FOURCC_apcn:
+      case FOURCC_apco:
+      case FOURCC_ap4h:
       {
         const guint8 *buf;
         guint32 version;
@@ -4987,6 +5058,34 @@ qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
   return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
 }
 
+static void
+qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
+{
+  GstQuery *query;
+
+  query = gst_query_new_allocation (stream->caps, FALSE);
+
+  if (!gst_pad_peer_query (stream->pad, query)) {
+    /* not a problem, just debug a little */
+    GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
+  }
+
+  if (stream->allocator)
+    gst_object_unref (stream->allocator);
+
+  if (gst_query_get_n_allocation_params (query) > 0) {
+    /* try the allocator */
+    gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
+        &stream->params);
+    stream->use_allocator = TRUE;
+  } else {
+    stream->allocator = NULL;
+    gst_allocation_params_init (&stream->params);
+    stream->use_allocator = FALSE;
+  }
+  gst_query_unref (query);
+}
+
 static gboolean
 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
     QtDemuxStream * stream, GstTagList * list)
@@ -5096,7 +5195,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
         /* make sure it's not writable. We leave MALLOCDATA to NULL so that we
          * don't free any of the buffer data. */
         palette = _gst_buffer_new_wrapped ((gpointer) palette_data,
-            palette_count, NULL);
+            palette_count * 4, NULL);
 
         gst_caps_set_simple (stream->caps, "palette_data",
             GST_TYPE_BUFFER, palette, NULL);
@@ -5117,10 +5216,17 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
         gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
     g_free (name);
     if (stream->caps) {
-      /* FIXME: Need to set channel-mask here and maybe reorder */
       gst_caps_set_simple (stream->caps,
           "rate", G_TYPE_INT, (int) stream->rate,
           "channels", G_TYPE_INT, stream->n_channels, NULL);
+
+      if (stream->n_channels > 2) {
+        /* FIXME: Need to parse the 'chan' atom to get channel layouts
+         * correctly; this is just the minimum we can do - assume
+         * we don't actually have any channel positions. */
+        gst_caps_set_simple (stream->caps,
+            "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
+      }
     }
     qtdemux->n_audio_streams++;
   } else if (stream->subtype == FOURCC_strm) {
@@ -5132,12 +5238,21 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
         gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
     g_free (name);
     qtdemux->n_sub_streams++;
+  } else if (stream->caps) {
+    gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
+
+    stream->pad =
+        gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
+    g_free (name);
+    qtdemux->n_video_streams++;
   } else {
     GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
     goto done;
   }
 
   if (stream->pad) {
+    gchar *stream_id;
+
     GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
 
     gst_pad_use_fixed_caps (stream->pad);
@@ -5146,6 +5261,11 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
     gst_pad_set_active (stream->pad, TRUE);
 
     GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
+    stream_id =
+        gst_pad_create_stream_id_printf (stream->pad,
+        GST_ELEMENT_CAST (qtdemux), "%u", stream->track_id);
+    gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id));
+    g_free (stream_id);
     gst_pad_set_caps (stream->pad, stream->caps);
 
     GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
@@ -5153,10 +5273,12 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
     gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
 
     if (stream->pending_tags)
-      gst_tag_list_free (stream->pending_tags);
+      gst_tag_list_unref (stream->pending_tags);
     stream->pending_tags = list;
     /* global tags go on each pad anyway */
     stream->send_global_tags = TRUE;
+
+    qtdemux_do_allocation (qtdemux, stream);
   }
 done:
   return TRUE;
@@ -5175,9 +5297,9 @@ qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
       G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
 
   while (TRUE) {
-    guint8 *bdata;
-    gsize bsize;
+    GstMapInfo map;
 
+    buf = NULL;
     ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
     if (G_UNLIKELY (ret != GST_FLOW_OK))
       goto locate_failed;
@@ -5187,9 +5309,9 @@ qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
       gst_buffer_unref (buf);
       goto locate_failed;
     }
-    bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-    extract_initial_length_and_fourcc (bdata, 16, length, &lfourcc);
-    gst_buffer_unmap (buf, bdata, bsize);
+    gst_buffer_map (buf, &map, GST_MAP_READ);
+    extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
+    gst_buffer_unmap (buf, &map);
     gst_buffer_unref (buf);
 
     if (G_UNLIKELY (*length == 0)) {
@@ -5229,8 +5351,7 @@ qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
   GstBuffer *buf = NULL;
   GstFlowReturn ret = GST_FLOW_OK;
   GstFlowReturn res = GST_FLOW_OK;
-  guint8 *bdata;
-  gsize bsize;
+  GstMapInfo map;
 
   offset = qtdemux->moof_offset;
   GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
@@ -5250,15 +5371,15 @@ qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
   ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto flow_failed;
-  bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-  if (!qtdemux_parse_moof (qtdemux, bdata, bsize, offset, NULL)) {
-    gst_buffer_unmap (buf, bdata, bsize);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
+    gst_buffer_unmap (buf, &map);
     gst_buffer_unref (buf);
     buf = NULL;
     goto parse_failed;
   }
 
-  gst_buffer_unmap (buf, bdata, bsize);
+  gst_buffer_unmap (buf, &map);
   gst_buffer_unref (buf);
   buf = NULL;
 
@@ -5285,7 +5406,7 @@ parse_failed:
 flow_failed:
   {
     /* maybe upstream temporarily flushing */
-    if (ret != GST_FLOW_WRONG_STATE) {
+    if (ret != GST_FLOW_FLUSHING) {
       GST_DEBUG_OBJECT (qtdemux, "no next moof");
       offset = 0;
     } else {
@@ -5317,8 +5438,13 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
   GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
 
   /* make sure there's enough data */
-  if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 2 * 4))
-    goto corrupt_file;
+  if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
+    stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
+    GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
+        stream->n_sample_times);
+    if (!stream->n_sample_times)
+      goto corrupt_file;
+  }
 
   /* sync sample atom */
   stream->stps_present = FALSE;
@@ -6097,7 +6223,7 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
                 seqh_size = QT_UINT32 (data + 4);
                 if (seqh_size > 0) {
                   _seqh = gst_buffer_new_and_alloc (seqh_size);
-                  _gst_buffer_copy_into_mem (_seqh, data + 8, 0, seqh_size);
+                  _gst_buffer_copy_into_mem (_seqh, 0, data + 8, seqh_size);
                 }
               }
             }
@@ -6203,7 +6329,8 @@ qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
                   break;
                 }
                 /* skipping to the next entry */
-                gst_byte_reader_skip (&dref, atom_size - 8);
+                if (!gst_byte_reader_skip (&dref, atom_size - 8))
+                  break;
               } else {
                 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
                     "atom header");
@@ -6213,7 +6340,8 @@ qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
             break;
           }
           /* skip to the next entry */
-          gst_byte_reader_skip (&dref, size - 8);
+          if (!gst_byte_reader_skip (&dref, size - 8))
+            break;
         } else {
           GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
         }
@@ -6254,24 +6382,24 @@ qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
   static const guint wb_bitrates[] = {
     6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
   };
-  guint8 *data;
-  gsize size, max_mode;
+  GstMapInfo map;
+  gsize max_mode;
   guint16 mode_set;
 
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
 
-  if (size != 0x11) {
-    GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, size);
+  if (map.size != 0x11) {
+    GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
     goto bad_data;
   }
 
-  if (QT_FOURCC (data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
+  if (QT_FOURCC (map.data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
     GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
-        GST_FOURCC_ARGS (QT_UINT32 (data + 4)));
+        GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
     goto bad_data;
   }
 
-  mode_set = QT_UINT16 (data + 13);
+  mode_set = QT_UINT16 (map.data + 13);
 
   if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
     max_mode = 7 + (wb ? 1 : 0);
@@ -6285,11 +6413,11 @@ qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
     goto bad_data;
   }
 
-  gst_buffer_unmap (buf, data, size);
+  gst_buffer_unmap (buf, &map);
   return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
 
 bad_data:
-  gst_buffer_unmap (buf, data, size);
+  gst_buffer_unmap (buf, &map);
   return 0;
 }
 
@@ -6557,7 +6685,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                     avc_data + 8 + 1, size - 1);
 
                 buf = gst_buffer_new_and_alloc (size);
-                _gst_buffer_copy_into_mem (buf, avc_data + 0x8, 0, size);
+                _gst_buffer_copy_into_mem (buf, 0, avc_data + 0x8, size);
                 gst_caps_set_simple (stream->caps,
                     "codec_data", GST_TYPE_BUFFER, buf, NULL);
                 gst_buffer_unref (buf);
@@ -6636,7 +6764,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             if (len > 0x8) {
               len -= 0x8;
               buf = gst_buffer_new_and_alloc (len);
-              _gst_buffer_copy_into_mem (buf, data + 8, 0, len);
+              _gst_buffer_copy_into_mem (buf, 0, data + 8, len);
               gst_caps_set_simple (stream->caps,
                   "codec_data", GST_TYPE_BUFFER, buf, NULL);
               gst_buffer_unref (buf);
@@ -6820,7 +6948,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             if (len > 0x8) {
               len -= 0x8;
               buf = gst_buffer_new_and_alloc (len);
-              _gst_buffer_copy_into_mem (buf, data + 8, 0, len);
+              _gst_buffer_copy_into_mem (buf, 0, data + 8, len);
               gst_caps_set_simple (stream->caps,
                   "codec_data", GST_TYPE_BUFFER, buf, NULL);
               gst_buffer_unref (buf);
@@ -6850,7 +6978,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
           GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
           buf = gst_buffer_new_and_alloc (len);
-          _gst_buffer_copy_into_mem (buf, stsd_data, 0, len);
+          _gst_buffer_copy_into_mem (buf, 0, stsd_data, len);
           gst_caps_set_simple (stream->caps,
               "codec_data", GST_TYPE_BUFFER, buf, NULL);
           gst_buffer_unref (buf);
@@ -6899,7 +7027,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             break;
           }
           buf = gst_buffer_new_and_alloc (ovc1_len - 198);
-          _gst_buffer_copy_into_mem (buf, ovc1_data + 198, 0, ovc1_len - 198);
+          _gst_buffer_copy_into_mem (buf, 0, ovc1_data + 198, ovc1_len - 198);
           gst_caps_set_simple (stream->caps,
               "codec_data", GST_TYPE_BUFFER, buf, NULL);
           gst_buffer_unref (buf);
@@ -7074,7 +7202,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         }
         if (enda) {
           gst_caps_set_simple (stream->caps,
-              "format", G_TYPE_STRING, "S24_3LE", NULL);
+              "format", G_TYPE_STRING, "S24LE", NULL);
         }
         break;
       }
@@ -7113,7 +7241,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         }
         wfex = (WAVEFORMATEX *) (owma_data + 36);
         buf = gst_buffer_new_and_alloc (owma_len - 54);
-        _gst_buffer_copy_into_mem (buf, owma_data + 54, 0, owma_len - 54);
+        _gst_buffer_copy_into_mem (buf, 0, owma_data + 54, owma_len - 54);
         if (wfex->wFormatTag == 0x0161) {
           codec_name = "Windows Media Audio";
           version = 2;
@@ -7212,7 +7340,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
               headerlen -= 8;
 
               headerbuf = gst_buffer_new_and_alloc (headerlen);
-              _gst_buffer_copy_into_mem (headerbuf, waveheader, 0, headerlen);
+              _gst_buffer_copy_into_mem (headerbuf, 0, waveheader, headerlen);
 
               if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
                       headerbuf, &header, &extra)) {
@@ -7223,6 +7351,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
                 if (extra)
                   gst_buffer_unref (extra);
+                g_free (header);
               }
             }
           } else
@@ -7251,7 +7380,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           if (len > 0x4C) {
             GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
 
-            _gst_buffer_copy_into_mem (buf, stsd_data + 0x4C, 0, len - 0x4C);
+            _gst_buffer_copy_into_mem (buf, 0, stsd_data + 0x4C, len - 0x4C);
             gst_caps_set_simple (stream->caps,
                 "codec_data", GST_TYPE_BUFFER, buf, NULL);
             gst_buffer_unref (buf);
@@ -7286,7 +7415,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
               /* codec-data contains alac atom size and prefix,
                * ffmpeg likes it that way, not quite gst-ish though ...*/
               buf = gst_buffer_new_and_alloc (len);
-              _gst_buffer_copy_into_mem (buf, alac->data, 0, len);
+              _gst_buffer_copy_into_mem (buf, 0, alac->data, len);
               gst_caps_set_simple (stream->caps,
                   "codec_data", GST_TYPE_BUFFER, buf, NULL);
               gst_buffer_unref (buf);
@@ -7307,7 +7436,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
             guint bitrate;
 
-            _gst_buffer_copy_into_mem (buf, stsd_data + 0x34, 0, len - 0x34);
+            _gst_buffer_copy_into_mem (buf, 0, stsd_data + 0x34, len - 0x34);
 
             /* If we have enough data, let's try to get the 'damr' atom. See
              * the 3GPP container spec (26.244) for more details. */
@@ -7408,7 +7537,22 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         break;
     }
   } else {
-    goto unknown_stream;
+    /* everything in 1 sample */
+    stream->sampled = TRUE;
+
+    stream->caps =
+        qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec);
+
+    if (stream->caps == NULL)
+      goto unknown_stream;
+
+    if (codec) {
+      list = gst_tag_list_new_empty ();
+      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          GST_TAG_SUBTITLE_CODEC, codec, NULL);
+      g_free (codec);
+      codec = NULL;
+    }
   }
 
   /* promote to sampled format */
@@ -7539,7 +7683,8 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
 
   GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
 
-  if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)) {
+  if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
+      || size <= 0) {
     GST_DEBUG_OBJECT (qtdemux,
         "Size in bytes of the stream not known - bailing");
     return;
@@ -7548,7 +7693,10 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
   /* Subtract the header size */
   GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
       size, qtdemux->header_size);
-  g_assert (size >= qtdemux->header_size);
+
+  if (size < qtdemux->header_size)
+    return;
+
   size = size - qtdemux->header_size;
 
   if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
@@ -7696,8 +7844,6 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
   if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
     GstMessage *m;
 
-    qtdemux_post_global_tags (qtdemux);
-
     GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
         "an external content");
     m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
@@ -7719,18 +7865,21 @@ qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
     return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
         GST_MAKE_FOURCC ('3', 'g', 0, 0));
   } else if (qtdemux->comp_brands != NULL) {
+    GstMapInfo map;
     guint8 *data;
     gsize size;
     gboolean res = FALSE;
 
-    data = gst_buffer_map (qtdemux->comp_brands, &size, NULL, GST_MAP_READ);
+    gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
+    data = map.data;
+    size = map.size;
     while (size >= 4) {
       res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
           GST_MAKE_FOURCC ('3', 'g', 0, 0));
       data += 4;
       size -= 4;
     }
-    gst_buffer_unmap (qtdemux->comp_brands, data, size);
+    gst_buffer_unmap (qtdemux->comp_brands, &map);
     return res;
   } else {
     return FALSE;
@@ -8403,7 +8552,7 @@ qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
   if (len < 12 + 2)
     return;
 
-  buf = gst_buffer_new_allocate (NULL, len - 14, 0);
+  buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
   gst_buffer_fill (buf, 0, data + 14, len - 14);
 
   taglist = gst_tag_list_from_id3v2_tag (buf);
@@ -8415,7 +8564,7 @@ qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
   }
 
   if (taglist)
-    gst_tag_list_free (taglist);
+    gst_tag_list_unref (taglist);
 
   gst_buffer_unref (buf);
 }
@@ -8502,14 +8651,15 @@ qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
   GstBuffer *buf;
   gchar *media_type;
   const gchar *style;
-  GstCaps *caps;
+  GstSample *sample;
+  GstStructure *s;
   guint i;
   guint8 ndata[4];
 
   data = node->data;
   len = QT_UINT32 (data);
   buf = gst_buffer_new_and_alloc (len);
-  _gst_buffer_copy_into_mem (buf, data, 0, len);
+  _gst_buffer_copy_into_mem (buf, 0, data, len);
 
   /* heuristic to determine style of tag */
   if (QT_FOURCC (data + 4) == FOURCC_____ ||
@@ -8534,18 +8684,16 @@ qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
       ndata[0], ndata[1], ndata[2], ndata[3]);
   GST_DEBUG_OBJECT (demux, "media type %s", media_type);
 
-  caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL);
-  // TODO conver to metadata or ???
-//   gst_buffer_set_caps (buf, caps);
-  gst_caps_unref (caps);
+  s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
+  sample = gst_sample_new (buf, NULL, NULL, s);
+  gst_buffer_unref (buf);
   g_free (media_type);
 
-  GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT,
-      len, caps);
+  GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
+      len, s);
 
   gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND,
-      GST_QT_DEMUX_PRIVATE_TAG, buf, NULL);
-  gst_buffer_unref (buf);
+      GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
 }
 
 static void
@@ -8570,8 +8718,10 @@ qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta)
   }
 
   GST_DEBUG_OBJECT (qtdemux, "new tag list");
-  if (!qtdemux->tag_list)
+  if (!qtdemux->tag_list) {
     qtdemux->tag_list = gst_tag_list_new_empty ();
+    gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
+  }
 
   i = 0;
   while (i < G_N_ELEMENTS (add_funcs)) {
@@ -8788,8 +8938,10 @@ qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
 {
   const gchar *fmt;
 
-  if (tags == NULL)
+  if (tags == NULL) {
     tags = gst_tag_list_new_empty ();
+    gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
+  }
 
   if (qtdemux->major_brand == FOURCC_mjp2)
     fmt = "Motion JPEG 2000";
@@ -8866,8 +9018,10 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
     }
   }
   if (datetime) {
-    if (!qtdemux->tag_list)
+    if (!qtdemux->tag_list) {
       qtdemux->tag_list = gst_tag_list_new_empty ();
+      gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
+    }
 
     /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
     gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
@@ -9146,7 +9300,7 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
     GstBuffer *buffer;
 
     buffer = gst_buffer_new_and_alloc (data_len);
-    _gst_buffer_copy_into_mem (buffer, data_ptr, 0, data_len);
+    _gst_buffer_copy_into_mem (buffer, 0, data_ptr, data_len);
 
     GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
     GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
@@ -9313,11 +9467,6 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_simple ("video/x-msmpeg",
           "msmpegversion", G_TYPE_INT, 43, NULL);
       break;
-    case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
-    case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
-      _codec ("3ivX video");
-      caps = gst_caps_new_empty_simple ("video/x-3ivx");
-      break;
     case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
       _codec ("DivX 3");
       caps = gst_caps_new_simple ("video/x-divx",
@@ -9334,18 +9483,17 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_simple ("video/x-divx",
           "divxversion", G_TYPE_INT, 5, NULL);
       break;
+
+    case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
+    case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
     case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
     case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
-      _codec ("XVID MPEG-4");
-      caps = gst_caps_new_empty_simple ("video/x-xvid");
-      break;
-
     case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
     case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
       caps = gst_caps_new_simple ("video/mpeg",
           "mpegversion", G_TYPE_INT, 4, NULL);
       if (codec_name)
-        *codec_name = g_strdup ("FFmpeg MPEG-4");
+        *codec_name = g_strdup ("MPEG-4");
       break;
 
     case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
@@ -9442,6 +9590,36 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       _codec ("On2 VP8");
       caps = gst_caps_from_string ("video/x-vp8");
       break;
+    case GST_MAKE_FOURCC ('a', 'p', 'c', 's'):
+      _codec ("Apple ProRes LT");
+      caps =
+          gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
+          NULL);
+      break;
+    case GST_MAKE_FOURCC ('a', 'p', 'c', 'h'):
+      _codec ("Apple ProRes HQ");
+      caps =
+          gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
+          NULL);
+      break;
+    case GST_MAKE_FOURCC ('a', 'p', 'c', 'n'):
+      _codec ("Apple ProRes");
+      caps =
+          gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+          "standard", NULL);
+      break;
+    case GST_MAKE_FOURCC ('a', 'p', 'c', 'o'):
+      _codec ("Apple ProRes Proxy");
+      caps =
+          gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+          "proxy", NULL);
+      break;
+    case GST_MAKE_FOURCC ('a', 'p', '4', 'h'):
+      _codec ("Apple ProRes 4444");
+      caps =
+          gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
+          "4444", NULL);
+      break;
     case FOURCC_ovc1:
       _codec ("VC-1");
       caps = gst_caps_new_simple ("video/x-wmv",
@@ -9450,10 +9628,10 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
     case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
     default:
     {
-      char *s;
+      char *s, fourstr[5];
 
-      s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
-          GST_FOURCC_ARGS (fourcc));
+      g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+      s = g_strdup_printf ("video/x-gst-fourcc-%s", g_strstrip (fourstr));
       caps = gst_caps_new_empty_simple (s);
       break;
     }
@@ -9484,7 +9662,8 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
     case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
       _codec ("Raw 8-bit PCM audio");
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, "U8", NULL);
+          "format", G_TYPE_STRING, "U8",
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
       endian = G_BIG_ENDIAN;
@@ -9506,30 +9685,35 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       g_free (str);
 
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, gst_audio_format_to_string (format), NULL);
+          "format", G_TYPE_STRING, gst_audio_format_to_string (format),
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     }
     case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
       _codec ("Raw 64-bit floating-point audio");
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, "F64BE", NULL);
+          "format", G_TYPE_STRING, "F64BE",
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
       _codec ("Raw 32-bit floating-point audio");
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, "F32BE", NULL);
+          "format", G_TYPE_STRING, "F32BE",
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     case FOURCC_in24:
       _codec ("Raw 24-bit PCM audio");
       /* we assume BIG ENDIAN, an enda box will tell us to change this to little
        * endian later */
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, "S24BE", NULL);
+          "format", G_TYPE_STRING, "S24BE",
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
       _codec ("Raw 32-bit PCM audio");
       caps = gst_caps_new_simple ("audio/x-raw",
-          "format", G_TYPE_STRING, "S32BE", NULL);
+          "format", G_TYPE_STRING, "S32BE",
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
     case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
       _codec ("Mu-law audio");
@@ -9654,10 +9838,10 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       /* ? */
     default:
     {
-      char *s;
+      char *s, fourstr[5];
 
-      s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
-          GST_FOURCC_ARGS (fourcc));
+      g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+      s = g_strdup_printf ("audio/x-gst-fourcc-%s", g_strstrip (fourstr));
       caps = gst_caps_new_empty_simple (s);
       break;
     }
@@ -9692,7 +9876,7 @@ qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
   switch (fourcc) {
     case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
       _codec ("DVD subtitle");
-      caps = gst_caps_new_empty_simple ("video/x-dvd-subpicture");
+      caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
       break;
     case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
       _codec ("Quicktime timed text");
@@ -9700,19 +9884,39 @@ qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
     case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
       _codec ("3GPP timed text");
     text:
-      caps = gst_caps_new_empty_simple ("text/plain");
+      caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
+          "utf8", NULL);
       /* actual text piece needs to be extracted */
       stream->need_process = TRUE;
       break;
     default:
     {
-      char *s;
+      char *s, fourstr[5];
 
-      s = g_strdup_printf ("text/x-gst-fourcc-%" GST_FOURCC_FORMAT,
-          GST_FOURCC_ARGS (fourcc));
+      g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+      s = g_strdup_printf ("text/x-gst-fourcc-%s", g_strstrip (fourstr));
       caps = gst_caps_new_empty_simple (s);
       break;
     }
   }
   return caps;
 }
+
+static GstCaps *
+qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
+    guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
+{
+  GstCaps *caps;
+
+  switch (fourcc) {
+    case GST_MAKE_FOURCC ('m', '1', 'v', ' '):
+      _codec ("MPEG 1 video");
+      caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
+          "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+      break;
+    default:
+      caps = NULL;
+      break;
+  }
+  return caps;
+}