rmdemux: Make sure we have enough data available when parsing audio/video packets
[platform/upstream/gstreamer.git] / gst / realmedia / rmdemux.c
index 2e6abe5..68b0736 100644 (file)
@@ -20,8 +20,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.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -55,7 +55,6 @@ struct _GstRMDemuxStream
 
   int id;
   GstPad *pad;
-  GstFlowReturn last_flow;
   gboolean discont;
   int timescale;
 
@@ -198,13 +197,13 @@ gst_rmdemux_base_init (GstRMDemuxClass * klass)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_rmdemux_sink_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_rmdemux_videosrc_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_rmdemux_audiosrc_template));
-  gst_element_class_set_details_simple (element_class, "RealMedia Demuxer",
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_rmdemux_sink_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_rmdemux_videosrc_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_rmdemux_audiosrc_template);
+  gst_element_class_set_static_metadata (element_class, "RealMedia Demuxer",
       "Codec/Demuxer",
       "Demultiplex a RealMedia file into audio and video streams",
       "David Schleef <ds@schleef.org>");
@@ -238,6 +237,10 @@ gst_rmdemux_finalize (GObject * object)
     g_object_unref (rmdemux->adapter);
     rmdemux->adapter = NULL;
   }
+  if (rmdemux->flowcombiner) {
+    gst_flow_combiner_free (rmdemux->flowcombiner);
+    rmdemux->flowcombiner = NULL;
+  }
 
   GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
 }
@@ -262,6 +265,9 @@ gst_rmdemux_init (GstRMDemux * rmdemux)
   rmdemux->first_ts = GST_CLOCK_TIME_NONE;
   rmdemux->base_ts = GST_CLOCK_TIME_NONE;
   rmdemux->need_newsegment = TRUE;
+  rmdemux->have_group_id = FALSE;
+  rmdemux->group_id = G_MAXUINT;
+  rmdemux->flowcombiner = gst_flow_combiner_new ();
 
   gst_rm_utils_run_tests ();
 }
@@ -343,8 +349,9 @@ gst_rmdemux_validate_offset (GstRMDemux * rmdemux)
   GstFlowReturn flowret;
   guint16 version, length;
   gboolean ret = TRUE;
-  guint8 *data;
+  GstMapInfo map;
 
+  buffer = NULL;
   flowret = gst_pad_pull_range (rmdemux->sinkpad, rmdemux->offset, 4, &buffer);
 
   if (flowret != GST_FLOW_OK) {
@@ -361,21 +368,21 @@ gst_rmdemux_validate_offset (GstRMDemux * rmdemux)
    * 4 bytes, and we can check that it won't take us past our known total size
    */
 
-  data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
-  version = RMDEMUX_GUINT16_GET (data);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  version = RMDEMUX_GUINT16_GET (map.data);
   if (version != 0 && version != 1) {
     GST_DEBUG_OBJECT (rmdemux, "Expected version 0 or 1, got %d",
         (int) version);
     ret = FALSE;
   }
 
-  length = RMDEMUX_GUINT16_GET (data + 2);
+  length = RMDEMUX_GUINT16_GET (map.data + 2);
   /* TODO: Also check against total stream length */
   if (length < 4) {
     GST_DEBUG_OBJECT (rmdemux, "Expected length >= 4, got %d", (int) length);
     ret = FALSE;
   }
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
 
   if (ret) {
     rmdemux->offset += 4;
@@ -520,8 +527,11 @@ gst_rmdemux_perform_seek (GstRMDemux * rmdemux, GstEvent * event)
   GST_LOG_OBJECT (rmdemux, "Took streamlock");
 
   if (event) {
-    gst_segment_do_seek (&rmdemux->segment, rate, format, flags,
-        cur_type, cur, stop_type, stop, &update);
+    if (!gst_segment_do_seek (&rmdemux->segment, rate, format, flags,
+            cur_type, cur, stop_type, stop, &update)) {
+      ret = FALSE;
+      goto done;
+    }
   }
 
   GST_DEBUG_OBJECT (rmdemux, "segment positions set to %" GST_TIME_FORMAT "-%"
@@ -584,7 +594,7 @@ gst_rmdemux_perform_seek (GstRMDemux * rmdemux, GstEvent * event)
     /* restart our task since it might have been stopped when we did the 
      * flush. */
     gst_pad_start_task (rmdemux->sinkpad, (GstTaskFunction) gst_rmdemux_loop,
-        rmdemux->sinkpad);
+        rmdemux->sinkpad, NULL);
   }
 
 done:
@@ -644,6 +654,25 @@ gst_rmdemux_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
       }
       break;
     }
