mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
dashdemux: include both Period start and presentationTimeOffset in segment start
The start of each segment is relative to the Period start, minus the presentation time offset. As specified in section 5.3.9.6 of the MPEG DASH specification: The value of the @t attribute minus the value of the @presentationTimeOffset specifies the MPD start time of the first Segment in the series. dashdemux was not taking account of presentationTimeOffset and in some methods was not taking into account the Period start time. This commit modifies the segment->start value to always be relative to the MPD start time (zero for VOD, availabilityStartTime for live streams). This makes all uses of the segment list consistent. Fixes #841
This commit is contained in:
parent
51ea6ec6b7
commit
a11f7ed924
2 changed files with 130 additions and 26 deletions
|
@ -4021,7 +4021,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
GST_DEBUG ("No useful SegmentList node for the current Representation");
|
||||
/* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
|
||||
if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
|
||||
PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
|
||||
PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
|
@ -4036,14 +4036,22 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
/* build segment list */
|
||||
i = stream->cur_segment_list->MultSegBaseType->startNumber;
|
||||
start = 0;
|
||||
start_time = 0;
|
||||
start_time = PeriodStart;
|
||||
|
||||
GST_LOG ("Building media segment list using a SegmentList node");
|
||||
if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
|
||||
GstSegmentTimelineNode *timeline;
|
||||
GstSNode *S;
|
||||
GList *list;
|
||||
GstClockTime presentationTimeOffset;
|
||||
GstSegmentBaseType *segbase;
|
||||
|
||||
segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
|
||||
presentationTimeOffset =
|
||||
gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
|
||||
segbase->timescale);
|
||||
GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
|
||||
presentationTimeOffset);
|
||||
timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
|
||||
for (list = g_queue_peek_head_link (&timeline->S); list;
|
||||
list = g_list_next (list)) {
|
||||
|
@ -4058,7 +4066,8 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
|
||||
if (S->t > 0) {
|
||||
start = S->t;
|
||||
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
|
||||
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
|
||||
+ PeriodStart - presentationTimeOffset;
|
||||
}
|
||||
|
||||
if (!SegmentURL) {
|
||||
|
@ -4115,8 +4124,15 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
GstClockTime presentationTimeOffset;
|
||||
GstMultSegmentBaseType *mult_seg =
|
||||
stream->cur_seg_template->MultSegBaseType;
|
||||
|
||||
presentationTimeOffset =
|
||||
gst_util_uint64_scale (mult_seg->SegBaseType->presentationTimeOffset,
|
||||
GST_SECOND, mult_seg->SegBaseType->timescale);
|
||||
GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (presentationTimeOffset));
|
||||
/* build segment list */
|
||||
i = mult_seg->startNumber;
|
||||
start = 0;
|
||||
|
@ -4143,7 +4159,8 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
|
||||
if (S->t > 0) {
|
||||
start = S->t;
|
||||
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
|
||||
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
|
||||
+ PeriodStart - presentationTimeOffset;
|
||||
}
|
||||
|
||||
if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
|
||||
|
@ -4170,13 +4187,12 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
|||
GstMediaSegment *media_segment =
|
||||
g_ptr_array_index (stream->segments, n);
|
||||
if (media_segment) {
|
||||
if (media_segment->start + media_segment->duration >
|
||||
PeriodEnd - PeriodStart) {
|
||||
GstClockTime stop = PeriodEnd - PeriodStart;
|
||||
if (media_segment->start + media_segment->duration > PeriodEnd) {
|
||||
GstClockTime stop = PeriodEnd;
|
||||
if (n < stream->segments->len - 1) {
|
||||
GstMediaSegment *next_segment =
|
||||
g_ptr_array_index (stream->segments, n + 1);
|
||||
if (next_segment && next_segment->start < PeriodEnd - PeriodStart)
|
||||
if (next_segment && next_segment->start < PeriodEnd)
|
||||
stop = next_segment->start;
|
||||
}
|
||||
media_segment->duration =
|
||||
|
@ -5015,6 +5031,11 @@ gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
|
|||
*ts = stream_period->start + stream_period->duration;
|
||||
} else {
|
||||
segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
|
||||
if (segment_idx >= stream->segments->len) {
|
||||
GST_WARNING ("Segment index %d is outside of segment list of length %d",
|
||||
segment_idx, stream->segments->len);
|
||||
return FALSE;
|
||||
}
|
||||
currentChunk = g_ptr_array_index (stream->segments, segment_idx);
|
||||
|
||||
if (currentChunk->repeat >= 0) {
|
||||
|
@ -5979,14 +6000,18 @@ gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
|
|||
{
|
||||
GstDateTime *availability_start_time, *rv;
|
||||
gint seg_idx;
|
||||
GstStreamPeriod *stream_period;
|
||||
GstMediaSegment *segment;
|
||||
GstClockTime segmentEndTime;
|
||||
const GstStreamPeriod *stream_period;
|
||||
GstClockTime period_start = 0;
|
||||
|
||||
g_return_val_if_fail (client != NULL, NULL);
|
||||
g_return_val_if_fail (stream != NULL, NULL);
|
||||
|
||||
stream_period = gst_mpdparser_get_stream_period (client);
|
||||
if (stream_period && stream_period->period) {
|
||||
period_start = stream_period->start;
|
||||
}
|
||||
|
||||
seg_idx = stream->segment_index;
|
||||
|
||||
|
@ -6001,16 +6026,15 @@ gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
|
|||
g_ptr_array_index (stream->segments, seg_idx + 1);
|
||||
segmentEndTime = next_segment->start;
|
||||
} else {
|
||||
const GstStreamPeriod *stream_period;
|
||||
stream_period = gst_mpdparser_get_stream_period (client);
|
||||
segmentEndTime = stream_period->start + stream_period->duration;
|
||||
g_return_val_if_fail (stream_period != NULL, NULL);
|
||||
segmentEndTime = period_start + stream_period->duration;
|
||||
}
|
||||
} else {
|
||||
GstClockTime seg_duration;
|
||||
seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
|
||||
if (seg_duration == 0)
|
||||
return NULL;
|
||||
segmentEndTime = (1 + seg_idx) * seg_duration;
|
||||
segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
|
||||
}
|
||||
|
||||
availability_start_time = gst_mpd_client_get_availability_start_time (client);
|
||||
|
@ -6019,19 +6043,6 @@ gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (stream_period && stream_period->period) {
|
||||
GstDateTime *t =
|
||||
gst_mpd_client_add_time_difference (availability_start_time,
|
||||
stream_period->start / GST_USECOND);
|
||||
gst_date_time_unref (availability_start_time);
|
||||
availability_start_time = t;
|
||||
|
||||
if (availability_start_time == NULL) {
|
||||
GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
rv = gst_mpd_client_add_time_difference (availability_start_time,
|
||||
segmentEndTime / GST_USECOND);
|
||||
gst_date_time_unref (availability_start_time);
|
||||
|
|
|
@ -838,6 +838,97 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplate)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
/*
|
||||
* Test parsing Period SegmentTemplate attributes where a
|
||||
* presentationTimeOffset attribute has been specified
|
||||
*
|
||||
*/
|
||||
GST_START_TEST (dash_mpdparser_period_segmentTemplateWithPresentationTimeOffset)
|
||||
{
|
||||
const gchar *xml =
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\""
|
||||
" profiles=\"urn:mpeg:dash:profile:isoff-main:2011\">"
|
||||
" <Period start=\"PT1M\" duration=\"PT40S\">"
|
||||
" <AdaptationSet"
|
||||
" bitstreamSwitching=\"false\""
|
||||
" mimeType=\"video/mp4\""
|
||||
" contentType=\"video\">"
|
||||
" <SegmentTemplate media=\"$RepresentationID$/TestMedia-$Time$.mp4\""
|
||||
" index=\"$RepresentationID$/TestIndex.mp4\""
|
||||
" timescale=\"100\""
|
||||
" presentationTimeOffset=\"6000\""
|
||||
" initialization=\"$RepresentationID$/TestInitialization\""
|
||||
" bitstreamSwitching=\"true\">"
|
||||
" <SegmentTimeline>"
|
||||
" <S d=\"400\" r=\"9\" t=\"100\"/>"
|
||||
" </SegmentTimeline></SegmentTemplate>"
|
||||
" <Representation bandwidth=\"95866\" frameRate=\"90000/3600\""
|
||||
" id=\"vrep\" /></AdaptationSet></Period></MPD>";
|
||||
|
||||
gboolean ret;
|
||||
GList *adaptationSets;
|
||||
GstAdaptationSetNode *adapt_set;
|
||||
GstActiveStream *activeStream;
|
||||
GstMediaFragmentInfo fragment;
|
||||
GstClockTime expectedDuration;
|
||||
GstClockTime expectedTimestamp;
|
||||
GstMpdClient *mpdclient;
|
||||
GstPeriodNode *periodNode;
|
||||
GstSegmentTemplateNode *segmentTemplate;
|
||||
|
||||
mpdclient = gst_mpd_client_new ();
|
||||
ret = gst_mpd_parse (mpdclient, xml, (gint) strlen (xml));
|
||||
assert_equals_int (ret, TRUE);
|
||||
|
||||
ret =
|
||||
gst_mpd_client_setup_media_presentation (mpdclient, GST_CLOCK_TIME_NONE,
|
||||
-1, NULL);
|
||||
assert_equals_int (ret, TRUE);
|
||||
|
||||
periodNode =
|
||||
(GstPeriodNode *) g_list_nth_data (mpdclient->mpd_node->Periods, 0);
|
||||
fail_if (periodNode == NULL);
|
||||
|
||||
/* get the list of adaptation sets of the first period */
|
||||
adaptationSets = gst_mpd_client_get_adaptation_sets (mpdclient);
|
||||
fail_if (adaptationSets == NULL);
|
||||
|
||||
/* setup streaming from the first adaptation set */
|
||||
adapt_set = (GstAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
|
||||
fail_if (adapt_set == NULL);
|
||||
ret = gst_mpd_client_setup_streaming (mpdclient, adapt_set);
|
||||
assert_equals_int (ret, TRUE);
|
||||
activeStream = gst_mpdparser_get_active_stream_by_index (mpdclient, 0);
|
||||
fail_if (activeStream == NULL);
|
||||
|
||||
segmentTemplate = adapt_set->SegmentTemplate;
|
||||
fail_if (segmentTemplate == NULL);
|
||||
assert_equals_string (segmentTemplate->media,
|
||||
"$RepresentationID$/TestMedia-$Time$.mp4");
|
||||
assert_equals_string (segmentTemplate->index,
|
||||
"$RepresentationID$/TestIndex.mp4");
|
||||
assert_equals_string (segmentTemplate->initialization,
|
||||
"$RepresentationID$/TestInitialization");
|
||||
assert_equals_string (segmentTemplate->bitstreamSwitching, "true");
|
||||
|
||||
ret = gst_mpd_client_get_next_fragment (mpdclient, 0, &fragment);
|
||||
assert_equals_int (ret, TRUE);
|
||||
expectedDuration = duration_to_ms (0, 0, 0, 0, 0, 4, 0);
|
||||
/* start = Period@start + S@t - presentationTimeOffset */
|
||||
expectedTimestamp = duration_to_ms (0, 0, 0, 0, 0, 1, 0);
|
||||
assert_equals_uint64 (fragment.duration, expectedDuration * GST_MSECOND);
|
||||
assert_equals_uint64 (fragment.timestamp, expectedTimestamp * GST_MSECOND);
|
||||
/* the $Time$ expansion uses the @t value, without including
|
||||
Period@start or presentationTimeOffset */
|
||||
assert_equals_string (fragment.uri, "/vrep/TestMedia-100.mp4");
|
||||
gst_media_fragment_info_clear (&fragment);
|
||||
|
||||
gst_mpd_client_free (mpdclient);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
/*
|
||||
* Test parsing Period SegmentTemplate MultipleSegmentBaseType attributes
|
||||
*
|
||||
|
@ -5882,6 +5973,8 @@ dash_suite (void)
|
|||
dash_mpdparser_period_segmentList_multipleSegmentBaseType_bitstreamSwitching);
|
||||
tcase_add_test (tc_simpleMPD, dash_mpdparser_period_segmentList_segmentURL);
|
||||
tcase_add_test (tc_simpleMPD, dash_mpdparser_period_segmentTemplate);
|
||||
tcase_add_test (tc_simpleMPD,
|
||||
dash_mpdparser_period_segmentTemplateWithPresentationTimeOffset);
|
||||
tcase_add_test (tc_simpleMPD,
|
||||
dash_mpdparser_period_segmentTemplate_multipleSegmentBaseType);
|
||||
tcase_add_test (tc_simpleMPD,
|
||||
|
|
Loading…
Reference in a new issue