hlsdemux2: m3u8: Use PDT to offset stream time when aligning playlist

When matching segments across playlists with Program-Date-Times,
use the difference in segment PDTs to adjust the stream time
that's being transferred. This can fix cases where the
segment boundaries don't align across different streams
and the first download gets thrown away once the PTS
is seen and found not to match.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3309>
This commit is contained in:
Jan Schmidt 2022-10-27 23:57:58 +11:00 committed by GStreamer Marge Bot
parent 6af3769511
commit 4a8805ade7

View file

@ -979,12 +979,13 @@ gst_hls_media_playlist_find_by_uri (GstHLSMediaPlaylist * playlist,
*/ */
static GstM3U8MediaSegment * static GstM3U8MediaSegment *
find_segment_in_playlist (GstHLSMediaPlaylist * playlist, find_segment_in_playlist (GstHLSMediaPlaylist * playlist,
GstM3U8MediaSegment * segment, gboolean * is_before) GstM3U8MediaSegment * segment, gboolean * is_before, gboolean * matched_pdt)
{ {
GstM3U8MediaSegment *res = NULL; GstM3U8MediaSegment *res = NULL;
guint idx; guint idx;
*is_before = FALSE; *is_before = FALSE;
*matched_pdt = FALSE;
/* The easy one. Happens when stream times need to be re-synced in an existing /* The easy one. Happens when stream times need to be re-synced in an existing
* playlist */ * playlist */
@ -1027,6 +1028,7 @@ find_segment_in_playlist (GstHLSMediaPlaylist * playlist,
g_ptr_array_insert (playlist->segments, 0, g_ptr_array_insert (playlist->segments, 0,
gst_m3u8_media_segment_ref (segment)); gst_m3u8_media_segment_ref (segment));
*is_before = TRUE; *is_before = TRUE;
*matched_pdt = TRUE;
return segment; return segment;
} }
if (ddiff > 0) { if (ddiff > 0) {
@ -1039,6 +1041,7 @@ find_segment_in_playlist (GstHLSMediaPlaylist * playlist,
if (cand->datetime if (cand->datetime
&& g_date_time_difference (cand->datetime, segment->datetime) >= 0) { && g_date_time_difference (cand->datetime, segment->datetime) >= 0) {
GST_DEBUG ("Picking by date time"); GST_DEBUG ("Picking by date time");
*matched_pdt = TRUE;
return cand; return cand;
} }
} }
@ -1122,7 +1125,8 @@ gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * playlist,
GST_TIME_ARGS (segment->duration), segment->sequence, GST_TIME_ARGS (segment->duration), segment->sequence,
segment->discont_sequence, segment->uri, playlist->uri); segment->discont_sequence, segment->uri, playlist->uri);
res = find_segment_in_playlist (playlist, segment, &is_before); gboolean matched_pdt = FALSE;
res = find_segment_in_playlist (playlist, segment, &is_before, &matched_pdt);
/* For live playlists we re-calculate all stream times based on the existing /* For live playlists we re-calculate all stream times based on the existing
* stream time. Non-live playlists have their stream time calculated at * stream time. Non-live playlists have their stream time calculated at
@ -1130,8 +1134,26 @@ gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * playlist,
if (res) { if (res) {
if (!is_before) if (!is_before)
gst_m3u8_media_segment_ref (res); gst_m3u8_media_segment_ref (res);
if (res->stream_time == GST_CLOCK_STIME_NONE) if (res->stream_time == GST_CLOCK_STIME_NONE) {
res->stream_time = segment->stream_time; GstClockTimeDiff stream_time_offset = 0;
/* If there is a PDT on both segments, adjust the stream time
* by the difference to align them precisely (hopefully).
*/
if (matched_pdt) {
/* If matched_pdt is TRUE, there must be PDT present in both segments */
g_assert (res->datetime);
g_assert (segment->datetime);
stream_time_offset =
g_date_time_difference (res->datetime,
segment->datetime) * GST_USECOND;
GST_DEBUG ("Transferring stream time %" GST_STIMEP_FORMAT
" adjusted by PDT offset %" GST_STIMEP_FORMAT,
&segment->stream_time, &stream_time_offset);
}
res->stream_time = segment->stream_time + stream_time_offset;
}
if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist)) if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist))
gst_hls_media_playlist_recalculate_stream_time (playlist, res); gst_hls_media_playlist_recalculate_stream_time (playlist, res);
/* If the playlist didn't specify a reference discont sequence number, we /* If the playlist didn't specify a reference discont sequence number, we
@ -1206,6 +1228,7 @@ gst_hls_media_playlist_sync_to_playlist (GstHLSMediaPlaylist * playlist,
GstM3U8MediaSegment *cand = NULL; GstM3U8MediaSegment *cand = NULL;
guint idx; guint idx;
gboolean is_before; gboolean is_before;
gboolean matched_pdt = FALSE;
g_return_val_if_fail (playlist && reference, FALSE); g_return_val_if_fail (playlist && reference, FALSE);
@ -1215,7 +1238,7 @@ retry_without_dsn:
* go backwards */ * go backwards */
for (idx = reference->segments->len - 1; idx; idx--) { for (idx = reference->segments->len - 1; idx; idx--) {
cand = g_ptr_array_index (reference->segments, idx); cand = g_ptr_array_index (reference->segments, idx);
res = find_segment_in_playlist (playlist, cand, &is_before); res = find_segment_in_playlist (playlist, cand, &is_before, &matched_pdt);
if (res) if (res)
break; break;
} }
@ -1223,7 +1246,7 @@ retry_without_dsn:
if (res == NULL) { if (res == NULL) {
if (playlist->has_ext_x_dsn) { if (playlist->has_ext_x_dsn) {
/* There is a possibility that the server doesn't have coherent DSN /* There is a possibility that the server doesn't have coherent DSN
* accross variants/renditions. If we reach this section, this means that * across variants/renditions. If we reach this section, this means that
* we have already attempted matching by PDT, URI, stream time. The last * we have already attempted matching by PDT, URI, stream time. The last
* matching would have been by MSN/DSN, therefore try it again without * matching would have been by MSN/DSN, therefore try it again without
* taking DSN into account. */ * taking DSN into account. */
@ -1236,8 +1259,26 @@ retry_without_dsn:
} }
/* Carry over reference stream time */ /* Carry over reference stream time */
if (res->stream_time == GST_CLOCK_STIME_NONE) if (res->stream_time == GST_CLOCK_STIME_NONE) {
res->stream_time = cand->stream_time; GstClockTimeDiff stream_time_offset = 0;
/* If there is a PDT on both segments, adjust the stream time
* by the difference to align them precisely (hopefully).
*/
if (matched_pdt) {
/* If matched_pdt is TRUE, there must be PDT present in both segments */
g_assert (playlist->ext_x_pdt_present && res->datetime);
g_assert (reference->ext_x_pdt_present && cand->datetime);
stream_time_offset =
g_date_time_difference (res->datetime, cand->datetime) * GST_USECOND;
GST_DEBUG ("Transferring stream time %" GST_STIMEP_FORMAT
" adjusted by PDT offset %" GST_STIMEP_FORMAT, &cand->stream_time,
&stream_time_offset);
}
res->stream_time = cand->stream_time + stream_time_offset;
}
if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist)) if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist))
gst_hls_media_playlist_recalculate_stream_time (playlist, res); gst_hls_media_playlist_recalculate_stream_time (playlist, res);
/* If the playlist didn't specify a reference discont sequence number, we /* If the playlist didn't specify a reference discont sequence number, we