mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-25 16:48:11 +00:00
hlsdemux: Implement handling of byte ranges
This commit is contained in:
parent
7ed08a1326
commit
cd02546089
4 changed files with 99 additions and 21 deletions
|
@ -774,7 +774,7 @@ gst_hls_demux_stream_loop (GstHLSDemux * demux)
|
||||||
|
|
||||||
/* Got a new fragment or not live anymore? */
|
/* Got a new fragment or not live anymore? */
|
||||||
if (gst_m3u8_client_get_next_fragment (demux->client, NULL, NULL,
|
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))
|
|| !gst_m3u8_client_is_live (demux->client))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1403,21 +1403,25 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux,
|
||||||
GstClockTime timestamp;
|
GstClockTime timestamp;
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
gboolean discont;
|
gboolean discont;
|
||||||
|
gint64 range_start, range_end;
|
||||||
const gchar *key = NULL;
|
const gchar *key = NULL;
|
||||||
const guint8 *iv = NULL;
|
const guint8 *iv = NULL;
|
||||||
|
|
||||||
*end_of_playlist = FALSE;
|
*end_of_playlist = FALSE;
|
||||||
if (!gst_m3u8_client_get_next_fragment (demux->client, &discont,
|
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");
|
GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments");
|
||||||
*end_of_playlist = TRUE;
|
*end_of_playlist = TRUE;
|
||||||
return NULL;
|
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,
|
download = gst_uri_downloader_fetch_uri_with_range (demux->downloader,
|
||||||
next_fragment_uri, FALSE, err);
|
next_fragment_uri, FALSE, range_start, range_end, err);
|
||||||
|
|
||||||
if (download == NULL)
|
if (download == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -135,6 +135,31 @@ int_from_string (gchar * ptr, gchar ** endptr, gint * val)
|
||||||
return end != ptr;
|
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
|
static gboolean
|
||||||
double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
|
double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
|
||||||
{
|
{
|
||||||
|
@ -168,7 +193,7 @@ double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
|
||||||
static gboolean
|
static gboolean
|
||||||
parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
|
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);
|
||||||
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;
|
*a = *ptr;
|
||||||
end = p = g_utf8_strchr (*ptr, -1, ',');
|
end = p = g_utf8_strchr (*ptr, -1, ',');
|
||||||
if(end){
|
if (end) {
|
||||||
gchar *q = g_utf8_strchr (*ptr, -1, '"');
|
gchar *q = g_utf8_strchr (*ptr, -1, '"');
|
||||||
if(q && q<end){
|
if (q && q < end) {
|
||||||
/* special case, such as CODECS="avc1.77.30, mp4a.40.2" */
|
/* special case, such as CODECS="avc1.77.30, mp4a.40.2" */
|
||||||
q = g_utf8_next_char (q);
|
q = g_utf8_next_char (q);
|
||||||
if(q){
|
if (q) {
|
||||||
q = g_utf8_strchr (q, -1, '"');
|
q = g_utf8_strchr (q, -1, '"');
|
||||||
}
|
}
|
||||||
if(q){
|
if (q) {
|
||||||
end = p = g_utf8_strchr (q, -1, ',');
|
end = p = g_utf8_strchr (q, -1, ',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (end) {
|
if (end) {
|
||||||
do {
|
do {
|
||||||
|
@ -240,6 +265,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
GstM3U8 *list;
|
GstM3U8 *list;
|
||||||
gboolean have_iv = FALSE;
|
gboolean have_iv = FALSE;
|
||||||
guint8 iv[16] = { 0, };
|
guint8 iv[16] = { 0, };
|
||||||
|
gint64 size = -1, offset = -1;
|
||||||
|
|
||||||
g_return_val_if_fail (self != NULL, FALSE);
|
g_return_val_if_fail (self != NULL, FALSE);
|
||||||
g_return_val_if_fail (data != NULL, FALSE);
|
g_return_val_if_fail (data != NULL, FALSE);
|
||||||
|
@ -324,11 +350,31 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (size != -1) {
|
||||||
|
file->size = 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;
|
file->discont = discontinuity;
|
||||||
|
|
||||||
duration = 0;
|
duration = 0;
|
||||||
title = NULL;
|
title = NULL;
|
||||||
discontinuity = FALSE;
|
discontinuity = FALSE;
|
||||||
|
size = offset = -1;
|
||||||
self->files = g_list_append (self->files, file);
|
self->files = g_list_append (self->files, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,6 +505,15 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
g_free (title);
|
g_free (title);
|
||||||
title = g_strdup (data);
|
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 {
|
} else {
|
||||||
GST_LOG ("Ignored line: %s", data);
|
GST_LOG ("Ignored line: %s", data);
|
||||||
}
|
}
|
||||||
|
@ -596,7 +651,8 @@ _find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
|
||||||
gboolean
|
gboolean
|
||||||
gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||||
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
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;
|
GList *l;
|
||||||
GstM3U8MediaFile *file;
|
GstM3U8MediaFile *file;
|
||||||
|
@ -626,6 +682,10 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||||
*uri = file->uri;
|
*uri = file->uri;
|
||||||
if (duration)
|
if (duration)
|
||||||
*duration = file->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)
|
if (key)
|
||||||
*key = file->key;
|
*key = file->key;
|
||||||
if (iv)
|
if (iv)
|
||||||
|
|
|
@ -69,6 +69,7 @@ struct _GstM3U8MediaFile
|
||||||
gboolean discont; /* this file marks a discontinuity */
|
gboolean discont; /* this file marks a discontinuity */
|
||||||
gchar *key;
|
gchar *key;
|
||||||
guint8 iv[16];
|
guint8 iv[16];
|
||||||
|
gint64 offset, size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstM3U8Client
|
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);
|
void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8);
|
||||||
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||||
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
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);
|
void gst_m3u8_client_advance_fragment (GstM3U8Client * client);
|
||||||
GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client);
|
GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client);
|
||||||
GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client);
|
GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client);
|
||||||
|
|
|
@ -235,9 +235,21 @@ gst_fragment_get_caps (GstFragment * fragment)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
g_mutex_lock (&fragment->priv->lock);
|
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 =
|
fragment->priv->caps =
|
||||||
gst_type_find_helper_for_buffer (NULL, fragment->priv->buffer, NULL);
|
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);
|
gst_caps_ref (fragment->priv->caps);
|
||||||
g_mutex_unlock (&fragment->priv->lock);
|
g_mutex_unlock (&fragment->priv->lock);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue