mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
hlsdemux: Implement trick modes via I-frame variant lists
This commit is contained in:
parent
77231ab957
commit
91ec00a0c0
3 changed files with 109 additions and 36 deletions
|
@ -114,6 +114,9 @@ static gboolean gst_hls_demux_set_location (GstHLSDemux * demux,
|
|||
const gchar * uri);
|
||||
static gchar *gst_hls_src_buf_to_utf8_playlist (GstBuffer * buf);
|
||||
|
||||
static gboolean gst_hls_demux_change_playlist (GstHLSDemux * demux,
|
||||
guint max_bitrate);
|
||||
|
||||
#define gst_hls_demux_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstHLSDemux, gst_hls_demux, GST_TYPE_ELEMENT);
|
||||
|
||||
|
@ -332,7 +335,7 @@ gst_hls_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|||
gint64 start, stop;
|
||||
GList *walk;
|
||||
GstClockTime current_pos, target_pos;
|
||||
gint current_sequence;
|
||||
gint64 current_sequence;
|
||||
GstM3U8MediaFile *file;
|
||||
|
||||
GST_INFO_OBJECT (demux, "Received GST_EVENT_SEEK");
|
||||
|
@ -351,15 +354,67 @@ gst_hls_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if ((rate > 1.0 || rate < -1.0) && (!demux->client->main
|
||||
|| !demux->client->main->iframe_lists)) {
|
||||
GST_ERROR_OBJECT (demux,
|
||||
"Trick modes only allowed for streams with I-frame lists");
|
||||
gst_event_unref (event);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT
|
||||
" stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
|
||||
GST_TIME_ARGS (stop));
|
||||
|
||||
gst_hls_demux_pause_tasks (demux);
|
||||
|
||||
/* wait for streaming to finish */
|
||||
g_rec_mutex_lock (&demux->updates_lock);
|
||||
g_rec_mutex_unlock (&demux->updates_lock);
|
||||
|
||||
g_rec_mutex_lock (&demux->stream_lock);
|
||||
|
||||
/* Use I-frame variants for trick modes */
|
||||
if ((rate > 1.0 || rate < -1.0) && demux->segment.rate >= -1.0
|
||||
&& demux->segment.rate <= 1.0) {
|
||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||
/* Switch to I-frame variant */
|
||||
demux->client->main->current_variant =
|
||||
demux->client->main->iframe_lists;
|
||||
GST_M3U8_CLIENT_UNLOCK (demux->client);
|
||||
gst_m3u8_client_set_current (demux->client,
|
||||
demux->client->main->iframe_lists->data);
|
||||
|
||||
gst_uri_downloader_reset (demux->downloader);
|
||||
gst_hls_demux_update_playlist (demux, FALSE, NULL);
|
||||
demux->discont = TRUE;
|
||||
demux->do_typefind = TRUE;
|
||||
|
||||
gst_hls_demux_change_playlist (demux,
|
||||
demux->current_download_rate * demux->bitrate_limit / ABS (rate));
|
||||
} else if (rate > -1.0 && rate <= 1.0 && (demux->segment.rate < -1.0
|
||||
|| demux->segment.rate > 1.0)) {
|
||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||
/* Switch to normal variant */
|
||||
demux->client->main->current_variant = demux->client->main->lists;
|
||||
GST_M3U8_CLIENT_UNLOCK (demux->client);
|
||||
gst_m3u8_client_set_current (demux->client,
|
||||
demux->client->main->lists->data);
|
||||
|
||||
gst_uri_downloader_reset (demux->downloader);
|
||||
gst_hls_demux_update_playlist (demux, FALSE, NULL);
|
||||
demux->discont = TRUE;
|
||||
demux->do_typefind = TRUE;
|
||||
|
||||
gst_hls_demux_change_playlist (demux,
|
||||
demux->current_download_rate * demux->bitrate_limit);
|
||||
}
|
||||
|
||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||
file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data);
|
||||
current_sequence = file->sequence;
|
||||
current_pos = 0;
|
||||
target_pos = (GstClockTime) start;
|
||||
target_pos = rate > 0 ? start : stop;
|
||||
/* FIXME: Here we need proper discont handling */
|
||||
for (walk = demux->client->current->files; walk; walk = walk->next) {
|
||||
file = walk->data;
|
||||
|
@ -383,16 +438,9 @@ gst_hls_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|||
gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ());
|
||||
}
|
||||
|
||||
gst_hls_demux_pause_tasks (demux);
|
||||
|
||||
/* wait for streaming to finish */
|
||||
g_rec_mutex_lock (&demux->updates_lock);
|
||||
g_rec_mutex_unlock (&demux->updates_lock);
|
||||
|
||||
g_rec_mutex_lock (&demux->stream_lock);
|
||||
|
||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||
GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence);
|
||||
GST_DEBUG_OBJECT (demux, "seeking to sequence %u",
|
||||
(guint) current_sequence);
|
||||
demux->client->sequence = current_sequence;
|
||||
demux->client->sequence_position = current_pos;
|
||||
GST_M3U8_CLIENT_UNLOCK (demux->client);
|
||||
|
@ -768,7 +816,7 @@ gst_hls_demux_stream_loop (GstHLSDemux * demux)
|
|||
|
||||
/* Got a new fragment or not live anymore? */
|
||||
if (gst_m3u8_client_get_next_fragment (demux->client, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL)
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, demux->segment.rate > 0)
|
||||
|| !gst_m3u8_client_is_live (demux->client))
|
||||
break;
|
||||
|
||||
|
@ -809,7 +857,7 @@ gst_hls_demux_stream_loop (GstHLSDemux * demux)
|
|||
}
|
||||
} else {
|
||||
demux->download_failed_count = 0;
|
||||
gst_m3u8_client_advance_fragment (demux->client);
|
||||
gst_m3u8_client_advance_fragment (demux->client, demux->segment.rate > 0);
|
||||
|
||||
if (demux->stop_updates_task) {
|
||||
g_object_unref (fragment);
|
||||
|
@ -1135,7 +1183,7 @@ gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
|
|||
* three fragments before the end of the list */
|
||||
if (updated && update == FALSE && demux->client->current &&
|
||||
gst_m3u8_client_is_live (demux->client)) {
|
||||
guint last_sequence;
|
||||
gint64 last_sequence;
|
||||
|
||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||
last_sequence =
|
||||
|
@ -1143,8 +1191,8 @@ gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
|
|||
data)->sequence;
|
||||
|
||||
if (demux->client->sequence >= last_sequence - 3) {
|
||||
GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %d",
|
||||
last_sequence - 3);
|
||||
GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %u",
|
||||
(guint) (last_sequence - 3));
|
||||
demux->need_segment = TRUE;
|
||||
demux->client->sequence = last_sequence - 3;
|
||||
}
|
||||
|
@ -1211,7 +1259,11 @@ retry_failover_protection:
|
|||
GST_M3U8_CLIENT_UNLOCK (demux->client);
|
||||
gst_m3u8_client_set_current (demux->client, previous_variant->data);
|
||||
/* Try a lower bitrate (or stop if we just tried the lowest) */
|
||||
if (new_bandwidth ==
|
||||
if (GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
||||
GST_M3U8 (g_list_first (demux->client->main->iframe_lists)->
|
||||
data)->bandwidth)
|
||||
return FALSE;
|
||||
else if (!GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
||||
GST_M3U8 (g_list_first (demux->client->main->lists)->data)->bandwidth)
|
||||
return FALSE;
|
||||
else
|
||||
|
@ -1421,7 +1473,7 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux,
|
|||
*end_of_playlist = FALSE;
|
||||
if (!gst_m3u8_client_get_next_fragment (demux->client, &discont,
|
||||
&next_fragment_uri, &duration, ×tamp, &range_start, &range_end,
|
||||
&key, &iv)) {
|
||||
&key, &iv, demux->segment.rate > 0)) {
|
||||
GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments");
|
||||
*end_of_playlist = TRUE;
|
||||
return NULL;
|
||||
|
|
|
@ -558,7 +558,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
|||
data = g_utf8_next_char (end); /* skip \n */
|
||||
}
|
||||
|
||||
/* redorder playlists by bitrate */
|
||||
/* reorder playlists by bitrate */
|
||||
if (self->lists) {
|
||||
gchar *top_variant_uri = NULL;
|
||||
gboolean iframe = FALSE;
|
||||
|
@ -670,7 +670,7 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
|
|||
self->sequence =
|
||||
GST_M3U8_MEDIA_FILE (g_list_first (m3u8->files)->data)->sequence;
|
||||
self->sequence_position = 0;
|
||||
GST_DEBUG ("Setting first sequence at %d", self->sequence);
|
||||
GST_DEBUG ("Setting first sequence at %u", (guint) self->sequence);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
@ -688,17 +688,26 @@ _find_current (GstM3U8MediaFile * file, GstM3U8Client * client)
|
|||
static gboolean
|
||||
_find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
|
||||
{
|
||||
GST_DEBUG ("Found fragment %d", file->sequence);
|
||||
GST_DEBUG ("Found fragment %u", (guint) file->sequence);
|
||||
if (file->sequence >= client->sequence)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_find_previous (GstM3U8MediaFile * file, GstM3U8Client * client)
|
||||
{
|
||||
GST_DEBUG ("Found fragment %u", (guint) file->sequence);
|
||||
if (file->sequence <= client->sequence)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
||||
GstClockTime * timestamp, gint64 * range_start, gint64 * range_end,
|
||||
const gchar ** key, const guint8 ** iv)
|
||||
const gchar ** key, const guint8 ** iv, gboolean forward)
|
||||
{
|
||||
GList *l;
|
||||
GstM3U8MediaFile *file;
|
||||
|
@ -707,9 +716,13 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
|||
g_return_val_if_fail (client->current != NULL, FALSE);
|
||||
|
||||
GST_M3U8_CLIENT_LOCK (client);
|
||||
GST_DEBUG ("Looking for fragment %d", client->sequence);
|
||||
GST_DEBUG ("Looking for fragment %" G_GINT64_FORMAT, client->sequence);
|
||||
if (client->sequence < 0) {
|
||||
GST_M3U8_CLIENT_UNLOCK (client);
|
||||
return FALSE;
|
||||
}
|
||||
l = g_list_find_custom (client->current->files, client,
|
||||
(GCompareFunc) _find_next);
|
||||
(GCompareFunc) (forward ? _find_next : _find_previous));
|
||||
if (l == NULL) {
|
||||
GST_M3U8_CLIENT_UNLOCK (client);
|
||||
return FALSE;
|
||||
|
@ -717,7 +730,7 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
|||
|
||||
file = GST_M3U8_MEDIA_FILE (l->data);
|
||||
GST_DEBUG ("Got fragment with sequence %u (client sequence %u)",
|
||||
file->sequence, client->sequence);
|
||||
(guint) file->sequence, (guint) client->sequence);
|
||||
|
||||
if (timestamp)
|
||||
*timestamp = client->sequence_position;
|
||||
|
@ -742,7 +755,7 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
|||
}
|
||||
|
||||
void
|
||||
gst_m3u8_client_advance_fragment (GstM3U8Client * client)
|
||||
gst_m3u8_client_advance_fragment (GstM3U8Client * client, gboolean forward)
|
||||
{
|
||||
GList *l;
|
||||
GstM3U8MediaFile *file;
|
||||
|
@ -751,9 +764,9 @@ gst_m3u8_client_advance_fragment (GstM3U8Client * client)
|
|||
g_return_if_fail (client->current != NULL);
|
||||
|
||||
GST_M3U8_CLIENT_LOCK (client);
|
||||
GST_DEBUG ("Looking for fragment %d", client->sequence);
|
||||
GST_DEBUG ("Looking for fragment %" G_GINT64_FORMAT, client->sequence);
|
||||
l = g_list_find_custom (client->current->files, client,
|
||||
(GCompareFunc) _find_next);
|
||||
(GCompareFunc) _find_current);
|
||||
if (l == NULL) {
|
||||
GST_ERROR ("Could not find current fragment");
|
||||
GST_M3U8_CLIENT_UNLOCK (client);
|
||||
|
@ -761,9 +774,17 @@ gst_m3u8_client_advance_fragment (GstM3U8Client * client)
|
|||
}
|
||||
|
||||
file = GST_M3U8_MEDIA_FILE (l->data);
|
||||
GST_DEBUG ("Advancing from sequence %u", file->sequence);
|
||||
client->sequence = file->sequence + 1;
|
||||
client->sequence_position += file->duration;
|
||||
GST_DEBUG ("Advancing from sequence %u", (guint) file->sequence);
|
||||
if (forward) {
|
||||
client->sequence = file->sequence + 1;
|
||||
client->sequence_position += file->duration;
|
||||
} else {
|
||||
client->sequence = file->sequence - 1;
|
||||
if (client->sequence_position > file->duration)
|
||||
client->sequence_position -= file->duration;
|
||||
else
|
||||
client->sequence_position = 0;
|
||||
}
|
||||
GST_M3U8_CLIENT_UNLOCK (client);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ struct _GstM3U8
|
|||
GList *iframe_lists; /* I-frame lists from the main playlist */
|
||||
GList *current_variant; /* Current variant playlist used */
|
||||
GstM3U8 *parent; /* main playlist (if any) */
|
||||
guint mediasequence; /* EXT-X-MEDIA-SEQUENCE & increased with new media file */
|
||||
gint64 mediasequence; /* EXT-X-MEDIA-SEQUENCE & increased with new media file */
|
||||
};
|
||||
|
||||
struct _GstM3U8MediaFile
|
||||
|
@ -67,7 +67,7 @@ struct _GstM3U8MediaFile
|
|||
gchar *title;
|
||||
GstClockTime duration;
|
||||
gchar *uri;
|
||||
guint sequence; /* the sequence nb of this file */
|
||||
gint64 sequence; /* the sequence nb of this file */
|
||||
gboolean discont; /* this file marks a discontinuity */
|
||||
gchar *key;
|
||||
guint8 iv[16];
|
||||
|
@ -79,7 +79,7 @@ struct _GstM3U8Client
|
|||
GstM3U8 *main; /* main playlist */
|
||||
GstM3U8 *current;
|
||||
guint update_failed_count;
|
||||
gint sequence; /* the next sequence for this client */
|
||||
gint64 sequence; /* the next sequence for this client */
|
||||
GstClockTime sequence_position; /* position of this sequence */
|
||||
GMutex lock;
|
||||
};
|
||||
|
@ -92,8 +92,8 @@ void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8);
|
|||
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
||||
GstClockTime * timestamp, gint64 * range_start, gint64 * range_end,
|
||||
const gchar ** key, const guint8 ** iv);
|
||||
void gst_m3u8_client_advance_fragment (GstM3U8Client * client);
|
||||
const gchar ** key, const guint8 ** iv, gboolean forward);
|
||||
void gst_m3u8_client_advance_fragment (GstM3U8Client * client, gboolean forward);
|
||||
GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client);
|
||||
GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client);
|
||||
const gchar *gst_m3u8_client_get_uri(GstM3U8Client * client);
|
||||
|
|
Loading…
Reference in a new issue