*
* 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
int id;
GstPad *pad;
- GstFlowReturn last_flow;
gboolean discont;
int timescale;
{
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_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",
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));
}
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 ();
}
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 "-%"
}
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;
}
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;
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;
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;
}
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
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)
switch (mode) {
case GST_PAD_MODE_PUSH:
demux->seekable = FALSE;
+ demux->running = active;
res = TRUE;
break;
case GST_PAD_MODE_PULL:
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;
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));
}
stream->next_ts = -1;
stream->last_seq = -1;
stream->next_seq = -1;
- stream->last_flow = GST_FLOW_OK;
break;
default:
break;
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) {
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;
}
} 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;
}
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));
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,
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);
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:
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);
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
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;
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);
g_ptr_array_set_size (stream->subpackets, 0);
gst_buffer_map (buf, &map, GST_MAP_READ);
- timestamp = GST_BUFFER_TIMESTAMP (buf);
+ timestamp = GST_BUFFER_PTS (buf);
frames = (map.data[1] & 0xf0) >> 4;
index = 2 * frames + 2;
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;
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, outmap.data + packet_size * p, packet_size);
}
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);
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) \
gst_buffer_map (in, &map, GST_MAP_READ);
+ 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;
}
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 - map.data,
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_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));
}
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;
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)
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,
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
data = map.data;
size = map.size;
+ if (size < 4 + 6 + 1 + 2)
+ goto not_enough_data;
+
/* stream number */
id = RMDEMUX_GUINT16_GET (data);
/* version 1 has an extra byte */
if (version == 1) {
+ if (size < 1)
+ goto not_enough_data;
+
data += 1;
size -= 1;
}
if (rmdemux->pending_tags != NULL) {
gst_rmdemux_send_event (rmdemux,
- gst_event_new_tag ("GstDemuxer", rmdemux->pending_tags));
+ gst_event_new_tag (rmdemux->pending_tags));
rmdemux->pending_tags = NULL;
}
}
if (stream->pending_tags != NULL) {
GST_LOG_OBJECT (stream->pad, "tags %" GST_PTR_FORMAT, stream->pending_tags);
- gst_pad_push_event (stream->pad, gst_event_new_tag ("GstDemuxer",
- stream->pending_tags));
+ gst_pad_push_event (stream->pad, gst_event_new_tag (stream->pending_tags));
stream->pending_tags = NULL;
}
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;
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;
+ }
}
gboolean