hlsdemux: Expose EXT-X-PROGRAM-DATE-TIME as tags.
authorEnrique Ocaña González <eocanha@igalia.com>
Mon, 2 May 2022 17:13:41 +0000 (19:13 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Thu, 17 Nov 2022 22:11:12 +0000 (22:11 +0000)
This allows an application to use timestamps associated
with fragments.

Patch by: Thomas Bluemel <tbluemel@control4.com>

See: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/195
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1417>

subprojects/gst-plugins-bad/ext/hls/gsthlsdemux.c
subprojects/gst-plugins-bad/ext/hls/gsthlsdemux.h
subprojects/gst-plugins-bad/ext/hls/m3u8.c
subprojects/gst-plugins-bad/ext/hls/m3u8.h
subprojects/gst-plugins-bad/tests/check/elements/hlsdemux_m3u8.c

index 8cf6c2a..305406e 100644 (file)
@@ -1166,6 +1166,13 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
     buffer = tmp_buffer;
   }
 
+  if (hlsdemux->prog_dt) {
+    gst_adaptive_demux_stream_set_tags (stream,
+        gst_tag_list_new (GST_TAG_DATE_TIME, hlsdemux->prog_dt, NULL));
+    gst_date_time_unref (hlsdemux->prog_dt);
+    hlsdemux->prog_dt = NULL;
+  }
+
   return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE);
 }
 
@@ -1247,7 +1254,9 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemuxStream * stream)
   m3u8 = gst_hls_demux_stream_get_m3u8 (hlsdemux_stream);
 
   forward = (stream->demux->segment.rate > 0);
-  file = gst_m3u8_get_next_fragment (m3u8, forward, &sequence_pos, &discont);
+  file =
+      gst_m3u8_get_next_fragment (m3u8, forward, &sequence_pos,
+      &hlsdemux->prog_dt, &discont);
 
   if (file == NULL) {
     GST_INFO_OBJECT (hlsdemux, "This playlist doesn't contain more fragments");
@@ -1359,6 +1368,12 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
   GST_DEBUG_OBJECT (demux, "Streams aware : %d", demux->streams_aware);
 
   gst_hls_demux_clear_all_pending_data (demux);
+
+  if (demux->prog_dt) {
+    gst_date_time_unref (demux->prog_dt);
+    demux->prog_dt = NULL;
+  }
+
   GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
 }
 
index 98d6099..846026e 100644 (file)
@@ -141,6 +141,8 @@ struct _GstHLSDemux
   GHashTable *keys;
   GMutex      keys_lock;
 
+  GstDateTime *prog_dt;
+
   /* FIXME: check locking, protected automatically by manifest_lock already? */
   /* The master playlist with the available variant streams */
   GstHLSMasterPlaylist *master;
index d20e352..4f3595e 100644 (file)
@@ -32,7 +32,8 @@
 #define GST_CAT_DEFAULT hls_debug
 
 static GstM3U8MediaFile *gst_m3u8_media_file_new (gchar * uri,
-    gchar * title, GstClockTime duration, guint sequence);
+    gchar * title, GstClockTime duration, guint sequence,
+    GstDateTime * program_dt);
 static void gst_m3u8_init_file_unref (GstM3U8InitFile * self);
 static gchar *uri_join (const gchar * uri, const gchar * path);
 
@@ -116,7 +117,7 @@ gst_m3u8_unref (GstM3U8 * self)
 
 static GstM3U8MediaFile *
 gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
-    guint sequence)
+    guint sequence, GstDateTime * program_dt)
 {
   GstM3U8MediaFile *file;
 
@@ -126,6 +127,7 @@ gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
   file->duration = duration;
   file->sequence = sequence;
   file->ref_count = 1;
+  file->program_dt = program_dt;
 
   return file;
 }
@@ -150,6 +152,8 @@ gst_m3u8_media_file_unref (GstM3U8MediaFile * self)
     g_free (self->title);
     g_free (self->uri);
     g_free (self->key);
+    if (self->program_dt)
+      gst_date_time_unref (self->program_dt);
     g_free (self);
   }
 }
