diff options
Diffstat (limited to 'ext/smoothstreaming')
-rw-r--r-- | ext/smoothstreaming/gstmssdemux.c | 126 | ||||
-rw-r--r-- | ext/smoothstreaming/gstmssmanifest.c | 280 | ||||
-rw-r--r-- | ext/smoothstreaming/gstmssmanifest.h | 4 |
3 files changed, 211 insertions, 199 deletions
diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c index 24aa031bf..09e375099 100644 --- a/ext/smoothstreaming/gstmssdemux.c +++ b/ext/smoothstreaming/gstmssdemux.c @@ -126,6 +126,8 @@ static GstClockTime gst_mss_demux_get_duration (GstAdaptiveDemux * demux); static void gst_mss_demux_reset (GstAdaptiveDemux * demux); static GstFlowReturn gst_mss_demux_stream_seek (GstAdaptiveDemuxStream * stream, GstClockTime ts); +static gboolean +gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream * stream); static GstFlowReturn gst_mss_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream); static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * @@ -133,6 +135,10 @@ static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * static GstFlowReturn gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream); static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek); +static gint64 +gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux); +static GstFlowReturn +gst_mss_demux_update_manifest (GstAdaptiveDemux * demux, GstBuffer * buffer); static void gst_mss_demux_class_init (GstMssDemuxClass * klass) @@ -185,15 +191,20 @@ gst_mss_demux_class_init (GstMssDemuxClass * klass) gstadaptivedemux_class->process_manifest = gst_mss_demux_process_manifest; gstadaptivedemux_class->is_live = gst_mss_demux_is_live; gstadaptivedemux_class->get_duration = gst_mss_demux_get_duration; + gstadaptivedemux_class->get_manifest_update_interval = + gst_mss_demux_get_manifest_update_interval; gstadaptivedemux_class->reset = gst_mss_demux_reset; gstadaptivedemux_class->seek = gst_mss_demux_seek; gstadaptivedemux_class->stream_seek = gst_mss_demux_stream_seek; gstadaptivedemux_class->stream_advance_fragment = gst_mss_demux_stream_advance_fragment; + gstadaptivedemux_class->stream_has_next_fragment = + gst_mss_demux_stream_has_next_fragment; gstadaptivedemux_class->stream_select_bitrate = gst_mss_demux_stream_select_bitrate; gstadaptivedemux_class->stream_update_fragment_info = gst_mss_demux_stream_update_fragment_info; + gstadaptivedemux_class->update_manifest = gst_mss_demux_update_manifest; GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin"); } @@ -447,20 +458,13 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux) return TRUE; } -static gboolean -gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) +static void +gst_mss_demux_update_base_url (GstMssDemux * mssdemux) { - GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux); + GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (mssdemux); gchar *baseurl_end; -#if 0 - if (mssdemux->base_url == NULL) { - GST_ELEMENT_ERROR (mssdemux, RESOURCE, NOT_FOUND, - (_("Couldn't get the Manifest's URI")), - ("need to get the manifest's URI from upstream elements")); - return FALSE; - } -#endif + g_free (mssdemux->base_url); mssdemux->base_url = g_strdup (demux->manifest_base_uri ? demux->manifest_base_uri : demux-> @@ -477,6 +481,15 @@ gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) GST_WARNING_OBJECT (mssdemux, "Stream's URI didn't end with /manifest"); } +} + +static gboolean +gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) +{ + GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux); + + gst_mss_demux_update_base_url (mssdemux); + mssdemux->manifest = gst_mss_manifest_new (buf); if (!mssdemux->manifest) { GST_ELEMENT_ERROR (mssdemux, STREAM, FORMAT, ("Bad manifest file"), @@ -486,57 +499,6 @@ gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) return gst_mss_demux_setup_streams (demux); } -#if 0 -static void -gst_mss_demux_reload_manifest (GstMssDemux * mssdemux) -{ - GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (mssdemux); - GstUriDownloader *downloader; - GstFragment *manifest_data; - GstBuffer *manifest_buffer; - gchar *baseurl_end; - - downloader = gst_uri_downloader_new (); - - manifest_data = - gst_uri_downloader_fetch_uri (downloader, demux->manifest_uri, NULL, - TRUE, TRUE, TRUE, NULL); - - /* FIXME not really nice to unref parent's data */ - g_free (demux->manifest_uri); - g_free (demux->manifest_base_uri); - demux->manifest_uri = g_strdup ((manifest_data->redirect_permanent - && manifest_data->redirect_uri) ? manifest_data-> - redirect_uri : manifest_data->uri); - demux->manifest_base_uri = - g_strdup (manifest_data->redirect_uri ? manifest_data-> - redirect_uri : manifest_data->uri); - baseurl_end = g_strrstr (demux->manifest_base_uri, "/Manifest"); - if (baseurl_end == NULL) { - /* second try */ - baseurl_end = g_strrstr (demux->manifest_base_uri, "/manifest"); - } - - if (baseurl_end) { - /* set the new end of the string */ - baseurl_end[0] = '\0'; - } else { - GST_WARNING_OBJECT (mssdemux, "Stream's URI didn't end with /manifest"); - } - - manifest_buffer = gst_fragment_get_buffer (manifest_data); - g_object_unref (manifest_data); - - gst_mss_manifest_reload_fragments (mssdemux->manifest, manifest_buffer); -#if 0 - gst_buffer_replace (&mssdemux->manifest_buffer, manifest_buffer); -#endif - gst_buffer_unref (manifest_buffer); - - g_object_unref (downloader); -} -#endif - static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream, guint64 bitrate) @@ -597,3 +559,43 @@ gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek) return TRUE; } + +static gboolean +gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream * stream) +{ + GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream; + + return gst_mss_stream_has_next_fragment (mssstream->manifest_stream); +} + +static gint64 +gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux) +{ + GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux); + GstClockTime interval; + + /* Not much information about this in the MSS spec. It seems that + * the fragments contain an UUID box that should tell the next + * fragments time and duration so one wouldn't need to fetch + * the Manifest again, but we need a fallback here. So use 2 times + * the current fragment duration */ + + interval = gst_mss_manifest_get_min_fragment_duration (mssdemux->manifest); + if (!GST_CLOCK_TIME_IS_VALID (interval)) + interval = 2 * GST_SECOND; /* default to 2 seconds */ + + interval = 2 * (interval / GST_USECOND); + + return interval; +} + +static GstFlowReturn +gst_mss_demux_update_manifest (GstAdaptiveDemux * demux, GstBuffer * buffer) +{ + GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux); + + gst_mss_demux_update_base_url (mssdemux); + + gst_mss_manifest_reload_fragments (mssdemux->manifest, buffer); + return GST_FLOW_OK; +} diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c index 26958ebb1..88c700054 100644 --- a/ext/smoothstreaming/gstmssmanifest.c +++ b/ext/smoothstreaming/gstmssmanifest.c @@ -98,6 +98,93 @@ struct _GstMssManifest GSList *streams; }; +/* For parsing and building a fragments list */ +typedef struct _GstMssFragmentListBuilder +{ + GList *fragments; + + GstMssStreamFragment *previous_fragment; + guint fragment_number; + guint64 fragment_time_accum; +} GstMssFragmentListBuilder; + +static void +gst_mss_fragment_list_builder_init (GstMssFragmentListBuilder * builder) +{ + builder->fragments = NULL; + builder->previous_fragment = NULL; + builder->fragment_time_accum = 0; + builder->fragment_number = 0; +} + +static void +gst_mss_fragment_list_builder_add (GstMssFragmentListBuilder * builder, + xmlNodePtr node) +{ + gchar *duration_str; + gchar *time_str; + gchar *seqnum_str; + gchar *repetition_str; + GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1); + + duration_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_DURATION); + time_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_TIME); + seqnum_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_NUMBER); + repetition_str = + (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_REPETITIONS); + + /* use the node's seq number or use the previous + 1 */ + if (seqnum_str) { + fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10); + xmlFree (seqnum_str); + builder->fragment_number = fragment->number; + } else { + fragment->number = builder->fragment_number; + } + builder->fragment_number = fragment->number + 1; + + if (repetition_str) { + fragment->repetitions = g_ascii_strtoull (repetition_str, NULL, 10); + xmlFree (repetition_str); + } else { + fragment->repetitions = 1; + } + + if (time_str) { + fragment->time = g_ascii_strtoull (time_str, NULL, 10); + + xmlFree (time_str); + builder->fragment_time_accum = fragment->time; + } else { + fragment->time = builder->fragment_time_accum; + } + + /* if we have a previous fragment, means we need to set its duration */ + if (builder->previous_fragment) + builder->previous_fragment->duration = + (fragment->time - + builder->previous_fragment->time) / + builder->previous_fragment->repetitions; + + if (duration_str) { + fragment->duration = g_ascii_strtoull (duration_str, NULL, 10); + + builder->previous_fragment = NULL; + builder->fragment_time_accum += fragment->duration * fragment->repetitions; + xmlFree (duration_str); + } else { + /* store to set the duration at the next iteration */ + builder->previous_fragment = fragment; + } + + /* we reverse it later */ + builder->fragments = g_list_prepend (builder->fragments, fragment); + GST_LOG ("Adding fragment number: %u, time: %" G_GUINT64_FORMAT + ", duration: %" G_GUINT64_FORMAT ", repetitions: %u", + fragment->number, fragment->time, fragment->duration, + fragment->repetitions); +} + static GstBuffer *gst_buffer_from_hex_string (const gchar * s); static gboolean @@ -146,9 +233,9 @@ static void _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node) { xmlNodePtr iter; - GstMssStreamFragment *previous_fragment = NULL; - guint fragment_number = 0; - guint64 fragment_time_accum = 0; + GstMssFragmentListBuilder builder; + + gst_mss_fragment_list_builder_init (&builder); stream->xmlnode = node; @@ -158,67 +245,7 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node) for (iter = node->children; iter; iter = iter->next) { if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) { - gchar *duration_str; - gchar *time_str; - gchar *seqnum_str; - gchar *repetition_str; - GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1); - - duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION); - time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME); - seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER); - repetition_str = - (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_REPETITIONS); - - /* use the node's seq number or use the previous + 1 */ - if (seqnum_str) { - fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10); - xmlFree (seqnum_str); - fragment_number = fragment->number; - } else { - fragment->number = fragment_number; - } - fragment_number = fragment->number + 1; - - if (repetition_str) { - fragment->repetitions = g_ascii_strtoull (repetition_str, NULL, 10); - xmlFree (repetition_str); - } else { - fragment->repetitions = 1; - } - - if (time_str) { - fragment->time = g_ascii_strtoull (time_str, NULL, 10); - - xmlFree (time_str); - fragment_time_accum = fragment->time; - } else { - fragment->time = fragment_time_accum; - } - - /* if we have a previous fragment, means we need to set its duration */ - if (previous_fragment) - previous_fragment->duration = - (fragment->time - - previous_fragment->time) / previous_fragment->repetitions; - - if (duration_str) { - fragment->duration = g_ascii_strtoull (duration_str, NULL, 10); - - previous_fragment = NULL; - fragment_time_accum += fragment->duration * fragment->repetitions; - xmlFree (duration_str); - } else { - /* store to set the duration at the next iteration */ - previous_fragment = fragment; - } - - /* we reverse it later */ - stream->fragments = g_list_prepend (stream->fragments, fragment); - GST_LOG ("Adding fragment number: %u, time: %" G_GUINT64_FORMAT - ", duration: %" G_GUINT64_FORMAT ", repetitions: %u", - fragment->number, fragment->time, fragment->duration, - fragment->repetitions); + gst_mss_fragment_list_builder_add (&builder, iter); } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) { GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter); stream->qualities = g_list_prepend (stream->qualities, quality); @@ -227,7 +254,7 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node) } } - stream->fragments = g_list_reverse (stream->fragments); + stream->fragments = g_list_reverse (builder.fragments); /* order them from smaller to bigger based on bitrates */ stream->qualities = @@ -797,6 +824,29 @@ gst_mss_manifest_get_gst_duration (GstMssManifest * manifest) return gstdur; } +GstClockTime +gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest) +{ + GSList *iter; + GstClockTime dur = GST_CLOCK_TIME_NONE; + GstClockTime iter_dur; + + for (iter = manifest->streams; iter; iter = g_slist_next (iter)) { + GstMssStream *stream = iter->data; + + iter_dur = gst_mss_stream_get_fragment_gst_duration (stream); + if (iter_dur != GST_CLOCK_TIME_NONE && iter_dur != 0) { + if (GST_CLOCK_TIME_IS_VALID (dur)) { + dur = MIN (dur, iter_dur); + } else { + dur = iter_dur; + } + } + } + + return dur; +} + GstCaps * gst_mss_stream_get_caps (GstMssStream * stream) { @@ -893,6 +943,17 @@ gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream) timescale); } +gboolean +gst_mss_stream_has_next_fragment (GstMssStream * stream) +{ + g_return_val_if_fail (stream->active, FALSE); + + if (stream->current_fragment == NULL) + return FALSE; + + return TRUE; +} + GstFlowReturn gst_mss_stream_advance_fragment (GstMssStream * stream) { @@ -982,6 +1043,8 @@ gst_mss_stream_seek (GstMssStream * stream, guint64 time) timescale = gst_mss_stream_get_timescale (stream); time = gst_util_uint64_scale_round (time, timescale, GST_SECOND); + GST_DEBUG ("Stream %s seeking to %" G_GUINT64_FORMAT, stream->url, time); + for (iter = stream->fragments; iter; iter = g_list_next (iter)) { GList *next = g_list_next (iter); if (next) { @@ -1008,6 +1071,10 @@ gst_mss_stream_seek (GstMssStream * stream, guint64 time) stream->fragment_repetition_index = (time - fragment->time) / fragment->duration; } + + GST_DEBUG ("Stream %s seeked to fragment time %" G_GUINT64_FORMAT + " repetition %u", stream->url, fragment->time, + stream->fragment_repetition_index); } guint64 @@ -1039,89 +1106,28 @@ static void gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex) { xmlNodePtr iter; - GList *new_fragments = NULL; - GstMssStreamFragment *previous_fragment = NULL; - GstMssStreamFragment *current_fragment = - stream->current_fragment ? stream->current_fragment->data : NULL; - guint64 current_time = gst_mss_stream_get_fragment_gst_timestamp (stream); - guint fragment_number = 0; - guint64 fragment_time_accum = 0; - - if (!current_fragment && stream->fragments) { - current_fragment = g_list_last (stream->fragments)->data; - } else if (g_list_previous (stream->current_fragment)) { - /* rewind one as this is the next to be pushed */ - current_fragment = g_list_previous (stream->current_fragment)->data; - } else { - current_fragment = NULL; - } + guint64 current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream); + GstMssFragmentListBuilder builder; - if (current_fragment) { - current_time = current_fragment->time; - fragment_number = current_fragment->number; - fragment_time_accum = current_fragment->time; - } + gst_mss_fragment_list_builder_init (&builder); + + GST_DEBUG ("Current position: %" GST_TIME_FORMAT, + GST_TIME_ARGS (current_gst_time)); for (iter = streamIndex->children; iter; iter = iter->next) { if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) { - gchar *duration_str; - gchar *time_str; - gchar *seqnum_str; - GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1); - - duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION); - time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME); - seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER); - - /* use the node's seq number or use the previous + 1 */ - if (seqnum_str) { - fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10); - xmlFree (seqnum_str); - } else { - fragment->number = fragment_number; - } - fragment_number = fragment->number + 1; - - if (time_str) { - fragment->time = g_ascii_strtoull (time_str, NULL, 10); - xmlFree (time_str); - fragment_time_accum = fragment->time; - } else { - fragment->time = fragment_time_accum; - } - - /* if we have a previous fragment, means we need to set its duration */ - if (previous_fragment) - previous_fragment->duration = fragment->time - previous_fragment->time; - - if (duration_str) { - fragment->duration = g_ascii_strtoull (duration_str, NULL, 10); - - previous_fragment = NULL; - fragment_time_accum += fragment->duration; - xmlFree (duration_str); - } else { - /* store to set the duration at the next iteration */ - previous_fragment = fragment; - } - - if (fragment->time > current_time) { - new_fragments = g_list_append (new_fragments, fragment); - } else { - previous_fragment = NULL; - g_free (fragment); - } - + gst_mss_fragment_list_builder_add (&builder, iter); } else { /* TODO gst log this */ } } /* store the new fragments list */ - if (new_fragments) { + if (builder.fragments) { g_list_free_full (stream->fragments, g_free); - stream->fragments = new_fragments; - stream->current_fragment = new_fragments; + stream->fragments = g_list_reverse (builder.fragments); + stream->current_fragment = stream->fragments; + gst_mss_stream_seek (stream, current_gst_time); } } diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h index e4d4f8ed9..364f6d12a 100644 --- a/ext/smoothstreaming/gstmssmanifest.h +++ b/ext/smoothstreaming/gstmssmanifest.h @@ -48,7 +48,10 @@ void gst_mss_manifest_seek (GstMssManifest * manifest, guint64 time); gboolean gst_mss_manifest_change_bitrate (GstMssManifest *manifest, guint64 bitrate); guint64 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest); gboolean gst_mss_manifest_is_live (GstMssManifest * manifest); +gint64 gst_mss_manifest_get_dvr_window_length (GstMssManifest * manifest); +gint gst_mss_manifest_get_look_ahead_fragments_count (GstMssManifest * manifest); void gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data); +GstClockTime gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest); GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream); GstCaps * gst_mss_stream_get_caps (GstMssStream * stream); @@ -59,6 +62,7 @@ guint64 gst_mss_stream_get_timescale (GstMssStream * stream); GstFlowReturn gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url); GstClockTime gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream); GstClockTime gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream); +gboolean gst_mss_stream_has_next_fragment (GstMssStream * stream); GstFlowReturn gst_mss_stream_advance_fragment (GstMssStream * stream); GstFlowReturn gst_mss_stream_regress_fragment (GstMssStream * stream); void gst_mss_stream_seek (GstMssStream * stream, guint64 time); |