asfdemux: add support for chained asfs (push mode)

Adds support for detecting and playing chained asfs
in push mode. asfdemux tries to detect a new asf start
by identifying the header object guid in a input buffer.
When it finds it, it resets its state, removing its pads
and creates new ones for the new file.
This commit is contained in:
Thiago Santos 2009-10-27 17:48:03 -03:00
parent 9e3e475f36
commit 37e805ef24
2 changed files with 93 additions and 18 deletions

View file

@ -169,13 +169,13 @@ gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream)
}
static void
gst_asf_demux_reset (GstASFDemux * demux)
gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset)
{
GST_LOG_OBJECT (demux, "resetting");
gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
demux->segment_running = FALSE;
if (demux->adapter) {
if (demux->adapter && !chain_reset) {
gst_adapter_clear (demux->adapter);
g_object_unref (demux->adapter);
demux->adapter = NULL;
@ -215,7 +215,8 @@ gst_asf_demux_reset (GstASFDemux * demux)
demux->first_ts = GST_CLOCK_TIME_NONE;
demux->segment_ts = GST_CLOCK_TIME_NONE;
demux->in_gap = 0;
gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
if (!chain_reset)
gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
demux->state = GST_ASF_DEMUX_STATE_HEADER;
demux->seekable = FALSE;
demux->broadcast = FALSE;
@ -225,6 +226,19 @@ gst_asf_demux_reset (GstASFDemux * demux)
demux->sidx_entries = NULL;
demux->speed_packets = 1;
if (chain_reset) {
GST_LOG_OBJECT (demux, "Restarting");
gst_segment_init (&demux->segment, GST_FORMAT_TIME);
demux->need_newsegment = TRUE;
demux->segment_running = FALSE;
demux->accurate = FALSE;
demux->metadata = gst_caps_new_empty ();
demux->global_metadata = gst_structure_empty_new ("metadata");
demux->data_size = 0;
demux->data_offset = 0;
demux->index_offset = 0;
}
}
static void
@ -245,7 +259,7 @@ gst_asf_demux_init (GstASFDemux * demux, GstASFDemuxClass * klass)
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
/* set initial state */
gst_asf_demux_reset (demux);
gst_asf_demux_reset (demux, FALSE);
}
static gboolean
@ -720,9 +734,14 @@ typedef struct
guint64 size;
} AsfObject;
/* expect is true when the user is expeting an object,
* when false, it will give no warnings if the object
* is not identified
*/
static gboolean
asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
guint data_len, AsfObject * object)
guint data_len, AsfObject * object, gboolean expect)
{
ASFGuid guid;
@ -738,7 +757,7 @@ asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
/* FIXME: make asf_demux_identify_object_guid() */
object->id = gst_asf_demux_identify_guid (asf_object_guids, &guid);
if (object->id == ASF_OBJ_UNDEFINED) {
if (object->id == ASF_OBJ_UNDEFINED && expect) {
GST_WARNING_OBJECT (demux, "Unknown object %08x-%08x-%08x-%08x",
guid.v1, guid.v2, guid.v3, guid.v4);
}
@ -759,7 +778,7 @@ gst_asf_demux_chain_headers (GstASFDemux * demux)
if (cdata == NULL)
goto need_more_data;
asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj);
asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, TRUE);
if (obj.id != ASF_OBJ_HEADER)
goto wrong_type;
@ -890,7 +909,7 @@ gst_asf_demux_pull_indices (GstASFDemux * demux)
GstFlowReturn flow;
AsfObject obj;
asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj);
asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj, TRUE);
gst_buffer_replace (&buf, NULL);
/* check for sanity */
@ -924,7 +943,7 @@ gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data)
{
AsfObject obj;
asf_demux_peek_object (demux, data, 50, &obj);
asf_demux_peek_object (demux, data, 50, &obj, TRUE);
if (obj.id != ASF_OBJ_DATA) {
GST_WARNING_OBJECT (demux, "headers not followed by a DATA object");
return FALSE;
@ -984,7 +1003,7 @@ gst_asf_demux_pull_headers (GstASFDemux * demux)
if (!gst_asf_demux_pull_data (demux, 0, 16 + 8, &buf, NULL))
goto read_failed;
asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj);
asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj, TRUE);
gst_buffer_replace (&buf, NULL);
if (obj.id != ASF_OBJ_HEADER)
@ -1509,6 +1528,27 @@ parse_error:
}
}
#define GST_ASF_DEMUX_CHECK_HEADER_YES 0
#define GST_ASF_DEMUX_CHECK_HEADER_NO 1
#define GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA 2
static gint
gst_asf_demux_check_header (GstASFDemux * demux)
{
AsfObject obj;
guint8 *cdata = (guint8 *) gst_adapter_peek (demux->adapter,
ASF_OBJECT_HEADER_SIZE);
if (cdata == NULL) /* need more data */
return GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA;
asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, FALSE);
if (obj.id != ASF_OBJ_HEADER) {
return GST_ASF_DEMUX_CHECK_HEADER_NO;
} else {
return GST_ASF_DEMUX_CHECK_HEADER_YES;
}
}
static GstFlowReturn
gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
{
@ -1537,6 +1577,24 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
gst_adapter_push (demux->adapter, buf);
switch (demux->state) {
case GST_ASF_DEMUX_STATE_INDEX:{
gint result = gst_asf_demux_check_header (demux);
if (result == GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA) /* need more data */
break;
if (result == GST_ASF_DEMUX_CHECK_HEADER_NO) {
/* we don't care about this, probably an index */
/* TODO maybe would be smarter to skip all the indices
* until we got a new header or EOS to decide */
GST_LOG_OBJECT (demux, "Received index object, its EOS");
goto eos;
} else {
GST_INFO_OBJECT (demux, "Chained asf starting");
/* cleanup and get ready for a chained asf */
gst_asf_demux_reset (demux, TRUE);
/* fall through */
}
}
case GST_ASF_DEMUX_STATE_HEADER:{
ret = gst_asf_demux_chain_headers (demux);
if (demux->state != GST_ASF_DEMUX_STATE_DATA)
@ -1552,10 +1610,22 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
while (gst_adapter_available (demux->adapter) >= data_size) {
GstBuffer *buf;
/* do not overshoot data section when streaming */
if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0
&& demux->packet >= demux->num_packets))
goto eos;
/* we don't know the length of the stream
* check for a chained asf everytime */
if (demux->num_packets == 0) {
gint result = gst_asf_demux_check_header (demux);
if (result == GST_ASF_DEMUX_CHECK_HEADER_YES) {
GST_INFO_OBJECT (demux, "Chained asf starting");
/* cleanup and get ready for a chained asf */
gst_asf_demux_reset (demux, TRUE);
break;
}
} else if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0
&& demux->packet >= demux->num_packets)) {
/* do not overshoot data section when streaming */
break;
}
buf = gst_adapter_take_buffer (demux->adapter, data_size);
@ -1572,6 +1642,10 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
if (demux->packet >= 0)
++demux->packet;
}
if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0
&& demux->packet >= demux->num_packets)) {
demux->state = GST_ASF_DEMUX_STATE_INDEX;
}
break;
}
default:
@ -3130,7 +3204,7 @@ gst_asf_demux_process_ext_stream_props (GstASFDemux * demux, guint8 * data,
}
/* get size of the stream object */
if (!asf_demux_peek_object (demux, data, size, &stream_obj))
if (!asf_demux_peek_object (demux, data, size, &stream_obj, TRUE))
goto not_enough_data;
if (stream_obj.id != ASF_OBJ_STREAM)
@ -3341,7 +3415,7 @@ gst_asf_demux_process_object (GstASFDemux * demux, guint8 ** p_data,
if (*p_size < ASF_OBJECT_HEADER_SIZE)
return ASF_FLOW_NEED_MORE_DATA;
asf_demux_peek_object (demux, *p_data, ASF_OBJECT_HEADER_SIZE, &obj);
asf_demux_peek_object (demux, *p_data, ASF_OBJECT_HEADER_SIZE, &obj, TRUE);
gst_asf_demux_skip_bytes (ASF_OBJECT_HEADER_SIZE, p_data, p_size);
obj_data_size = obj.size - ASF_OBJECT_HEADER_SIZE;
@ -3738,7 +3812,7 @@ gst_asf_demux_change_state (GstElement * element, GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
case GST_STATE_CHANGE_READY_TO_NULL:
gst_asf_demux_reset (demux);
gst_asf_demux_reset (demux, FALSE);
break;
default:
break;

View file

@ -115,7 +115,8 @@ typedef struct
typedef enum {
GST_ASF_DEMUX_STATE_HEADER,
GST_ASF_DEMUX_STATE_DATA
GST_ASF_DEMUX_STATE_DATA,
GST_ASF_DEMUX_STATE_INDEX
} GstAsfDemuxState;
#define GST_ASF_DEMUX_NUM_VIDEO_PADS 16