From cd02546089e5ae0e53e3e18ab33a4b6a6c2d5d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 5 Mar 2014 10:47:01 +0100 Subject: [PATCH] hlsdemux: Implement handling of byte ranges --- ext/hls/gsthlsdemux.c | 14 ++-- ext/hls/m3u8.c | 88 ++++++++++++++++++++---- ext/hls/m3u8.h | 4 +- gst-libs/gst/uridownloader/gstfragment.c | 14 +++- 4 files changed, 99 insertions(+), 21 deletions(-) diff --git a/ext/hls/gsthlsdemux.c b/ext/hls/gsthlsdemux.c index ad16f4f07a..1899068611 100644 --- a/ext/hls/gsthlsdemux.c +++ b/ext/hls/gsthlsdemux.c @@ -774,7 +774,7 @@ gst_hls_demux_stream_loop (GstHLSDemux * demux) /* Got a new fragment or not live anymore? */ if (gst_m3u8_client_get_next_fragment (demux->client, NULL, NULL, - NULL, NULL, NULL, NULL) + NULL, NULL, NULL, NULL, NULL, NULL) || !gst_m3u8_client_is_live (demux->client)) break; @@ -1403,21 +1403,25 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux, GstClockTime timestamp; GstBuffer *buf; gboolean discont; + gint64 range_start, range_end; const gchar *key = NULL; const guint8 *iv = NULL; *end_of_playlist = FALSE; if (!gst_m3u8_client_get_next_fragment (demux->client, &discont, - &next_fragment_uri, &duration, ×tamp, &key, &iv)) { + &next_fragment_uri, &duration, ×tamp, &range_start, &range_end, + &key, &iv)) { GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments"); *end_of_playlist = TRUE; return NULL; } - GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); + GST_INFO_OBJECT (demux, + "Fetching next fragment %s (range=%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT + ")", next_fragment_uri, range_start, range_end); - download = gst_uri_downloader_fetch_uri (demux->downloader, - next_fragment_uri, FALSE, err); + download = gst_uri_downloader_fetch_uri_with_range (demux->downloader, + next_fragment_uri, FALSE, range_start, range_end, err); if (download == NULL) goto error; diff --git a/ext/hls/m3u8.c b/ext/hls/m3u8.c index de6dc61fa4..fac11598c9 100644 --- a/ext/hls/m3u8.c +++ b/ext/hls/m3u8.c @@ -135,6 +135,31 @@ int_from_string (gchar * ptr, gchar ** endptr, gint * val) return end != ptr; } +static gboolean +int64_from_string (gchar * ptr, gchar ** endptr, gint64 * val) +{ + gchar *end; + gint64 ret; + + g_return_val_if_fail (ptr != NULL, FALSE); + g_return_val_if_fail (val != NULL, FALSE); + + errno = 0; + ret = g_ascii_strtoll (ptr, &end, 10); + if ((errno == ERANGE && (ret == G_MAXINT64 || ret == G_MININT64)) + || (errno != 0 && ret == 0)) { + GST_WARNING ("%s", g_strerror (errno)); + return FALSE; + } + + if (endptr) + *endptr = end; + + *val = ret; + + return end != ptr; +} + static gboolean double_from_string (gchar * ptr, gchar ** endptr, gdouble * val) { @@ -168,7 +193,7 @@ double_from_string (gchar * ptr, gchar ** endptr, gdouble * val) static gboolean parse_attributes (gchar ** ptr, gchar ** a, gchar ** v) { - gchar *end=NULL, *p; + gchar *end = NULL, *p; g_return_val_if_fail (ptr != NULL, FALSE); g_return_val_if_fail (*ptr != NULL, FALSE); @@ -179,18 +204,18 @@ parse_attributes (gchar ** ptr, gchar ** a, gchar ** v) *a = *ptr; end = p = g_utf8_strchr (*ptr, -1, ','); - if(end){ - gchar *q = g_utf8_strchr (*ptr, -1, '"'); - if(q && qsize = size; + if (offset != -1) { + file->offset = offset; + } else { + GstM3U8MediaFile *prev = + self->files ? g_list_last (self->files)->data : NULL; + + if (!prev) { + offset = 0; + } else { + offset = prev->offset + prev->size; + } + } + } else { + file->size = -1; + file->offset = 0; + } + file->discont = discontinuity; duration = 0; title = NULL; discontinuity = FALSE; + size = offset = -1; self->files = g_list_append (self->files, file); } @@ -459,6 +505,15 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) g_free (title); title = g_strdup (data); } + } else if (g_str_has_prefix (data, "#EXT-X-BYTERANGE:")) { + gchar *v = data + 17; + + if (int64_from_string (v, &v, &size)) { + if (*v == '@' && !int64_from_string (v + 1, &v, &offset)) + goto next_line; + } else { + goto next_line; + } } else { GST_LOG ("Ignored line: %s", data); } @@ -596,7 +651,8 @@ _find_next (GstM3U8MediaFile * file, GstM3U8Client * client) gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, - GstClockTime * timestamp, const gchar ** key, const guint8 ** iv) + GstClockTime * timestamp, gint64 * range_start, gint64 * range_end, + const gchar ** key, const guint8 ** iv) { GList *l; GstM3U8MediaFile *file; @@ -626,6 +682,10 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, *uri = file->uri; if (duration) *duration = file->duration; + if (range_start) + *range_start = file->offset; + if (range_end) + *range_end = file->size != -1 ? file->offset + file->size : -1; if (key) *key = file->key; if (iv) diff --git a/ext/hls/m3u8.h b/ext/hls/m3u8.h index 7ec03fa751..a1ee42552e 100644 --- a/ext/hls/m3u8.h +++ b/ext/hls/m3u8.h @@ -69,6 +69,7 @@ struct _GstM3U8MediaFile gboolean discont; /* this file marks a discontinuity */ gchar *key; guint8 iv[16]; + gint64 offset, size; }; struct _GstM3U8Client @@ -88,7 +89,8 @@ gboolean gst_m3u8_client_update (GstM3U8Client * client, gchar * data); void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8); gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, - GstClockTime * timestamp, const gchar ** key, const guint8 ** iv); + GstClockTime * timestamp, gint64 * range_start, gint64 * range_end, + const gchar ** key, const guint8 ** iv); void gst_m3u8_client_advance_fragment (GstM3U8Client * client); GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client); GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client); diff --git a/gst-libs/gst/uridownloader/gstfragment.c b/gst-libs/gst/uridownloader/gstfragment.c index 452456a490..d595e82538 100644 --- a/gst-libs/gst/uridownloader/gstfragment.c +++ b/gst-libs/gst/uridownloader/gstfragment.c @@ -235,9 +235,21 @@ gst_fragment_get_caps (GstFragment * fragment) return NULL; g_mutex_lock (&fragment->priv->lock); - if (fragment->priv->caps == NULL) + if (fragment->priv->caps == NULL) { + guint64 offset, offset_end; + + /* FIXME: This is currently necessary as typefinding only + * works with 0 offsets... need to find a better way to + * do that */ + offset = GST_BUFFER_OFFSET (fragment->priv->buffer); + offset_end = GST_BUFFER_OFFSET_END (fragment->priv->buffer); + GST_BUFFER_OFFSET (fragment->priv->buffer) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_OFFSET_END (fragment->priv->buffer) = GST_BUFFER_OFFSET_NONE; fragment->priv->caps = gst_type_find_helper_for_buffer (NULL, fragment->priv->buffer, NULL); + GST_BUFFER_OFFSET (fragment->priv->buffer) = offset; + GST_BUFFER_OFFSET_END (fragment->priv->buffer) = offset_end; + } gst_caps_ref (fragment->priv->caps); g_mutex_unlock (&fragment->priv->lock);