+    case GST_QUERY_SEGMENT:
+    {
+      GstFormat format;
+      gint64 start, stop;
+
+      format = rmdemux->segment.format;
+
+      start =
+          gst_segment_to_stream_time (&rmdemux->segment, format,
+          rmdemux->segment.start);
+      if ((stop = rmdemux->segment.stop) == -1)
+        stop = rmdemux->segment.duration;
+      else
+        stop = gst_segment_to_stream_time (&rmdemux->segment, format, stop);
+
+      gst_query_set_segment (query, rmdemux->segment.rate, format, start, stop);
+      res = TRUE;
+      break;
+    }
     default:
       res = gst_pad_query_default (pad, parent, query);
       break;
@@ -653,6 +682,19 @@ gst_rmdemux_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
 }
 
 static void
+gst_rmdemux_free_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
+{
+  g_object_unref (stream->adapter);
+  gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream);
+  if (stream->pending_tags)
+    gst_tag_list_unref (stream->pending_tags);
+  if (stream->subpackets)
+    g_ptr_array_free (stream->subpackets, TRUE);
+  g_free (stream->index);
+  g_free (stream);
+}
+
+static void
 gst_rmdemux_reset (GstRMDemux * rmdemux)
 {
   GSList *cur;
@@ -664,15 +706,9 @@ gst_rmdemux_reset (GstRMDemux * rmdemux)
   for (cur = rmdemux->streams; cur; cur = cur->next) {
     GstRMDemuxStream *stream = cur->data;
 
-    g_object_unref (stream->adapter);
-    gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream);
+    gst_flow_combiner_remove_pad (rmdemux->flowcombiner, stream->pad);
     gst_element_remove_pad (GST_ELEMENT (rmdemux), stream->pad);
-    if (stream->pending_tags)
-      gst_tag_list_free (stream->pending_tags);
-    if (stream->subpackets)
-      g_ptr_array_free (stream->subpackets, TRUE);
-    g_free (stream->index);
-    g_free (stream);
+    gst_rmdemux_free_stream (rmdemux, stream);
   }
   g_slist_free (rmdemux->streams);
   rmdemux->streams = NULL;
@@ -680,7 +716,7 @@ gst_rmdemux_reset (GstRMDemux * rmdemux)
   rmdemux->n_video_streams = 0;
 
   if (rmdemux->pending_tags != NULL) {
-    gst_tag_list_free (rmdemux->pending_tags);
+    gst_tag_list_unref (rmdemux->pending_tags);
     rmdemux->pending_tags = NULL;
   }
 
@@ -692,6 +728,9 @@ gst_rmdemux_reset (GstRMDemux * rmdemux)
   rmdemux->first_ts = GST_CLOCK_TIME_NONE;
   rmdemux->base_ts = GST_CLOCK_TIME_NONE;
   rmdemux->need_newsegment = TRUE;
+
+  rmdemux->have_group_id = FALSE;
+  rmdemux->group_id = G_MAXUINT;
 }
 
 static GstStateChangeReturn
@@ -752,7 +791,8 @@ gst_rmdemux_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)
@@ -780,6 +820,7 @@ gst_rmdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
   switch (mode) {
     case GST_PAD_MODE_PUSH:
       demux->seekable = FALSE;
+      demux->running = active;
       res = TRUE;
       break;
     case GST_PAD_MODE_PULL:
@@ -790,7 +831,7 @@ gst_rmdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
         demux->data_offset = G_MAXUINT;
         res =
             gst_pad_start_task (sinkpad, (GstTaskFunction) gst_rmdemux_loop,
-            sinkpad);
+            sinkpad, NULL);
       } else {
         res = gst_pad_stop_task (sinkpad);
       }
@@ -837,6 +878,7 @@ gst_rmdemux_loop (GstPad * pad)
       size = rmdemux->size;
   }
 
