dashdemux: improve mpd parsing for repeated segment entries

Use a single segment to represent it internally to avoid using too
much memory. This has the drawback of issuing a linear search to
find the correct segment to play but this can be fixed by using
binary searches or caching the current position and just looking
for the next one.

https://bugzilla.gnome.org/show_bug.cgi?id=748369
This commit is contained in:
Thiago Santos 2015-05-07 16:30:48 -03:00
parent 61cad123d7
commit 6344f86e44
2 changed files with 97 additions and 67 deletions

View file

@ -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);

View file

@ -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 */
};