From 92a83e016aa0824549a8e913f8bfa8ee105b0a44 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 14 Jan 2010 17:11:13 -0300 Subject: [PATCH] qtdemux: Handle another kind of redirect trak Some traks might contain a redirect rtsp uri inside hndl atom (which is a dref atom entry). This commit makes qtdemux post a message when it finds one of these traks and there are no other traks. Fixes #597497 --- gst/qtdemux/qtdemux.c | 114 ++++++++++++++++++++++++++++++++++- gst/qtdemux/qtdemux_fourcc.h | 1 + 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index da86c72848..469145cdb1 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -192,6 +192,9 @@ struct _QtDemuxStream GstCaps *caps; guint32 fourcc; + /* if the stream has a redirect URI in its headers, we store it here */ + gchar *redirect_uri; + /* duration/scale */ guint64 duration; /* in timescale */ guint32 timescale; @@ -1679,6 +1682,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) g_free (stream->segments); if (stream->pending_tags) gst_tag_list_free (stream->pending_tags); + g_free (stream->redirect_uri); /* free stbl sub-atoms */ gst_qtdemux_stbl_free (stream); g_free (stream); @@ -4854,6 +4858,98 @@ end: } } +static gchar * +qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf) +{ + GNode *hndl; + GNode *dinf; + GstByteReader dref; + gchar *uri = NULL; + + /* + * Get 'dinf', to get its child 'dref', that might contain a 'hndl' + * atom that might contain a 'data' atom with the rtsp uri. + * This case was reported in bug #597497, some info about + * the hndl atom can be found in TN1195 + */ + dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf); + GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak"); + + if (dinf) { + guint32 dref_num_entries; + if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) && + gst_byte_reader_skip (&dref, 4) && + gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) { + gint i; + hndl = NULL; + + /* search dref entries for hndl atom */ + for (i = 0; i < dref_num_entries; i++) { + guint32 size, type; + guint8 string_len; + if (gst_byte_reader_get_uint32_be (&dref, &size) && + qt_atom_parser_get_fourcc (&dref, &type)) { + if (type == FOURCC_hndl) { + GST_DEBUG_OBJECT (qtdemux, "Found hndl atom"); + + /* skip data reference handle bytes and the + * following pascal string and some extra 4 + * bytes I have no idea what are */ + if (!gst_byte_reader_skip (&dref, 4) || + !gst_byte_reader_get_uint8 (&dref, &string_len) || + !gst_byte_reader_skip (&dref, string_len + 4)) { + GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom"); + break; + } + + /* iterate over the atoms to find the data atom */ + while (gst_byte_reader_get_remaining (&dref) >= 8) { + guint32 atom_size; + guint32 atom_type; + + if (gst_byte_reader_get_uint32_be (&dref, &atom_size) && + qt_atom_parser_get_fourcc (&dref, &atom_type)) { + if (atom_type == FOURCC_data) { + const guint8 *uri_aux = NULL; + + /* found the data atom that might contain the rtsp uri */ + GST_DEBUG_OBJECT (qtdemux, "Found data atom inside " + "hndl atom, interpreting it as an URI"); + if (gst_byte_reader_peek_data (&dref, atom_size - 8, + &uri_aux)) { + if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL) + uri = g_strndup ((gchar *) uri_aux, atom_size - 8); + else + GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom " + "didn't contain a rtsp address"); + } else { + GST_WARNING_OBJECT (qtdemux, "Failed to get the data " + "atom contents"); + } + break; + } + /* skipping to the next entry */ + gst_byte_reader_skip (&dref, atom_size - 8); + } else { + GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child " + "atom header"); + break; + } + } + break; + } + /* skip to the next entry */ + gst_byte_reader_skip (&dref, size - 8); + } else { + GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom"); + } + } + GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom"); + } + } + return uri; +} + /* parse the traks. * With each track we associate a new QtDemuxStream that contains all the info * about the trak. @@ -5659,7 +5755,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) GST_FOURCC_ARGS (fourcc), stream->caps); } else if (stream->subtype == FOURCC_strm) { - if (fourcc != FOURCC_rtsp) { + if (fourcc == FOURCC_rtsp) { + stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf); + } else { GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); goto unknown_stream; @@ -6730,6 +6828,20 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) gst_message_new_tag (GST_OBJECT (qtdemux), gst_tag_list_copy (qtdemux->tag_list))); + /* check if we should post a redirect in case there is a single trak + * and it is a redirecting trak */ + if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) { + GstMessage *m; + GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with " + "a external content"); + m = gst_message_new_element (GST_OBJECT_CAST (qtdemux), + gst_structure_new ("redirect", + "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri, + NULL)); + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); + qtdemux->posted_redirect = TRUE; + } + return TRUE; } diff --git a/gst/qtdemux/qtdemux_fourcc.h b/gst/qtdemux/qtdemux_fourcc.h index 3528383ea0..57a2e63936 100644 --- a/gst/qtdemux/qtdemux_fourcc.h +++ b/gst/qtdemux/qtdemux_fourcc.h @@ -46,6 +46,7 @@ G_BEGIN_DECLS #define FOURCC_mdia GST_MAKE_FOURCC('m','d','i','a') #define FOURCC_mdhd GST_MAKE_FOURCC('m','d','h','d') #define FOURCC_hdlr GST_MAKE_FOURCC('h','d','l','r') +#define FOURCC_hndl GST_MAKE_FOURCC('h','n','d','l') #define FOURCC_minf GST_MAKE_FOURCC('m','i','n','f') #define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d') #define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d')