@@ -483,6 +487,7 @@ gboolean
 gst_m3u8_update (GstM3U8 * self, gchar * data)
 {
   gint val;
+  GstDateTime *program_dt = NULL;
   GstClockTime duration;
   gchar *title, *end;
   gboolean discontinuity = FALSE;
@@ -558,7 +563,10 @@ gst_m3u8_update (GstM3U8 * self, gchar * data)
       data = uri_join (self->base_uri ? self->base_uri : self->uri, data);
       if (data != NULL) {
         GstM3U8MediaFile *file;
-        file = gst_m3u8_media_file_new (data, title, duration, mediasequence++);
+        file =
+            gst_m3u8_media_file_new (data, title, duration,
+            mediasequence++, program_dt);
+        program_dt = NULL;
 
         /* set encryption params */
         file->key = current_key ? g_strdup (current_key) : NULL;
@@ -647,8 +655,12 @@ gst_m3u8_update (GstM3U8 * self, gchar * data)
         self->discont_sequence++;
         discontinuity = TRUE;
       } else if (g_str_has_prefix (data_ext_x, "PROGRAM-DATE-TIME:")) {
-        /* <YYYY-MM-DDThh:mm:ssZ> */
-        GST_DEBUG ("FIXME parse date");
+        if (program_dt)
+          gst_date_time_unref (program_dt);
+        program_dt = gst_date_time_new_from_iso8601_string (data + 25);
+        if (!program_dt) {
+          GST_WARNING ("Could not parse program date/time");
+        }
       } else if (g_str_has_prefix (data_ext_x, "ALLOW-CACHE:")) {
         self->allowcache = g_ascii_strcasecmp (data + 19, "YES") == 0;
       } else if (g_str_has_prefix (data_ext_x, "KEY:")) {
@@ -767,6 +779,11 @@ gst_m3u8_update (GstM3U8 * self, gchar * data)
   g_free (current_key);
   current_key = NULL;
 
+  if (program_dt) {
+    gst_date_time_unref (program_dt);
+    program_dt = NULL;
+  }
+
   self->files = g_list_reverse (self->files);
 
   if (last_init_file)
@@ -919,7 +936,8 @@ m3u8_find_next_fragment (GstM3U8 * m3u8, gboolean forward)
 
 GstM3U8MediaFile *
 gst_m3u8_get_next_fragment (GstM3U8 * m3u8, gboolean forward,
-    GstClockTime * sequence_position, gboolean * discont)
+    GstClockTime * sequence_position, GstDateTime ** program_dt,
+    gboolean * discont)
 {
   GstM3U8MediaFile *file = NULL;
 
@@ -945,6 +963,11 @@ gst_m3u8_get_next_fragment (GstM3U8 * m3u8, gboolean forward,
 
   if (sequence_position)
     *sequence_position = m3u8->sequence_position;
+
+  if (program_dt)
+    *program_dt =
+        file->program_dt ? gst_date_time_ref (file->program_dt) : NULL;
+
   if (discont)
     *discont = file->discont || (m3u8->sequence != file->sequence);
 
index aa51136..4a8d926 100644 (file)
@@ -99,6 +99,7 @@ struct _GstM3U8MediaFile
   gchar *key;
   guint8 iv[16];
   gint64 offset, size;
+  GstDateTime *program_dt;      /* program date time */
   gint ref_count;               /* ATOMIC */
   GstM3U8InitFile *init_file;   /* Media Initialization (hold ref) */
 };
@@ -127,6 +128,7 @@ void               gst_m3u8_set_uri              (GstM3U8      * m3u8,
 GstM3U8MediaFile * gst_m3u8_get_next_fragment    (GstM3U8      * m3u8,
                                                   gboolean       forward,
                                                   GstClockTime * sequence_position,
+                                                  GstDateTime ** program_dt,
                                                   gboolean     * discont);
 
 gboolean           gst_m3u8_has_next_fragment    (GstM3U8 * m3u8,
index a418fe9..beb492b 100644 (file)
@@ -572,7 +572,7 @@ GST_START_TEST (test_live_playlist_rotated)
 
   ret = gst_m3u8_update (pl, g_strdup (LIVE_ROTATED_PLAYLIST));
   assert_equals_int (ret, TRUE);
-  file = gst_m3u8_get_next_fragment (pl, TRUE, NULL, NULL);
+  file = gst_m3u8_get_next_fragment (pl, TRUE, NULL, NULL, NULL);
   fail_unless (file != NULL);
   gst_m3u8_media_file_unref (file);
 
@@ -805,7 +805,7 @@ GST_START_TEST (test_get_next_fragment)
   pl = master->default_variant->m3u8;
 
   /* Check the next fragment */
-  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, &discontinuous);
+  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, NULL, &discontinuous);
   fail_unless (mf != NULL);
   assert_equals_int (discontinuous, FALSE);
   assert_equals_string (mf->uri, "http://media.example.com/all.ts");
@@ -818,7 +818,7 @@ GST_START_TEST (test_get_next_fragment)
   gst_m3u8_advance_fragment (pl, TRUE);
 
   /* Check next media segments */
-  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, &discontinuous);
+  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, NULL, &discontinuous);
   fail_unless (mf != NULL);
   assert_equals_int (discontinuous, FALSE);
   assert_equals_string (mf->uri, "http://media.example.com/all.ts");
@@ -831,7 +831,7 @@ GST_START_TEST (test_get_next_fragment)
   gst_m3u8_advance_fragment (pl, TRUE);
 
   /* Check next media segments */
-  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, &discontinuous);
+  mf = gst_m3u8_get_next_fragment (pl, TRUE, &timestamp, NULL, &discontinuous);
   assert_equals_int (discontinuous, FALSE);
   assert_equals_string (mf->uri, "http://media.example.com/all.ts");
   assert_equals_uint64 (timestamp, 20 * GST_SECOND);