summaryrefslogtreecommitdiff
path: root/ext/hls/m3u8.c
diff options
context:
space:
mode:
authorAlex Ashley <bugzilla@ashley-family.net>2014-04-04 16:45:51 +0100
committerThiago Santos <thiagoss@osg.samsung.com>2015-01-05 17:59:08 -0300
commit50b5d94b2a1dfa702107c9f9203baf6df517f61e (patch)
tree60caa97268abd855a6438a4e28d82ce38e3ab261 /ext/hls/m3u8.c
parentaf78e2501ca02d2f9cf19a2dd4713b46ff64aca0 (diff)
downloadgstreamer-plugins-bad-50b5d94b2a1dfa702107c9f9203baf6df517f61e.tar.gz
hlsdemux: Implement live seeking
hlsdemux assumes that seeking is not allowed for live streams, however seek is possible if there are sufficient fragments in the manifest. For example the BBC have live streams that contain 2 hours of fragments. The seek code for both live and on-demand is common code. The difference between them is that an offset has to be calculated for the timecode of the first fragment in the live playlist. When hlsdemux starts to play a live stream, the possible seek range is between 0 and A seconds. After some time has passed, the beginning of the stream will no longer be available in the playlist and the seek range is between B and C seconds. Seek range: start 0 ........... A later B ........... C This commit adds code to keep a note of the B and C values and the highest sequence number it has seen. Every time it updates the media playlist, it walks the list of fragments, seeing if there is a fragment with sequence number > highest_seen_sequence. If so, the values of B and C are updated. The value of B is used when timestamping buffers. It also makes sure the seek range is never closer than three fragments from the end of the playlist - see 6.3.3. "Playing the Playlist file" of the HLS draft. https://bugzilla.gnome.org/show_bug.cgi?id=725435
Diffstat (limited to 'ext/hls/m3u8.c')
-rwxr-xr-xext/hls/m3u8.c81
1 files changed, 77 insertions, 4 deletions
diff --git a/ext/hls/m3u8.c b/ext/hls/m3u8.c
index c737b3d4a..ee0a45d84 100755
--- a/ext/hls/m3u8.c
+++ b/ext/hls/m3u8.c
@@ -51,8 +51,8 @@ gst_g_list_copy_deep (GList * list, GCopyFunc func, gpointer user_data)
static GstM3U8 *gst_m3u8_new (void);
static void gst_m3u8_free (GstM3U8 * m3u8);
-static gboolean gst_m3u8_update (GstM3U8 * m3u8, gchar * data,
- gboolean * updated);
+static gboolean gst_m3u8_update (GstM3U8Client * client, GstM3U8 * m3u8,
+ gchar * data, gboolean * updated);
static GstM3U8MediaFile *gst_m3u8_media_file_new (gchar * uri,
gchar * title, GstClockTime duration, guint sequence);
static void gst_m3u8_media_file_free (GstM3U8MediaFile * self);
@@ -382,7 +382,8 @@ gst_m3u8_compare_playlist_by_bitrate (gconstpointer a, gconstpointer b)
* @data: a m3u8 playlist text data, taking ownership
*/
static gboolean
-gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
+gst_m3u8_update (GstM3U8Client * client, GstM3U8 * self, gchar * data,
+ gboolean * updated)
{
gint val;
GstClockTime duration;
@@ -723,6 +724,37 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
self->current_variant = g_list_find_custom (self->lists, top_variant_uri,
(GCompareFunc) _m3u8_compare_uri);
}
+ /* calculate the start and end times of this media playlist. */
+ if (self->files) {
+ GList *walk;
+ GstM3U8MediaFile *file;
+ GstClockTime duration = 0;
+
+ for (walk = self->files; walk; walk = walk->next) {
+ file = walk->data;
+ duration += file->duration;
+ if (file->sequence > client->highest_sequence_number) {
+ if (client->highest_sequence_number >= 0) {
+ /* if an update of the media playlist has been missed, there
+ will be a gap between self->highest_sequence_number and the
+ first sequence number in this media playlist. In this situation
+ assume that the missing fragments had a duration of
+ targetduration each */
+ client->last_file_end +=
+ (file->sequence - client->highest_sequence_number -
+ 1) * self->targetduration;
+ }
+ client->last_file_end += file->duration;
+ client->highest_sequence_number = file->sequence;
+ }
+ }
+ if (GST_M3U8_CLIENT_IS_LIVE (client)) {
+ client->first_file_start = client->last_file_end - duration;
+ GST_DEBUG ("Live playlist range %" GST_TIME_FORMAT " -> %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (client->first_file_start),
+ GST_TIME_ARGS (client->last_file_end));
+ }
+ }
return TRUE;
}
@@ -740,6 +772,7 @@ gst_m3u8_client_new (const gchar * uri, const gchar * base_uri)
client->sequence = -1;
client->sequence_position = 0;
client->update_failed_count = 0;
+ client->highest_sequence_number = -1;
g_mutex_init (&client->lock);
gst_m3u8_set_uri (client->main, g_strdup (uri), g_strdup (base_uri), NULL);
@@ -781,7 +814,7 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
GST_M3U8_CLIENT_LOCK (self);
m3u8 = self->current ? self->current : self->main;
- if (!gst_m3u8_update (m3u8, data, &updated))
+ if (!gst_m3u8_update (self, m3u8, data, &updated))
goto out;
if (!updated) {
@@ -1228,3 +1261,43 @@ gst_m3u8_client_get_current_fragment_duration (GstM3U8Client * client)
GST_M3U8_CLIENT_UNLOCK (client);
return dur;
}
+
+gboolean
+gst_m3u8_client_get_seek_range (GstM3U8Client * client, gint64 * start,
+ gint64 * stop)
+{
+ GstClockTime duration = 0;
+ GList *walk;
+ GstM3U8MediaFile *file;
+ guint count;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+
+ if (client->current == NULL || client->current->files == NULL) {
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+
+ count = g_list_length (client->current->files);
+
+ /* count is used to make sure the seek range is never closer than
+ GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE fragments from the end of the
+ playlist - see 6.3.3. "Playing the Playlist file" of the HLS draft */
+ for (walk = client->current->files;
+ walk && count >= GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE; walk = walk->next) {
+ file = walk->data;
+ --count;
+ duration += file->duration;
+ }
+
+ if (duration <= 0) {
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+ *start = client->first_file_start;
+ *stop = *start + duration;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return TRUE;
+}