From 7496d2750ea9c47e6246482888e90998b560aa2a Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 18 Nov 2022 03:24:38 +1100 Subject: [PATCH] hlsdemux2: Parse EXT-X-SKIP tag Parse the attributes from the EXT-X-SKIP tag Part-of: --- .../ext/adaptivedemux2/hls/m3u8.c | 48 +++++++++++++++++++ .../ext/adaptivedemux2/hls/m3u8.h | 5 ++ .../tests/check/elements/hlsdemux_m3u8.c | 43 +++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c index 644bdc9cf9..f494b8a588 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c @@ -66,6 +66,9 @@ gst_hls_media_playlist_unref (GstHLSMediaPlaylist * self) if (self->preload_hints != NULL) g_ptr_array_free (self->preload_hints, TRUE); + if (self->removed_date_ranges != NULL) + g_strfreev (self->removed_date_ranges); + g_free (self->last_data); g_mutex_clear (&self->lock); g_free (self); @@ -479,6 +482,17 @@ gst_hls_media_playlist_dump (GstHLSMediaPlaylist * self) GST_DEBUG ("can block reloads: %s", self->can_block_reload ? "YES" : "NO"); + GST_DEBUG ("skipped segments: %d", self->skipped_segments); + + if (self->num_removed_date_ranges && self->removed_date_ranges) { + GST_DEBUG ("Removed date ranges: %u", self->num_removed_date_ranges); + gchar **cur = self->removed_date_ranges; + while (*cur != NULL) { + GST_DEBUG (" ID: %s", *cur); + cur++; + } + } + GST_DEBUG ("Segments : %d", self->segments->len); for (idx = 0; idx < self->segments->len; idx++) { GstM3U8MediaSegment *segment = g_ptr_array_index (self->segments, idx); @@ -762,6 +776,37 @@ malformed_line: } } +/* Parse EXT-X-SKIP */ +static void +parse_skip_tag (GstHLSMediaPlaylist * self, gchar * data) +{ + gchar *v, *a; + + while (data != NULL && parse_attributes (&data, &a, &v)) { + if (strcmp (a, "SKIPPED-SEGMENTS") == 0) { + if (!int_from_string (v, NULL, &self->skipped_segments) + || self->skipped_segments < 0) { + GST_WARNING ("Can't read skipped segments from EXT-X-SKIP value"); + self->skipped_segments = 0; + goto malformed_line; + } + } else if (strcmp (a, "RECENTLY-REMOVED-DATERANGES") == 0) { + gchar **removed_date_ranges = g_strsplit (v, "\t", -1); + + g_strfreev (self->removed_date_ranges); + self->removed_date_ranges = removed_date_ranges; + self->num_removed_date_ranges = g_strv_length (removed_date_ranges); + } + } + + return; +malformed_line: + { + GST_WARNING ("Invalid EXT-X-SKIP entry in playlist"); + return; + } +} + /* Parse and create a new GstHLSMediaPlaylist */ GstHLSMediaPlaylist * gst_hls_media_playlist_parse (gchar * data, @@ -1106,6 +1151,9 @@ gst_hls_media_playlist_parse (gchar * data, (GDestroyNotify) gst_m3u8_preload_hint_unref); } g_ptr_array_add (self->preload_hints, hint); + } else if (g_str_has_prefix (data_ext_x, "SKIP:")) { + data += strlen ("#EXT-X-SKIP:"); + parse_skip_tag (self, data); } else { GST_LOG ("Ignored line: %s", data); } diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h index bdfa95572d..2ef5edc37b 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h @@ -142,6 +142,11 @@ struct _GstHLSMediaPlaylist GstClockTime part_hold_back; /* Part-Hold-Back value, if provided (or CLOCK_TIME_NONE */ gboolean can_block_reload; /* TRUE if CAN-BLOCK-RELOAD was YES */ + /* Delta playlist info from EXT-X-SKIP tag */ + gint skipped_segments; + gint num_removed_date_ranges; + gchar **removed_date_ranges; + /*< private > */ GMutex lock; diff --git a/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c b/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c index 45c4f802db..588a6b1e3d 100644 --- a/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c +++ b/subprojects/gst-plugins-good/tests/check/elements/hlsdemux_m3u8.c @@ -259,6 +259,30 @@ midRoll273.mp4\n\ #EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"midRoll274.1.mp4\"\n\ #EXT-X-RENDITION-REPORT:URI=\"/1M/LL-HLS.m3u8\",LAST-MSN=274,LAST-PART=1"; +static const gchar *SKIP_PLAYLIST = "#EXTM3U\n\ +#EXT-X-VERSION:7\n\ +#EXT-X-TARGETDURATION:4\n\ +#EXT-X-PART-INF:PART-TARGET=2\n\ +#EXT-X-SKIP:SKIPPED-SEGMENTS=2,RECENTLY-REMOVED-DATERANGES=\"splice-6FFFFFF0\tsplice-6FFFFFF1\"\n\ +#EXTINF:4.00008,\n\ +fileSequence270.mp4\n\ +#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart271.0.mp4\"\n\ +#EXT-X-PART:DURATION=2.00004,URI=\"filePart271.1.mp4\"\n\ +#EXTINF:4.00008,\n\ +fileSequence271.mp4\n\ +#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart272.0.mp4\"\n\ +#EXT-X-PART:DURATION=0.50001,URI=\"filePart272.1.mp4\"\n\ +#EXTINF:2.50005,\n\ +fileSequence272.mp4\n\ +#EXT-X-DISCONTINUITY\n\ +#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll273.0.mp4\"\n\ +#EXT-X-PART:DURATION=2.00004,URI=\"midRoll273.1.mp4\"\n\ +#EXTINF:4.00008,\n\ +midRoll273.mp4\n\ +#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll274.0.mp4\"\n\ +#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"midRoll274.1.mp4\"\n\ +#EXT-X-RENDITION-REPORT:URI=\"/1M/LL-HLS.m3u8\",LAST-MSN=274,LAST-PART=1"; + static GstHLSMediaPlaylist * load_m3u8 (const gchar * data) { @@ -920,6 +944,24 @@ GST_START_TEST (test_low_latency_playlist) } GST_END_TEST; + +GST_START_TEST (test_playlist_skip) +{ + GstHLSMediaPlaylist *pl; + + pl = load_m3u8 (SKIP_PLAYLIST); + fail_unless (pl != NULL); + + assert_equals_int (pl->skipped_segments, 2); + assert_equals_int (pl->num_removed_date_ranges, 2); + fail_unless (g_strcmp0 (pl->removed_date_ranges[0], "splice-6FFFFFF0") == 0); + fail_unless (g_strcmp0 (pl->removed_date_ranges[1], "splice-6FFFFFF1") == 0); + + gst_hls_media_playlist_unref (pl); +} + +GST_END_TEST; + static Suite * hlsdemux_suite (void) { @@ -955,6 +997,7 @@ hlsdemux_suite (void) tcase_add_test (tc_m3u8, test_stream_inf_tag); tcase_add_test (tc_m3u8, test_map_tag); tcase_add_test (tc_m3u8, test_low_latency_playlist); + tcase_add_test (tc_m3u8, test_playlist_skip); return s; }