hlsdemux2: Implement preload hint parsing

Load EXT-X-PRELOAD-HINT into a preload_hints array in the media playlist

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
This commit is contained in:
Jan Schmidt 2022-06-09 00:39:13 +10:00 committed by GStreamer Marge Bot
parent 3ed6a23a4d
commit 3c50f54310
2 changed files with 146 additions and 0 deletions
subprojects/gst-plugins-good/ext/adaptivedemux2/hls

View file

@ -60,6 +60,9 @@ gst_hls_media_playlist_unref (GstHLSMediaPlaylist * self)
g_ptr_array_free (self->segments, TRUE); g_ptr_array_free (self->segments, TRUE);
if (self->preload_hints != NULL)
g_ptr_array_free (self->preload_hints, TRUE);
g_free (self->last_data); g_free (self->last_data);
g_mutex_clear (&self->lock); g_mutex_clear (&self->lock);
g_free (self); g_free (self);
@ -138,6 +141,26 @@ gst_m3u8_partial_segment_unref (GstM3U8PartialSegment * part)
} }
} }
GstM3U8PreloadHint *
gst_m3u8_preload_hint_ref (GstM3U8PreloadHint * hint)
{
g_assert (hint != NULL && hint->ref_count > 0);
g_atomic_int_add (&hint->ref_count, 1);
return hint;
}
void
gst_m3u8_preload_hint_unref (GstM3U8PreloadHint * hint)
{
g_return_if_fail (hint != NULL && hint->ref_count > 0);
if (g_atomic_int_dec_and_test (&hint->ref_count)) {
g_free (hint->uri);
g_free (hint);
}
}
static GstM3U8InitFile * static GstM3U8InitFile *
gst_m3u8_init_file_new (gchar * uri, gint64 size, gint64 offset) gst_m3u8_init_file_new (gchar * uri, gint64 size, gint64 offset)
{ {
@ -474,6 +497,28 @@ gst_hls_media_playlist_dump (GstHLSMediaPlaylist * self)
} }
} }
} }
if (self->preload_hints) {
GST_DEBUG ("Preload Hints: %d", self->preload_hints->len);
for (idx = 0; idx < self->preload_hints->len; idx++) {
GstM3U8PreloadHint *hint = g_ptr_array_index (self->preload_hints, idx);
const gchar *hint_type_str;
switch (hint->hint_type) {
case M3U8_PRELOAD_HINT_MAP:
hint_type_str = "MAP";
break;
case M3U8_PRELOAD_HINT_PART:
hint_type_str = "PART";
break;
default:
g_assert_not_reached ();
}
GST_DEBUG (" preload hint %u: type %s", idx, hint_type_str);
GST_DEBUG (" uri : %s %" G_GUINT64_FORMAT " %"
G_GINT64_FORMAT, hint->uri, hint->offset, hint->size);
}
}
#endif #endif
} }
@ -596,6 +641,62 @@ malformed_line:
} }
} }
static GstM3U8PreloadHint *
gst_m3u8_parse_preload_hint (gchar * data, const gchar * base_uri)
{
gchar *v, *a;
GstM3U8PreloadHint *hint = g_new0 (GstM3U8PreloadHint, 1);
gboolean have_hint_type = FALSE;
hint->ref_count = 1;
hint->size = -1;
while (data != NULL && parse_attributes (&data, &a, &v)) {
if (strcmp (a, "TYPE") == 0) {
if (g_ascii_strcasecmp (v, "MAP") == 0) {
hint->hint_type = M3U8_PRELOAD_HINT_MAP;
} else if (g_ascii_strcasecmp (v, "PART") == 0) {
hint->hint_type = M3U8_PRELOAD_HINT_PART;
} else {
GST_WARNING ("Unknown Preload Hint type %s", v);
goto malformed_line;
}
have_hint_type = TRUE;
} else if (strcmp (a, "URI") == 0) {
g_free (hint->uri);
hint->uri = uri_join (base_uri, v);
} else if (strcmp (a, "BYTERANGE-START") == 0) {
if (int64_from_string (v, NULL, &hint->offset)) {
goto malformed_line;
}
} else if (strcmp (a, "BYTERANGE-LENGTH") == 0) {
if (int64_from_string (v, NULL, &hint->size)) {
goto malformed_line;
}
}
}
if (hint->uri == NULL || !have_hint_type) {
goto required_attributes_missing;
}
return hint;
required_attributes_missing:
{
GST_WARNING
("EXT-X-PRELOAD-HINT is missing required URI or TYPE attributes");
gst_m3u8_preload_hint_unref (hint);
return NULL;
}
malformed_line:
{
GST_WARNING ("Invalid EXT-X-PRELOAD-HINT entry in playlist");
gst_m3u8_preload_hint_unref (hint);
return NULL;
}
}
static void static void
parse_server_control (GstHLSMediaPlaylist * self, gchar * data) parse_server_control (GstHLSMediaPlaylist * self, gchar * data)
{ {
@ -960,6 +1061,20 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
} else if (g_str_has_prefix (data_ext_x, "SERVER-CONTROL:")) { } else if (g_str_has_prefix (data_ext_x, "SERVER-CONTROL:")) {
data += strlen ("#EXT-X-SERVER-CONTROL:"); data += strlen ("#EXT-X-SERVER-CONTROL:");
parse_server_control (self, data); parse_server_control (self, data);
} else if (g_str_has_prefix (data_ext_x, "PRELOAD-HINT:")) {
GstM3U8PreloadHint *hint = NULL;
hint =
gst_m3u8_parse_preload_hint (data + strlen ("#EXT-X-PRELOAD-HINT:"),
self->base_uri ? self->base_uri : self->uri);
if (hint == NULL)
goto next_line;
if (self->preload_hints == NULL) {
self->preload_hints = g_ptr_array_new_full (1,
(GDestroyNotify) gst_m3u8_preload_hint_unref);
}
g_ptr_array_add (self->preload_hints, hint);
} else { } else {
GST_LOG ("Ignored line: %s", data); GST_LOG ("Ignored line: %s", data);
} }

