summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/dash/gstdashdemux.c77
-rw-r--r--ext/dash/gstdashdemux.h2
-rw-r--r--ext/dash/gstmpdparser.c85
-rw-r--r--ext/dash/gstmpdparser.h2
4 files changed, 162 insertions, 4 deletions
diff --git a/ext/dash/gstdashdemux.c b/ext/dash/gstdashdemux.c
index 9b959da2a..bcb260a65 100644
--- a/ext/dash/gstdashdemux.c
+++ b/ext/dash/gstdashdemux.c
@@ -216,8 +216,11 @@ static void gst_dash_demux_resume_stream_task (GstDashDemux * demux);
static void gst_dash_demux_resume_download_task (GstDashDemux * demux);
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux);
static gboolean gst_dash_demux_select_representations (GstDashDemux * demux);
-static gboolean gst_dash_demux_get_next_fragment (GstDashDemux * demux);
+static gboolean gst_dash_demux_get_next_fragment (GstDashDemux * demux,
+ GstActiveStream ** stream, GstClockTime * next_ts);
static gboolean gst_dash_demux_advance_period (GstDashDemux * demux);
+static void gst_dash_demux_download_wait (GstDashDemux * demux,
+ GstClockTime time_diff);
static void gst_dash_demux_expose_streams (GstDashDemux * demux);
static void gst_dash_demux_remove_streams (GstDashDemux * demux,
@@ -254,6 +257,8 @@ gst_dash_demux_dispose (GObject * obj)
g_rec_mutex_clear (&demux->download_task_lock);
demux->download_task = NULL;
}
+ g_cond_clear (&demux->download_cond);
+ g_mutex_clear (&demux->download_mutex);
if (demux->downloader != NULL) {
g_object_unref (demux->downloader);
@@ -342,6 +347,8 @@ gst_dash_demux_init (GstDashDemux * demux)
gst_task_new ((GstTaskFunction) gst_dash_demux_download_loop, demux,
NULL);
gst_task_set_lock (demux->download_task, &demux->download_task_lock);
+ g_cond_init (&demux->download_cond);
+ g_mutex_init (&demux->download_mutex);
/* Streaming task */
g_rec_mutex_init (&demux->stream_task_lock);
@@ -998,6 +1005,9 @@ gst_dash_demux_stop (GstDashDemux * demux)
if (GST_TASK_STATE (demux->download_task) != GST_TASK_STOPPED) {
GST_TASK_SIGNAL (demux->download_task);
gst_task_stop (demux->download_task);
+ g_mutex_lock (&demux->download_mutex);
+ g_cond_signal (&demux->download_cond);
+ g_mutex_unlock (&demux->download_mutex);
g_rec_mutex_lock (&demux->download_task_lock);
g_rec_mutex_unlock (&demux->download_task_lock);
gst_task_join (demux->download_task);
@@ -1457,6 +1467,8 @@ void
gst_dash_demux_download_loop (GstDashDemux * demux)
{
gint64 update_period = demux->client->mpd_node->minimumUpdatePeriod;
+ GstClockTime fragment_ts = GST_CLOCK_TIME_NONE;
+ GstActiveStream *fragment_stream = NULL;
GST_LOG_OBJECT (demux, "Starting download loop");
@@ -1604,7 +1616,8 @@ gst_dash_demux_download_loop (GstDashDemux * demux)
}
/* fetch the next fragment */
- while (!gst_dash_demux_get_next_fragment (demux)) {
+ while (!gst_dash_demux_get_next_fragment (demux, &fragment_stream,
+ &fragment_ts)) {
if (demux->end_of_period) {
GST_INFO_OBJECT (demux, "Reached the end of the Period");
/* setup video, audio and subtitle streams, starting from the next Period */
@@ -1620,7 +1633,43 @@ gst_dash_demux_download_loop (GstDashDemux * demux)
gst_mpd_client_set_segment_index_for_all_streams (demux->client, 0);
demux->end_of_period = FALSE;
} else if (!demux->cancelled) {
- demux->client->update_failed_count++;
+ /* in case this is live, we might be ahead or before playback, so we
+ * either wait or jump ahead */
+ if (gst_mpd_client_is_live (demux->client)) {
+ gint64 time_diff;
+ gint pos;
+
+ pos =
+ gst_mpd_client_check_time_position (demux->client, fragment_stream,
+ fragment_ts, &time_diff);
+ GST_DEBUG_OBJECT (demux,
+ "Checked position for fragment ts %" GST_TIME_FORMAT
+ ", res: %d, diff: %" G_GINT64_FORMAT, GST_TIME_ARGS (fragment_ts),
+ pos, time_diff);
+
+ time_diff *= GST_USECOND;
+ if (pos < 0) {
+ /* we're behind, try moving to the 'present' */
+ GDateTime *now = g_date_time_new_now_utc ();
+
+ GST_DEBUG_OBJECT (demux,
+ "Falling behind live stream, moving forward");
+ gst_mpd_client_seek_to_time (demux->client, now);
+ g_date_time_unref (now);
+
+ } else if (pos > 0) {
+ /* we're ahead, wait a little */
+ gst_mpd_client_set_segment_index (fragment_stream,
+ fragment_stream->segment_idx - 1);
+ gst_dash_demux_download_wait (demux, time_diff);
+ } else {
+ gst_mpd_client_set_segment_index (fragment_stream,
+ fragment_stream->segment_idx - 1);
+ demux->client->update_failed_count++;
+ }
+ } else {
+ demux->client->update_failed_count++;
+ }
if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
GST_WARNING_OBJECT (demux, "Could not fetch the next fragment");
goto quit;
@@ -1920,6 +1969,8 @@ gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream)
/* gst_dash_demux_get_next_fragment:
*
* Get the next fragments for the stream with the earlier timestamp.
+ * It returns the selected timestamp so the caller can deal with
+ * sync issues in case the stream is live.
*
* This function uses the generic URI downloader API.
*
@@ -1927,7 +1978,8 @@ gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream)
*
*/
static gboolean
-gst_dash_demux_get_next_fragment (GstDashDemux * demux)
+gst_dash_demux_get_next_fragment (GstDashDemux * demux,
+ GstActiveStream ** stream, GstClockTime * selected_ts)
{
GstActiveStream *active_stream;
GstFragment *download;
@@ -1985,6 +2037,12 @@ gst_dash_demux_get_next_fragment (GstDashDemux * demux)
gst_dash_demux_stream_push_event (stream, event);
}
}
+ if (selected_ts)
+ *selected_ts = best_time;
+ if (stream && selected_stream)
+ *stream =
+ gst_mpdparser_get_active_stream_by_index (demux->client,
+ selected_stream->index);
/* Get the fragment corresponding to each stream index */
if (selected_stream) {
@@ -2097,3 +2155,14 @@ gst_dash_demux_get_next_fragment (GstDashDemux * demux)
}
return TRUE;
}
+
+static void
+gst_dash_demux_download_wait (GstDashDemux * demux, GstClockTime time_diff)
+{
+ gint64 end_time = g_get_monotonic_time () + time_diff / GST_USECOND;
+
+ GST_DEBUG_OBJECT (demux, "Download waiting for %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time_diff));
+ g_cond_wait_until (&demux->download_cond, &demux->download_mutex, end_time);
+ GST_DEBUG_OBJECT (demux, "Download finished waiting");
+}
diff --git a/ext/dash/gstdashdemux.h b/ext/dash/gstdashdemux.h
index 4b184ef8f..e5496ff75 100644
--- a/ext/dash/gstdashdemux.h
+++ b/ext/dash/gstdashdemux.h
@@ -132,6 +132,8 @@ struct _GstDashDemux
/* Download task */
GstTask *download_task;
GRecMutex download_task_lock;
+ GMutex download_mutex;
+ GCond download_cond;
gboolean cancelled;
/* Manifest update */
diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c
index bc6152f13..7e3563486 100644
--- a/ext/dash/gstmpdparser.c
+++ b/ext/dash/gstmpdparser.c
@@ -4284,6 +4284,91 @@ gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
return nb_adapatation_set;
}
+gint
+gst_mpd_client_check_time_position (GstMpdClient * client,
+ GstActiveStream * stream, GstClockTime ts, gint64 * diff)
+{
+ GDateTime *now = g_date_time_new_now_utc ();
+ GDateTime *start =
+ gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
+ GTimeSpan stream_now;
+ GTimeSpan ts_microseconds;
+ GstClockTime duration;
+
+ g_return_val_if_fail (gst_mpd_client_is_live (client), 0);
+
+ duration = gst_mpd_client_get_segment_duration (client, stream);
+ stream_now = g_date_time_difference (now, start);
+ g_date_time_unref (now);
+ g_date_time_unref (start);
+
+ /* sum duration to check if the segment is fully ready */
+ ts_microseconds = (ts + duration) / GST_USECOND;
+
+ /*
+ * This functions checks if a given ts is in the 'available range' of
+ * a DASH presentation. This only makes sense for live streams, which
+ * are continuously adding new segments and removing old ones.
+ *
+ * Note: Both the client and the server should use UTC as a time reference.
+ *
+ * @ts is the time since the beginning of the stream and we need to find out
+ * if it is currently available. The server should be hosting segments
+ *
+ * * ---------------- ... --- * ----------- * ---- ...
+ * |
+ * | past(unavailable) | | available | future(unavailable yet)
+ * |
+ * * ---------------- ... --- * ----------- * ---- ...
+ * | | |
+ * availabilitStartTime | UTC now
+ * UTC now - timeShiftBufferDepth
+ *
+ * This function should return 0 if @ts is in the 'available' area, 1 for
+ * 'future' and '-1' for past and the corresponding distance to the
+ * 'available' area is set to @diff
+ *
+ * TODO untested with live presentations with multiple periods as no
+ * examples for it could be found/generated
+ */
+
+ if (ts_microseconds > stream_now) {
+ *diff = ts_microseconds - stream_now;
+ return 1;
+ }
+ if (client->mpd_node->timeShiftBufferDepth
+ && ts_microseconds <
+ stream_now - client->mpd_node->timeShiftBufferDepth) {
+ *diff = ts_microseconds - stream_now;
+ return -1;
+ }
+
+ *diff = 0;
+ return 0;
+}
+
+gboolean
+gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
+{
+ GDateTime *start =
+ gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
+ GTimeSpan ts_microseconds;
+ GstClockTime ts;
+ gboolean ret = TRUE;
+ GList *stream;
+
+ g_return_val_if_fail (gst_mpd_client_is_live (client), 0);
+
+ ts_microseconds = g_date_time_difference (time, start);
+ g_date_time_unref (start);
+
+ ts = ts_microseconds * GST_USECOND;
+ for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
+ ret = ret & gst_mpd_client_stream_seek (client, stream->data, ts);
+ }
+ return ret;
+}
+
void
gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
{
diff --git a/ext/dash/gstmpdparser.h b/ext/dash/gstmpdparser.h
index 8a346aaad..562dbec0c 100644
--- a/ext/dash/gstmpdparser.h
+++ b/ext/dash/gstmpdparser.h
@@ -493,8 +493,10 @@ gboolean gst_mpd_client_get_next_header (GstMpdClient *client, gchar **uri, guin
gboolean gst_mpd_client_get_next_header_index (GstMpdClient *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
gboolean gst_mpd_client_is_live (GstMpdClient * client);
gboolean gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, GstClockTime ts);
+gboolean gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time);
GstDateTime *gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs);
gint gst_mpd_client_get_segment_index_at_time (GstMpdClient *client, GstActiveStream * stream, const GstDateTime *time);
+gint gst_mpd_client_check_time_position (GstMpdClient * client, GstActiveStream * stream, GstClockTime ts, gint64 * diff);
/* Period selection */
gboolean gst_mpd_client_set_period_index (GstMpdClient *client, guint period_idx);