diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c index 21f84f119e..309dd51b0a 100644 --- a/ext/dash/gstmpdparser.c +++ b/ext/dash/gstmpdparser.c @@ -131,11 +131,11 @@ static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream * static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template, const gchar * id, guint number, guint bandwidth, guint64 time); static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream, - GstSegmentURLNode * url_node, guint number, guint64 start, - GstClockTime start_time, GstClockTime duration); + GstSegmentURLNode * url_node, guint number, gint repeat, gint64 scale_start, + gint64 scale_duration, GstClockTime start, GstClockTime duration); static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType); static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client, - GstActiveStream * stream); + GstActiveStream * stream, gint64 * scale_duration); static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient * client); @@ -2767,7 +2767,7 @@ gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream, static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client, - GstActiveStream * stream) + GstActiveStream * stream, gint64 * scale_dur) { GstStreamPeriod *stream_period; GstMultSegmentBaseType *base = NULL; @@ -2787,8 +2787,12 @@ gst_mpd_client_get_segment_duration (GstMpdClient * client, if (base == NULL || base->SegBaseType == NULL) { /* this may happen when we have a single segment */ duration = stream_period->duration; + if (scale_dur) + *scale_dur = duration; } else { duration = base->duration * GST_SECOND; + if (scale_dur) + *scale_dur = duration; timescale = base->SegBaseType->timescale; if (timescale > 1) @@ -2916,6 +2920,31 @@ gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream) return stream->baseURL; } +static gboolean +gst_mpdparser_find_segment_by_index (GstMpdClient * client, + GPtrArray * segments, gint index, GstMediaSegment * result) +{ + gint i; + for (i = 0; i < segments->len; i++) { + GstMediaSegment *s; + + s = g_ptr_array_index (segments, i); + if (s->number + s->repeat >= index) { + /* it is in this segment */ + result->SegmentURL = s->SegmentURL; + result->number = index; + result->scale_start = + s->scale_start + (index - s->number) * s->scale_duration; + result->scale_duration = s->scale_duration; + result->start = s->start + (index - s->number) * s->duration; + result->duration = s->duration; + return TRUE; + } + } + + return FALSE; +} + gboolean gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream, guint indexChunk, GstMediaSegment * segment) @@ -2928,26 +2957,20 @@ gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream, stream = g_list_nth_data (client->active_streams, indexStream); g_return_val_if_fail (stream != NULL, FALSE); - if (stream->segments) { - GstMediaSegment *list_segment; - /* fixed list of segments */ - if (indexChunk >= stream->segments->len) - return FALSE; + indexChunk += 1; - list_segment = g_ptr_array_index (stream->segments, indexChunk); - segment->SegmentURL = list_segment->SegmentURL; - segment->number = list_segment->number; - segment->start = list_segment->start; - segment->start_time = list_segment->start_time; - segment->duration = list_segment->duration; + if (stream->segments) { + return gst_mpdparser_find_segment_by_index (client, stream->segments, + indexChunk, segment); } else { GstClockTime duration; GstStreamPeriod *stream_period; + gint64 scale_dur; g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType-> SegmentTimeline == NULL, FALSE); /* segment template generator */ - duration = gst_mpd_client_get_segment_duration (client, stream); + duration = gst_mpd_client_get_segment_duration (client, stream, &scale_dur); if (!GST_CLOCK_TIME_IS_VALID (duration)) return FALSE; @@ -2955,11 +2978,13 @@ gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream, segment->number = indexChunk + stream->cur_seg_template->MultSegBaseType->startNumber; - segment->start_time = duration * indexChunk; + segment->scale_start = indexChunk * scale_dur; + segment->scale_duration = scale_dur; + segment->start = duration * indexChunk; segment->duration = duration; segment->SegmentURL = NULL; - if (segment->start_time >= stream_period->duration) { + if (segment->start >= stream_period->duration) { return FALSE; } } @@ -2968,8 +2993,9 @@ gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream, static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream, - GstSegmentURLNode * url_node, guint number, guint64 start, - GstClockTime start_time, GstClockTime duration) + GstSegmentURLNode * url_node, guint number, gint repeat, + gint64 scale_start, gint64 scale_duration, + GstClockTime start, GstClockTime duration) { GstMediaSegment *media_segment; @@ -2983,15 +3009,17 @@ gst_mpd_client_add_media_segment (GstActiveStream * stream, media_segment->SegmentURL = url_node; media_segment->number = number; + media_segment->scale_start = scale_start; + media_segment->scale_duration = scale_duration; media_segment->start = start; - media_segment->start_time = start_time; media_segment->duration = duration; + media_segment->repeat = repeat; g_ptr_array_add (stream->segments, media_segment); - GST_LOG ("Added new segment: number %d, ts: %" GST_TIME_FORMAT ", dur: %" - GST_TIME_FORMAT " start time %" GST_TIME_FORMAT, number, - GST_TIME_ARGS (start), GST_TIME_ARGS (duration), - GST_TIME_ARGS (start_time)); + GST_LOG ("Added new segment: number %d, repeat %d, " + "ts: %" GST_TIME_FORMAT ", dur: %" + GST_TIME_FORMAT, number, repeat, + GST_TIME_ARGS (start), GST_TIME_ARGS (duration)); return TRUE; } @@ -3056,8 +3084,8 @@ gst_mpd_client_setup_representation (GstMpdClient * client, stream->cur_adapt_set, representation)) == NULL) { 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, PeriodStart, - PeriodEnd)) { + if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0, + PeriodEnd - PeriodStart, PeriodStart, PeriodEnd)) { return FALSE; } } else { @@ -3083,7 +3111,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline; for (list = g_queue_peek_head_link (&timeline->S); list; list = g_list_next (list)) { - guint j, timescale; + guint timescale; S = (GstSNode *) list->data; GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%" @@ -3100,28 +3128,30 @@ gst_mpd_client_setup_representation (GstMpdClient * client, start_time /= timescale; } - for (j = 0; j <= S->r && SegmentURL != NULL; j++) { - if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, - start, start_time, duration)) { - return FALSE; - } - i++; - start += S->d; - start_time += duration; - SegmentURL = g_list_next (SegmentURL); + if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, + S->r, start, S->d, start_time, duration)) { + return FALSE; } + i += S->r + 1; + start_time += duration * (S->r + 1); + start += S->d * (S->r + 1); + SegmentURL = g_list_next (SegmentURL); } } else { - duration = gst_mpd_client_get_segment_duration (client, stream); + gint64 scale_dur; + + duration = + gst_mpd_client_get_segment_duration (client, stream, &scale_dur); if (!GST_CLOCK_TIME_IS_VALID (duration)) return FALSE; while (SegmentURL) { - if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, 0, - start_time, duration)) { + if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, + 0, start, scale_dur, start_time, duration)) { return FALSE; } i++; + start += scale_dur; start_time += duration; SegmentURL = g_list_next (SegmentURL); } @@ -3141,7 +3171,8 @@ gst_mpd_client_setup_representation (GstMpdClient * client, gst_mpdparser_init_active_stream_segments (stream); /* 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)) { + if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0, PeriodEnd, + 0, PeriodEnd)) { return FALSE; } } else { @@ -3173,7 +3204,7 @@ gst_mpd_client_setup_representation (GstMpdClient * client, gst_mpdparser_init_active_stream_segments (stream); for (list = g_queue_peek_head_link (&timeline->S); list; list = g_list_next (list)) { - guint j, timescale; + guint timescale; S = (GstSNode *) list->data; GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%" @@ -3189,15 +3220,13 @@ gst_mpd_client_setup_representation (GstMpdClient * client, start_time /= timescale; } - for (j = 0; j <= S->r; j++) { - if (!gst_mpd_client_add_media_segment (stream, NULL, i, start, - start_time, duration)) { - return FALSE; - } - i++; - start += S->d; - start_time += duration; + if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start, + S->d, start_time, duration)) { + return FALSE; } + i += S->r + 1; + start += S->d * (S->r + 1); + start_time += duration * (S->r + 1); } } else { /* NOP - The segment is created on demand with the template, no need @@ -3211,9 +3240,8 @@ gst_mpd_client_setup_representation (GstMpdClient * client, g_ptr_array_index (stream->segments, stream->segments->len - 1) : NULL; if (last_media_segment && GST_CLOCK_TIME_IS_VALID (PeriodEnd)) { - if (last_media_segment->start_time + last_media_segment->duration > - PeriodEnd) { - last_media_segment->duration = PeriodEnd - last_media_segment->start_time; + if (last_media_segment->start + last_media_segment->duration > PeriodEnd) { + last_media_segment->duration = PeriodEnd - last_media_segment->start; GST_LOG ("Fixed duration of last segment: %" GST_TIME_FORMAT, GST_TIME_ARGS (last_media_segment->duration)); } @@ -3434,9 +3462,10 @@ gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, GstMediaSegment *segment = g_ptr_array_index (stream->segments, index); GST_DEBUG ("Looking at fragment sequence chunk %d", index); - if (segment->start_time <= ts - && ts < segment->start_time + segment->duration) { + if (segment->start <= ts + && ts < segment->start + (segment->repeat + 1) * segment->duration) { selectedChunk = segment; + index = segment->number + (ts - segment->start) / segment->duration; break; } } @@ -3446,7 +3475,7 @@ gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, } } else { GstClockTime duration = - gst_mpd_client_get_segment_duration (client, stream); + gst_mpd_client_get_segment_duration (client, stream, NULL); g_return_val_if_fail (stream->cur_seg_template-> MultSegBaseType->SegmentTimeline == NULL, FALSE); if (!GST_CLOCK_TIME_IS_VALID (duration)) { @@ -3457,7 +3486,6 @@ gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream, gst_mpd_client_set_segment_index (stream, index); - return TRUE; } @@ -3569,7 +3597,7 @@ gst_mpd_client_get_last_fragment_timestamp (GstMpdClient * client, return FALSE; } - *ts = currentChunk.start_time; + *ts = currentChunk.start; return TRUE; } @@ -3594,7 +3622,7 @@ gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client, return FALSE; } - *ts = currentChunk.start_time; + *ts = currentChunk.start; return TRUE; } @@ -3648,19 +3676,19 @@ gst_mpd_client_get_next_fragment (GstMpdClient * client, mediaURL = gst_mpdparser_build_URL_from_template (stream->cur_seg_template->media, stream->cur_representation->id, currentChunk.number, - stream->cur_representation->bandwidth, currentChunk.start); + stream->cur_representation->bandwidth, currentChunk.scale_start); if (stream->cur_seg_template->index) { indexURL = gst_mpdparser_build_URL_from_template (stream-> cur_seg_template->index, stream->cur_representation->id, currentChunk.number, stream->cur_representation->bandwidth, - currentChunk.start); + currentChunk.scale_start); } } GST_DEBUG ("mediaURL = %s", mediaURL); GST_DEBUG ("indexURL = %s", indexURL); - fragment->timestamp = currentChunk.start_time; + fragment->timestamp = currentChunk.start; fragment->duration = currentChunk.duration; fragment->discontinuity = segment_idx != currentChunk.number; fragment->range_start = 0; @@ -3859,7 +3887,7 @@ gst_mpd_client_get_next_fragment_duration (GstMpdClient * client, return media_segment == NULL ? 0 : media_segment->duration; } else { GstClockTime duration = - gst_mpd_client_get_segment_duration (client, stream); + gst_mpd_client_get_segment_duration (client, stream, NULL); g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType-> SegmentTimeline == NULL, 0); @@ -4227,7 +4255,7 @@ gst_mpd_client_get_next_segment_availability_end_time (GstMpdClient * client, stream_period = gst_mpdparser_get_stream_period (client); seg_idx = gst_mpd_client_get_segment_index (stream); - seg_duration = gst_mpd_client_get_segment_duration (client, stream); + seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL); if (seg_duration == 0) return NULL; availability_start_time = gst_mpd_client_get_availability_start_time (client); @@ -4262,7 +4290,7 @@ gst_mpd_client_check_time_position (GstMpdClient * client, g_return_val_if_fail (gst_mpd_client_is_live (client), 0); - duration = gst_mpd_client_get_segment_duration (client, stream); + duration = gst_mpd_client_get_segment_duration (client, stream, NULL); stream_now = g_date_time_difference (now, start); g_date_time_unref (now); g_date_time_unref (start); diff --git a/ext/dash/gstmpdparser.h b/ext/dash/gstmpdparser.h index 7a098c3bc8..ddafd4b013 100644 --- a/ext/dash/gstmpdparser.h +++ b/ext/dash/gstmpdparser.h @@ -414,8 +414,10 @@ struct _GstMediaSegment { GstSegmentURLNode *SegmentURL; /* this is NULL when using a SegmentTemplate */ guint number; /* segment number */ - guint64 start; /* segment start time in timescale units */ - GstClockTime start_time; /* segment start time */ + gint repeat; /* number of extra repetitions (0 = played only once) */ + gint64 scale_start; /* start time in timescale units */ + gint64 scale_duration; /* duration in timescale units */ + GstClockTime start; /* segment start time */ GstClockTime duration; /* segment duration */ };