View file

@ -37,6 +37,8 @@ typedef struct _GstHLSTimeMap GstHLSTimeMap;
typedef struct _GstM3U8MediaSegment GstM3U8MediaSegment; typedef struct _GstM3U8MediaSegment GstM3U8MediaSegment;
typedef struct _GstM3U8PartialSegment GstM3U8PartialSegment; typedef struct _GstM3U8PartialSegment GstM3U8PartialSegment;
typedef struct _GstM3U8InitFile GstM3U8InitFile; typedef struct _GstM3U8InitFile GstM3U8InitFile;
typedef enum _GstM3U8PreloadHintType GstM3U8PreloadHintType;
typedef struct _GstM3U8PreloadHint GstM3U8PreloadHint;
typedef struct _GstHLSRenditionStream GstHLSRenditionStream; typedef struct _GstHLSRenditionStream GstHLSRenditionStream;
typedef struct _GstM3U8Client GstM3U8Client; typedef struct _GstM3U8Client GstM3U8Client;
typedef struct _GstHLSVariantStream GstHLSVariantStream; typedef struct _GstHLSVariantStream GstHLSVariantStream;
@ -105,6 +107,8 @@ struct _GstHLSMediaPlaylist
GPtrArray *segments; /* Array of GstM3U8MediaSegment */ GPtrArray *segments; /* Array of GstM3U8MediaSegment */
GPtrArray *preload_hints; /* Array of GstM3U8PreloadHint */
/* Generated information */ /* Generated information */
GstClockTime duration; /* The estimated total duration of all segments GstClockTime duration; /* The estimated total duration of all segments
contained in this playlist */ contained in this playlist */
@ -164,6 +168,33 @@ gst_m3u8_partial_segment_ref (GstM3U8PartialSegment *part);
void void
gst_m3u8_partial_segment_unref (GstM3U8PartialSegment *part); gst_m3u8_partial_segment_unref (GstM3U8PartialSegment *part);
enum _GstM3U8PreloadHintType {
M3U8_PRELOAD_HINT_MAP,
M3U8_PRELOAD_HINT_PART,
};
/**
* GstM3U8PreloadHint:
*
* Official term in RFC : "Preload Hint"
*
*/
struct _GstM3U8PreloadHint
{
GstM3U8PreloadHintType hint_type;
gchar *uri;
gint64 offset, size;
gint ref_count; /* ATOMIC */
};
GstM3U8PreloadHint *
gst_m3u8_preload_hint_ref (GstM3U8PreloadHint *hint);
void
gst_m3u8_preload_hint_unref (GstM3U8PreloadHint *hint);
/** /**
* GstM3U8MediaSegment: * GstM3U8MediaSegment:
* *