qtdemux: prepare qtdemux to accept multiple dash moovs in a row

Whenever dashdemux switches bitrates it sends a new moov with the
new stream configuration. qtdemux should now handle this by splitting
the exposing and configuration of streams into separate functions. When
the stream is new it is configured and exposed, when it is a new bitrate
of an existing stream it is only reconfigured.

Conflicts:
	gst/isomp4/qtdemux.c
This commit is contained in:
Thiago Santos 2013-02-08 00:29:20 -03:00
parent 2a7d3d1598
commit 6c69e59677

View file

@ -416,6 +416,8 @@ static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent, static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event); GstEvent * event);
static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps); static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream);
static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
const guint8 * buffer, guint length); const guint8 * buffer, guint length);
@ -443,6 +445,7 @@ static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
QtDemuxStream * stream); QtDemuxStream * stream);
static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
static void static void
gst_qtdemux_class_init (GstQTDemuxClass * klass) gst_qtdemux_class_init (GstQTDemuxClass * klass)
@ -3008,6 +3011,7 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
beach: beach:
if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) { if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
/* digested all data, show what we have */ /* digested all data, show what we have */
qtdemux_prepare_streams (qtdemux);
ret = qtdemux_expose_streams (qtdemux); ret = qtdemux_expose_streams (qtdemux);
qtdemux->state = QTDEMUX_STATE_MOVIE; qtdemux->state = QTDEMUX_STATE_MOVIE;
@ -4345,8 +4349,9 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
gst_adapter_push (demux->adapter, inbuf); gst_adapter_push (demux->adapter, inbuf);
/* we never really mean to buffer that much */ /* we never really mean to buffer that much */
if (demux->neededbytes == -1) if (demux->neededbytes == -1) {
goto eos; goto eos;
}
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux,
"pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf, "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
@ -4479,7 +4484,10 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
} else { } else {
GST_DEBUG_OBJECT (demux, "Parsing [moov]"); GST_DEBUG_OBJECT (demux, "Parsing [moov]");
demux->got_moov = TRUE; if (demux->got_moov && demux->fragmented) {
GST_DEBUG_OBJECT (demux,
"Got a second moov, clean up data from old one");
}
/* prepare newsegment to send when streaming actually starts */ /* prepare newsegment to send when streaming actually starts */
if (!demux->pending_newsegment) if (!demux->pending_newsegment)
@ -4489,7 +4497,20 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
qtdemux_parse_moov (demux, data, demux->neededbytes); qtdemux_parse_moov (demux, data, demux->neededbytes);
qtdemux_node_dump (demux, demux->moov_node); qtdemux_node_dump (demux, demux->moov_node);
qtdemux_parse_tree (demux); qtdemux_parse_tree (demux);
qtdemux_expose_streams (demux); qtdemux_prepare_streams (demux);
if (!demux->got_moov)
qtdemux_expose_streams (demux);
else {
gint n;
for (n = 0; n < demux->n_streams; n++) {
QtDemuxStream *stream = demux->streams[n];
gst_qtdemux_configure_stream (demux, stream);
}
}
demux->got_moov = TRUE;
g_node_destroy (demux->moov_node); g_node_destroy (demux->moov_node);
demux->moov_node = NULL; demux->moov_node = NULL;
@ -5355,19 +5376,9 @@ qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
} }
static gboolean static gboolean
gst_qtdemux_add_stream (GstQTDemux * qtdemux, gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
QtDemuxStream * stream, GstTagList * list)
{ {
/* consistent default for push based mode */
gst_segment_init (&stream->segment, GST_FORMAT_TIME);
if (stream->subtype == FOURCC_vide) { if (stream->subtype == FOURCC_vide) {
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);
/* fps is calculated base on the duration of the first frames since /* fps is calculated base on the duration of the first frames since
* qt does not have a fixed framerate. */ * qt does not have a fixed framerate. */
if ((stream->n_samples == 1) && (stream->min_duration == 0)) { if ((stream->n_samples == 1) && (stream->min_duration == 0)) {
@ -5478,18 +5489,11 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
stream->pad = NULL; stream->pad = NULL;
} }
} }
qtdemux->n_video_streams++;
} else if (stream->subtype == FOURCC_soun) { } else if (stream->subtype == FOURCC_soun) {
gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
stream->pad =
gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
g_free (name);
if (stream->caps) { if (stream->caps) {
gst_caps_set_simple (stream->caps, gst_caps_set_simple (stream->caps,
"rate", G_TYPE_INT, (int) stream->rate, "rate", G_TYPE_INT, (int) stream->rate,
"channels", G_TYPE_INT, stream->n_channels, NULL); "channels", G_TYPE_INT, stream->n_channels, NULL);
if (stream->n_channels > 2) { if (stream->n_channels > 2) {
/* FIXME: Need to parse the 'chan' atom to get channel layouts /* FIXME: Need to parse the 'chan' atom to get channel layouts
* correctly; this is just the minimum we can do - assume * correctly; this is just the minimum we can do - assume
@ -5498,6 +5502,53 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
"channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL); "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
} }
} }
}
if (stream->pad) {
gchar *stream_id;
GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
gst_pad_set_active (stream->pad, TRUE);
gst_pad_use_fixed_caps (stream->pad);
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), "%03u", 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);
}
return TRUE;
}
static gboolean
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstTagList * list)
{
/* consistent default for push based mode */
gst_segment_init (&stream->segment, GST_FORMAT_TIME);
if (stream->subtype == FOURCC_vide) {
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);
gst_qtdemux_configure_stream (qtdemux, stream);
qtdemux->n_video_streams++;
} else if (stream->subtype == FOURCC_soun) {
gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
stream->pad =
gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
g_free (name);
gst_qtdemux_configure_stream (qtdemux, stream);
qtdemux->n_audio_streams++; qtdemux->n_audio_streams++;
} else if (stream->subtype == FOURCC_strm) { } else if (stream->subtype == FOURCC_strm) {
GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad"); GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
@ -5508,6 +5559,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
stream->pad = stream->pad =
gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name); gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
g_free (name); g_free (name);
gst_qtdemux_configure_stream (qtdemux, stream);
qtdemux->n_sub_streams++; qtdemux->n_sub_streams++;
} else if (stream->caps) { } else if (stream->caps) {
gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams); gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
@ -5522,23 +5574,6 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
} }
if (stream->pad) { if (stream->pad) {
gchar *stream_id;
GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
gst_pad_use_fixed_caps (stream->pad);
gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
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), "%03u", 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", GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux); GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
@ -5694,6 +5729,7 @@ static gboolean
qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
{ {
stream->stbl_index = -1; /* no samples have yet been parsed */ stream->stbl_index = -1; /* no samples have yet been parsed */
stream->sample_index = -1;
/* time-to-sample atom */ /* time-to-sample atom */
if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts)) if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
@ -6714,7 +6750,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
GNode *wave; GNode *wave;
GNode *esds; GNode *esds;
GNode *pasp; GNode *pasp;
QtDemuxStream *stream; QtDemuxStream *stream = NULL;
GstTagList *list = NULL; GstTagList *list = NULL;
gchar *codec = NULL; gchar *codec = NULL;
const guint8 *stsd_data; const guint8 *stsd_data;
@ -6724,8 +6760,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
guint8 tkhd_version = 0; guint8 tkhd_version = 0;
guint32 fourcc; guint32 fourcc;
guint value_size, len; guint value_size, len;
guint32 track_id;
stream = _create_stream (); GST_DEBUG_OBJECT (qtdemux, "parse_trak");
if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd) if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
|| !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version) || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
@ -6738,11 +6775,20 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
/* pick between 64 or 32 bits */ /* pick between 64 or 32 bits */
value_size = tkhd_version == 1 ? 8 : 4; value_size = tkhd_version == 1 ? 8 : 4;
if (!gst_byte_reader_skip (&tkhd, value_size * 2) || if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
!gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id)) !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
goto corrupt_file; goto corrupt_file;
if (qtdemux_find_stream (qtdemux, stream->track_id)) if (!qtdemux->got_moov) {
goto existing_stream; if (!qtdemux->got_moov && qtdemux_find_stream (qtdemux, track_id))
goto existing_stream;
stream = _create_stream ();
stream->track_id = track_id;
} else {
stream = qtdemux_find_stream (qtdemux, track_id);
if (!stream) {
GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
}
}
GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u", GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
tkhd_version, tkhd_flags, stream->track_id); tkhd_version, tkhd_flags, stream->track_id);
@ -7904,10 +7950,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS) if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
goto too_many_streams; goto too_many_streams;
stream->pending_tags = list; if (!qtdemux->got_moov) {
qtdemux->streams[qtdemux->n_streams] = stream; stream->pending_tags = list;
qtdemux->n_streams++; qtdemux->streams[qtdemux->n_streams] = stream;
GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); qtdemux->n_streams++;
GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
}
return TRUE; return TRUE;
@ -7943,8 +7991,9 @@ segments_failed:
existing_stream: existing_stream:
{ {
GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists", GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
stream->track_id); track_id);
g_free (stream); if (stream)
g_free (stream);
return TRUE; return TRUE;
} }
unknown_stream: unknown_stream:
@ -8059,22 +8108,18 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
} }
static GstFlowReturn static GstFlowReturn
qtdemux_expose_streams (GstQTDemux * qtdemux) qtdemux_prepare_streams (GstQTDemux * qtdemux)
{ {
gint i; gint i;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GSList *oldpads = NULL;
GSList *iter;
GST_DEBUG_OBJECT (qtdemux, "exposing streams"); GST_DEBUG_OBJECT (qtdemux, "prepare streams");
for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) { for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
QtDemuxStream *stream = qtdemux->streams[i]; QtDemuxStream *stream = qtdemux->streams[i];
GstPad *oldpad = stream->pad;
guint32 sample_num = 0; guint32 sample_num = 0;
guint samples = 20; guint samples = 20;
GArray *durations; GArray *durations;
GstTagList *list;
GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
@ -8129,6 +8174,28 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
stream->min_duration = g_array_index (durations, guint32, samples / 2); stream->min_duration = g_array_index (durations, guint32, samples / 2);
g_array_free (durations, TRUE); g_array_free (durations, TRUE);
} }
}
return ret;
}
static GstFlowReturn
qtdemux_expose_streams (GstQTDemux * qtdemux)
{
gint i;
GstFlowReturn ret = GST_FLOW_OK;
GSList *oldpads = NULL;
GSList *iter;
GST_DEBUG_OBJECT (qtdemux, "exposing streams");
for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
QtDemuxStream *stream = qtdemux->streams[i];
GstPad *oldpad = stream->pad;
GstTagList *list;
GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
/* now we have all info and can expose */ /* now we have all info and can expose */
list = stream->pending_tags; list = stream->pending_tags;