+  buffer = NULL;
   ret = gst_pad_pull_range (pad, rmdemux->offset, size, &buffer);
   if (ret != GST_FLOW_OK) {
     if (rmdemux->offset == rmdemux->index_offset) {
@@ -920,14 +962,15 @@ need_pause:
         gst_element_post_message (GST_ELEMENT (rmdemux),
             gst_message_new_segment_done (GST_OBJECT (rmdemux),
                 GST_FORMAT_TIME, stop));
+        gst_rmdemux_send_event (rmdemux,
+            gst_event_new_segment_done (GST_FORMAT_TIME, stop));
       } else {
         /* normal playback, send EOS to all linked pads */
         GST_LOG_OBJECT (rmdemux, "Sending EOS, at end of stream");
         gst_rmdemux_send_event (rmdemux, gst_event_new_eos ());
       }
     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
-      GST_ELEMENT_ERROR (rmdemux, STREAM, FAILED,
-          (NULL), ("stream stopped, reason %s", reason));
+      GST_ELEMENT_FLOW_ERROR (rmdemux, ret);
       gst_rmdemux_send_event (rmdemux, gst_event_new_eos ());
     }
     return;
@@ -958,7 +1001,11 @@ gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
   GstRMDemux *rmdemux = GST_RMDEMUX (parent);
 
   if (rmdemux->base_ts == -1) {
-    rmdemux->base_ts = GST_BUFFER_TIMESTAMP (buffer);
+    if (GST_BUFFER_DTS_IS_VALID (buffer))
+      rmdemux->base_ts = GST_BUFFER_DTS (buffer);
+    else
+      rmdemux->base_ts = GST_BUFFER_PTS (buffer);
+
     GST_LOG_OBJECT (rmdemux, "base_ts %" GST_TIME_FORMAT,
         GST_TIME_ARGS (rmdemux->base_ts));
   }
@@ -1260,7 +1307,6 @@ gst_rmdemux_send_event (GstRMDemux * rmdemux, GstEvent * event)
         stream->next_ts = -1;
         stream->last_seq = -1;
         stream->next_seq = -1;
-        stream->last_flow = GST_FLOW_OK;
         break;
       default:
         break;
@@ -1277,6 +1323,7 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
   GstCaps *stream_caps = NULL;
   const gchar *codec_tag = NULL;
   gchar *codec_name = NULL;
+  gchar *stream_id;
   int version = 0;
 
   if (stream->subtype == GST_RMDEMUX_STREAM_VIDEO) {
@@ -1400,7 +1447,8 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
         if (stream->flavor > 3) {
           GST_WARNING_OBJECT (rmdemux, "bad SIPR flavor %d, freeing it",
               stream->flavor);
-          g_free (stream);
+          g_object_unref (stream->pad);
+          gst_rmdemux_free_stream (rmdemux, stream);
           goto beach;
         }
 
@@ -1443,7 +1491,7 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
   } else {
     GST_WARNING_OBJECT (rmdemux, "not adding stream of type %d, freeing it",
         stream->subtype);
-    g_free (stream);
+    gst_rmdemux_free_stream (rmdemux, stream);
     goto beach;
   }
 
@@ -1456,6 +1504,7 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
       stream_caps);
 
   if (stream->pad && stream_caps) {
+    GstEvent *event;
 
     GST_LOG_OBJECT (rmdemux, "%d bytes of extra data for stream %s",
         stream->extra_data_size, GST_PAD_NAME (stream->pad));
@@ -1475,7 +1524,6 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
 
     gst_pad_use_fixed_caps (stream->pad);
 
-    gst_pad_set_caps (stream->pad, stream_caps);
     gst_pad_set_event_function (stream->pad,
         GST_DEBUG_FUNCPTR (gst_rmdemux_src_event));
     gst_pad_set_query_function (stream->pad,
@@ -1484,7 +1532,32 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
     GST_DEBUG_OBJECT (rmdemux, "adding pad %s with caps %" GST_PTR_FORMAT
         ", stream_id=%d", GST_PAD_NAME (stream->pad), stream_caps, stream->id);
     gst_pad_set_active (stream->pad, TRUE);
-    gst_element_add_pad (GST_ELEMENT_CAST (rmdemux), stream->pad);
+
+    stream_id =
+        gst_pad_create_stream_id_printf (stream->pad,
+        GST_ELEMENT_CAST (rmdemux), "%03u", stream->id);
+
+    event =
+        gst_pad_get_sticky_event (rmdemux->sinkpad, GST_EVENT_STREAM_START, 0);
+    if (event) {
+      if (gst_event_parse_group_id (event, &rmdemux->group_id))
+        rmdemux->have_group_id = TRUE;
+      else
+        rmdemux->have_group_id = FALSE;
+      gst_event_unref (event);
+    } else if (!rmdemux->have_group_id) {
+      rmdemux->have_group_id = TRUE;
+      rmdemux->group_id = gst_util_group_id_next ();
+    }
+
+    event = gst_event_new_stream_start (stream_id);
+    if (rmdemux->have_group_id)
+      gst_event_set_group_id (event, rmdemux->group_id);
+
+    gst_pad_push_event (stream->pad, event);
+    g_free (stream_id);
+
+    gst_pad_set_caps (stream->pad, stream_caps);
 
     codec_name = gst_pb_utils_get_codec_description (stream_caps);
 
@@ -1496,6 +1569,8 @@ gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
           codec_tag, codec_name, NULL);
       g_free (codec_name);
     }
+    gst_element_add_pad (GST_ELEMENT_CAST (rmdemux), stream->pad);
+    gst_flow_combiner_add_pad (rmdemux->flowcombiner, stream->pad);
   }
 
 beach:
@@ -1566,7 +1641,6 @@ gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length)
   stream->seek_offset = 0;
   stream->last_ts = -1;
   stream->next_ts = -1;
-  stream->last_flow = GST_FLOW_OK;
   stream->discont = TRUE;
   stream->adapter = gst_adapter_new ();
   GST_LOG_OBJECT (rmdemux, "stream_number=%d", stream->id);
@@ -1857,43 +1931,20 @@ gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, int length)
 
   tags = gst_rm_utils_read_tags (data, length, gst_rm_utils_read_string16);
 
-  GST_LOG_OBJECT (rmdemux, "tags: %" GST_PTR_FORMAT, tags);
-
-  rmdemux->pending_tags =
-      gst_tag_list_merge (rmdemux->pending_tags, tags, GST_TAG_MERGE_APPEND);
-}
-
-static GstFlowReturn
-gst_rmdemux_combine_flows (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
-    GstFlowReturn ret)
-{
-  GSList *cur;
-
-  /* store the value */
-  stream->last_flow = ret;
+  if (tags) {
+    GstTagList *old_tags = rmdemux->pending_tags;
 
-  /* if it's success we can return the value right away */
-  if (ret == GST_FLOW_OK)
-    goto done;
+    GST_LOG_OBJECT (rmdemux, "tags: %" GST_PTR_FORMAT, tags);
 
-  /* any other error that is not-linked can be returned right
-   * away */
-  if (ret != GST_FLOW_NOT_LINKED)
-    goto done;
+    rmdemux->pending_tags =
+        gst_tag_list_merge (old_tags, tags, GST_TAG_MERGE_APPEND);
 
-  for (cur = rmdemux->streams; cur; cur = cur->next) {
-    GstRMDemuxStream *ostream = cur->data;
+    gst_tag_list_unref (tags);
+    if (old_tags)
+      gst_tag_list_unref (old_tags);
 
-    ret = ostream->last_flow;
-    /* some other return value (must be SUCCESS but we can return
-     * other values as well) */
-    if (ret != GST_FLOW_NOT_LINKED)
-      goto done;
+    gst_tag_list_set_scope (rmdemux->pending_tags, GST_TAG_SCOPE_GLOBAL);
   }
-  /* if we get here, all other pads were unlinked and we return
-   * NOT_LINKED then */
-done:
-  return ret;
 }
 
 static void
@@ -1914,7 +1965,7 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
 {
   GstFlowReturn ret = GST_FLOW_ERROR;
   GstBuffer *outbuf;
-  guint8 *outdata;
+  GstMapInfo outmap;
   guint packet_size = stream->packet_size;
   guint height = stream->subpackets->len;
   guint leaf_size = stream->leaf_size;
@@ -1926,14 +1977,18 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
       leaf_size, height);
 
   outbuf = gst_buffer_new_and_alloc (height * packet_size);
-  outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
+  gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
 
   for (p = 0; p < height; ++p) {
     GstBuffer *b = g_ptr_array_index (stream->subpackets, p);
-    guint8 *b_data = gst_buffer_map (b, NULL, NULL, GST_MAP_READ);
+    GstMapInfo map;
+
+    gst_buffer_map (b, &map, GST_MAP_READ);
 
-    if (p == 0)
-      GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (b);
+    if (p == 0) {
+      GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (b);
+      GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (b);
+    }
 
     for (x = 0; x < packet_size / leaf_size; ++x) {
       guint idx;
@@ -1941,11 +1996,12 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
       idx = height * x + ((height + 1) / 2) * (p % 2) + (p / 2);
 
       /* GST_LOG ("%3u => %3u", (height * p) + x, idx); */
-      memcpy (outdata + leaf_size * idx, b_data + leaf_size * x, leaf_size);
+      memcpy (outmap.data + leaf_size * idx, map.data + leaf_size * x,
+          leaf_size);
     }
-    gst_buffer_unmap (b, b_data, -1);
+    gst_buffer_unmap (b, &map);
   }
-  gst_buffer_unmap (outbuf, outdata, -1);
+  gst_buffer_unmap (outbuf, &outmap);
 
   /* some decoders, such as realaudiodec, need to be fed in packet units */
   for (p = 0; p < height; ++p) {
@@ -1955,8 +2011,9 @@ gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream)
         gst_buffer_copy_region (outbuf, GST_BUFFER_COPY_ALL, p * packet_size,
         packet_size);
 
-    GST_LOG_OBJECT (rmdemux, "pushing buffer timestamp %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (subbuf)));
+    GST_LOG_OBJECT (rmdemux, "pushing buffer dts %" GST_TIME_FORMAT ", pts %"
+        GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_DTS (subbuf)),
+        GST_TIME_ARGS (GST_BUFFER_PTS (subbuf)));
 
     if (stream->discont) {
       GST_BUFFER_FLAG_SET (subbuf, GST_BUFFER_FLAG_DISCONT);
@@ -2001,7 +2058,7 @@ gst_rmdemux_descramble_mp4a_audio (GstRMDemux * rmdemux,
   GstFlowReturn res;
   GstBuffer *buf, *outbuf;
   guint frames, index, i;
-  guint8 *data;
+  GstMapInfo map;
   GstClockTime timestamp;
 
   res = GST_FLOW_OK;
@@ -2010,18 +2067,20 @@ gst_rmdemux_descramble_mp4a_audio (GstRMDemux * rmdemux,
   g_ptr_array_index (stream->subpackets, 0) = NULL;
   g_ptr_array_set_size (stream->subpackets, 0);
 
-  data = gst_buffer_map (buf, NULL, NULL, GST_MAP_READ);
-  timestamp = GST_BUFFER_TIMESTAMP (buf);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  timestamp = GST_BUFFER_PTS (buf);
 
-  frames = (data[1] & 0xf0) >> 4;
+  frames = (map.data[1] & 0xf0) >> 4;
   index = 2 * frames + 2;
 
   for (i = 0; i < frames; i++) {
-    guint len = (data[i * 2 + 2] << 8) | data[i * 2 + 3];
+    guint len = (map.data[i * 2 + 2] << 8) | map.data[i * 2 + 3];
 
     outbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, index, len);
-    if (i == 0)
-      GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+    if (i == 0) {
+      GST_BUFFER_PTS (outbuf) = timestamp;
+      GST_BUFFER_DTS (outbuf) = timestamp;
+    }
 
     index += len;
 
@@ -2033,7 +2092,7 @@ gst_rmdemux_descramble_mp4a_audio (GstRMDemux * rmdemux,
     if (res != GST_FLOW_OK)
       break;
   }
-  gst_buffer_unmap (buf, data, -1);
+  gst_buffer_unmap (buf, &map);
   gst_buffer_unref (buf);
   return res;
 }
@@ -2044,7 +2103,7 @@ gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux,
 {
   GstFlowReturn ret;
   GstBuffer *outbuf;
-  guint8 *outdata;
+  GstMapInfo outmap;
   guint packet_size = stream->packet_size;
   guint height = stream->subpackets->len;
   guint p;
@@ -2055,20 +2114,23 @@ gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux,
       stream->leaf_size, height);
 
   outbuf = gst_buffer_new_and_alloc (height * packet_size);
-  outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
+  gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
 
   for (p = 0; p < height; ++p) {
     GstBuffer *b = g_ptr_array_index (stream->subpackets, p);
 
-    if (p == 0)
-      GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (b);
+    if (p == 0) {
+      GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (b);
+      GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (b);
+    }
 
-    gst_buffer_extract (b, 0, outdata + packet_size * p, packet_size);
+    gst_buffer_extract (b, 0, outmap.data + packet_size * p, packet_size);
   }
-  gst_buffer_unmap (outbuf, outdata, -1);
+  gst_buffer_unmap (outbuf, &outmap);
 
-  GST_LOG_OBJECT (rmdemux, "pushing buffer timestamp %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
+  GST_LOG_OBJECT (rmdemux, "pushing buffer dts %" GST_TIME_FORMAT ", pts %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
+      GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)));
 
   if (stream->discont) {
     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
@@ -2124,142 +2186,13 @@ gst_rmdemux_handle_scrambled_packet (GstRMDemux * rmdemux,
       ret = gst_rmdemux_descramble_sipr_audio (rmdemux, stream);
       break;
     default:
+      ret = GST_FLOW_ERROR;
       g_assert_not_reached ();
   }
 
   return ret;
 }
 
-static GstClockTime
-gst_rmdemux_fix_timestamp (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
-    guint8 * data, GstClockTime timestamp)
-{
-  guint8 frame_type;
-  guint16 seq;
-  GstClockTime ts = timestamp;
-
-  if (timestamp == GST_CLOCK_TIME_NONE)
-    goto done;
-
-  /* only adjust when we have a stream with B frames */
-  if (stream->format < 0x20200002)
-    goto done;
-
-  /* Fix timestamp. */
-  switch (stream->fourcc) {
-    case GST_RM_VDO_RV10:
-      goto done;
-    case GST_RM_VDO_RV20:
-    {
-      /*
-       * Bit  1- 2: frame type
-       * Bit  3- 9: ?
-       * Bit 10-22: sequence number
-       * Bit 23-32: ?
-       */
-      frame_type = (data[0] >> 6) & 0x03;
-      seq = ((data[1] & 0x7f) << 6) + ((data[2] & 0xfc) >> 2);
-      break;
-    }
-    case GST_RM_VDO_RV30:
-    {
-      /*
-       * Bit  1- 2: ?
-       * Bit     3: skip packet if 1
-       * Bit  4- 5: frame type
-       * Bit  6-12: ?
-       * Bit 13-25: sequence number
-       * Bit 26-32: ?
-       */
-      frame_type = (data[0] >> 3) & 0x03;
-      seq = ((data[1] & 0x0f) << 9) + (data[2] << 1) + ((data[3] & 0x80) >> 7);
-      break;
-    }
-    case GST_RM_VDO_RV40:
-    {
-      /*
-       * Bit     1: skip packet if 1
-       * Bit  2- 3: frame type
-       * Bit  4-13: ?
-       * Bit 14-26: sequence number
-       * Bit 27-32: ?
-       */
-      frame_type = (data[0] >> 5) & 0x03;
-      seq = ((data[1] & 0x07) << 10) + (data[2] << 2) + ((data[3] & 0xc0) >> 6);
-      break;
-    }
-    default:
-      goto unknown_version;
-  }
-
-  switch (frame_type) {
-    case 0:
-    case 1:
-    {
-      GST_LOG_OBJECT (rmdemux, "I frame %d", frame_type);
-      /* I frame */
-      if (stream->next_ts == -1)
-        stream->next_ts = timestamp;
-      else
-        timestamp = stream->next_ts;
-      stream->last_ts = stream->next_ts;
-      stream->next_ts = ts;
-      stream->last_seq = stream->next_seq;
-      stream->next_seq = seq;
-      break;
-    }
-    case 2:
-    {
-      GST_LOG_OBJECT (rmdemux, "P frame");
-      /* P frame */
-      timestamp = stream->last_ts = stream->next_ts;
-      if (seq < stream->next_seq)
-        stream->next_ts += (seq + 0x2000 - stream->next_seq) * GST_MSECOND;
-      else
-        stream->next_ts += (seq - stream->next_seq) * GST_MSECOND;
-      stream->last_seq = stream->next_seq;
-      stream->next_seq = seq;
-      break;
-    }
-    case 3:
-    {
-      GST_LOG_OBJECT (rmdemux, "B frame");
-      /* B frame */
-      if (seq < stream->last_seq) {
-        timestamp =
-            (seq + 0x2000 - stream->last_seq) * GST_MSECOND + stream->last_ts;
-      } else {
-        timestamp = (seq - stream->last_seq) * GST_MSECOND + stream->last_ts;
-      }
-      break;
-    }
-    default:
-      goto unknown_frame_type;
-  }
-
-done:
-  GST_LOG_OBJECT (rmdemux,
-      "timestamp %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT, GST_TIME_ARGS (ts),
-      GST_TIME_ARGS (timestamp));
-
-  return timestamp;
-
-  /* Errors */
-unknown_version:
-  {
-    GST_ELEMENT_ERROR (rmdemux, STREAM, DECODE,
-        ("Unknown version: %i.", stream->version), (NULL));
-    return GST_FLOW_ERROR;
-  }
-
-unknown_frame_type:
-  {
-    GST_ELEMENT_ERROR (rmdemux, STREAM, DECODE, ("Unknown frame type %d.",
-            frame_type), (NULL));
-    return GST_FLOW_ERROR;
-  }
-}
-
 #define PARSE_NUMBER(data, size, number, label) \
 G_STMT_START {                                  \
   if (size < 2)                                 \
@@ -2284,18 +2217,21 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
     GstClockTime timestamp, gboolean key)
 {
   GstFlowReturn ret;
+  GstMapInfo map;
   const guint8 *data;
-  guint8 *base;
   gsize size;
 
-  base = gst_buffer_map (in, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (in, &map, GST_MAP_READ);
 
-  data = base + offset;
-  size -= offset;
+  if (map.size < offset)
+    goto not_enough_data;
+
+  data = map.data + offset;
+  size = map.size - offset;
 
   /* if size <= 2, we want this method to return the same GstFlowReturn as it
    * was previously for that given stream. */
-  ret = stream->last_flow;
+  ret = GST_PAD_LAST_FLOW_RETURN (stream->pad);
 
   while (size > 2) {
     guint8 pkg_header;
@@ -2356,9 +2292,12 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
     }
     GST_DEBUG_OBJECT (rmdemux, "fragment size %d", fragment_size);
 
+    if (map.size < (data - map.data) + fragment_size)
+      goto not_enough_data;
+
     /* get the fragment */
     fragment =
-        gst_buffer_copy_region (in, GST_BUFFER_COPY_ALL, data - base,
+        gst_buffer_copy_region (in, GST_BUFFER_COPY_ALL, data - map.data,
         fragment_size);
 
     if (pkg_subseq == 1) {
@@ -2389,7 +2328,8 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
     /* flush fragment when complete */
     if (stream->frag_current >= stream->frag_length) {
       GstBuffer *out;
-      guint8 *outdata, *outbase;
+      GstMapInfo outmap;
+      guint8 *outdata;
       guint header_size;
       gint i, avail;
 
@@ -2412,8 +2352,8 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
       avail = gst_adapter_available (stream->adapter);
 
       out = gst_buffer_new_and_alloc (header_size + avail);
-      outbase = gst_buffer_map (out, NULL, NULL, GST_MAP_WRITE);
-      outdata = outbase;
+      gst_buffer_map (out, &outmap, GST_MAP_WRITE);
+      outdata = outmap.data;
 
       /* create header */
       *outdata++ = stream->frag_count - 1;
@@ -2441,12 +2381,11 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
         if (rmdemux->base_ts != -1)
           timestamp += rmdemux->base_ts;
       }
-      timestamp =
-          gst_rmdemux_fix_timestamp (rmdemux, stream, outdata, timestamp);
+      gst_buffer_unmap (out, &outmap);
 
-      gst_buffer_unmap (out, outbase, -1);
-
-      GST_BUFFER_TIMESTAMP (out) = timestamp;
+      /* video has DTS */
+      GST_BUFFER_DTS (out) = timestamp;
+      GST_BUFFER_PTS (out) = GST_CLOCK_TIME_NONE;
 
       GST_LOG_OBJECT (rmdemux, "pushing timestamp %" GST_TIME_FORMAT,
           GST_TIME_ARGS (timestamp));
@@ -2461,7 +2400,7 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
       }
 
       ret = gst_pad_push (stream->pad, out);
-      ret = gst_rmdemux_combine_flows (rmdemux, stream, ret);
+      ret = gst_flow_combiner_update_flow (rmdemux->flowcombiner, ret);
       if (ret != GST_FLOW_OK)
         break;
 
@@ -2473,7 +2412,7 @@ gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
   GST_DEBUG_OBJECT (rmdemux, "%" G_GSIZE_FORMAT " bytes left", size);
 
 done:
-  gst_buffer_unmap (in, base, -1);
+  gst_buffer_unmap (in, &map);
   gst_buffer_unref (in);
 
   return ret;
@@ -2504,6 +2443,9 @@ gst_rmdemux_parse_audio_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
   GstFlowReturn ret;
   GstBuffer *buffer;
 
+  if (gst_buffer_get_size (in) < offset)
+    goto not_enough_data;
+
   buffer = gst_buffer_copy_region (in, GST_BUFFER_COPY_MEMORY, offset, -1);
 
   if (rmdemux->first_ts != -1 && timestamp > rmdemux->first_ts)
@@ -2514,7 +2456,8 @@ gst_rmdemux_parse_audio_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
   if (rmdemux->base_ts != -1)
     timestamp += rmdemux->base_ts;
 
-  GST_BUFFER_TIMESTAMP (buffer) = timestamp;
+  GST_BUFFER_PTS (buffer) = timestamp;
+  GST_BUFFER_DTS (buffer) = timestamp;
 
   if (stream->needs_descrambling) {
     GST_LOG_OBJECT (rmdemux, "descramble timestamp %" GST_TIME_FORMAT,
@@ -2533,9 +2476,19 @@ gst_rmdemux_parse_audio_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream,
     ret = gst_pad_push (stream->pad, buffer);
   }
 
+done:
   gst_buffer_unref (in);
 
   return ret;
+
+  /* ERRORS */
+not_enough_data:
+  {
+    GST_ELEMENT_WARNING (rmdemux, STREAM, DECODE, ("Skipping bad packet."),
+        (NULL));
+    ret = GST_FLOW_OK;
+    goto done;
+  }
 }
 
 static GstFlowReturn
@@ -2547,11 +2500,17 @@ gst_rmdemux_parse_packet (GstRMDemux * rmdemux, GstBuffer * in, guint16 version)
   GstFlowReturn cret, ret;
   GstClockTime timestamp;
   gboolean key;
-  guint8 *data, *base;
+  GstMapInfo map;
+  guint8 *data;
   guint8 flags;
   guint32 ts;
 
-  base = data = gst_buffer_map (in, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (in, &map, GST_MAP_READ);
+  data = map.data;
+  size = map.size;
+
+  if (size < 4 + 6 + 1 + 2)
+    goto not_enough_data;
 
   /* stream number */
   id = RMDEMUX_GUINT16_GET (data);
@@ -2588,11 +2547,14 @@ gst_rmdemux_parse_packet (GstRMDemux * rmdemux, GstBuffer * in, guint16 version)
 
   /* version 1 has an extra byte */
   if (version == 1) {
+    if (size < 1)
+      goto not_enough_data;
+
     data += 1;
     size -= 1;
   }
-  offset = data - base;
-  gst_buffer_unmap (in, base, -1);
+  offset = data - map.data;
+  gst_buffer_unmap (in, &map);
 
   key = (flags & 0x02) != 0;
   GST_DEBUG_OBJECT (rmdemux, "flags %d, Keyframe %d", flags, key);
@@ -2644,7 +2606,8 @@ gst_rmdemux_parse_packet (GstRMDemux * rmdemux, GstBuffer * in, guint16 version)
     ret = GST_FLOW_OK;
   }
 
-  cret = gst_rmdemux_combine_flows (rmdemux, stream, ret);
+  cret = gst_flow_combiner_update_pad_flow (rmdemux->flowcombiner, stream->pad,
+      ret);
 
 beach:
   return cret;
@@ -2654,7 +2617,17 @@ unknown_stream:
   {
     GST_WARNING_OBJECT (rmdemux, "No stream for stream id %d in parsing "
         "data packet", id);
-    gst_buffer_unmap (in, base, -1);
+    gst_buffer_unmap (in, &map);
+    gst_buffer_unref (in);
+    return GST_FLOW_OK;
+  }
+
+  /* ERRORS */
+not_enough_data:
+  {
+    GST_ELEMENT_WARNING (rmdemux, STREAM, DECODE, ("Skipping bad packet."),
+        (NULL));
+    gst_buffer_unmap (in, &map);
     gst_buffer_unref (in);
     return GST_FLOW_OK;
   }