dnl **** DASH ****
translit(dnm, m, l) AM_CONDITIONAL(USE_DASH, true)
AG_GST_CHECK_FEATURE(DASH, [DASH plug-in], dash, [
- PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= 2.8, [HAVE_LIBXML2="yes"
+ PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= 2.4, [HAVE_LIBXML2="yes"
HAVE_DASH="yes"], [HAVE_LIBXML2="no"
HAVE_DASH="no"])
AC_SUBST(LIBXML2_CFLAGS)
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
- $(GIO_CFLAGS) \
$(LIBXML2_CFLAGS)
libgstdashdemux_la_LIBADD = \
$(top_builddir)/gst-libs/gst/uridownloader/libgsturidownloader-@GST_API_VERSION@.la \
-lgsttag-$(GST_API_VERSION) \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
- $(GIO_LIBS) \
- -lgstnet-$(GST_API_VERSION) \
$(LIBXML2_LIBS)
libgstdashdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstdashdemux_la_LIBTOOLFLAGS = --tag=disable-static
#endif
#include <string.h>
-#include <stdio.h>
#include <inttypes.h>
-#include <gio/gio.h>
#include <gst/base/gsttypefindhelper.h>
#include <gst/tag/tag.h>
#include "gst/gst-i18n-plugin.h"
dashstream->index);
}
-static GstClockTime
-gst_dash_demux_get_period_start_time (GstAdaptiveDemux * demux)
-{
- GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux);
-
- return gst_mpd_parser_get_period_start_time (dashdemux->client);
-}
-
static void
gst_dash_demux_class_init (GstDashDemuxClass * klass)
{
gst_dash_demux_get_live_seek_range;
gstadaptivedemux_class->get_presentation_offset =
gst_dash_demux_get_presentation_offset;
- gstadaptivedemux_class->get_period_start_time =
- gst_dash_demux_get_period_start_time;
gstadaptivedemux_class->finish_fragment =
gst_dash_demux_stream_fragment_finished;
}
static gboolean
-gst_dash_demux_stream_has_next_subfragment (GstAdaptiveDemuxStream * stream)
-{
- GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
- GstSidxBox *sidx = SIDX (dashstream);
-
- if (dashstream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
- if (stream->demux->segment.rate > 0.0) {
- if (sidx->entry_index + 1 < sidx->entries_count)
- return TRUE;
- } else {
- if (sidx->entry_index >= 1)
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static gboolean
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream)
{
GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
- if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
- if (gst_dash_demux_stream_has_next_subfragment (stream))
- return TRUE;
- }
-
return gst_mpd_client_has_next_segment (dashdemux->client,
dashstream->active_stream, stream->demux->segment.rate > 0.0);
}
GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client) &&
- dashstream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
+ dashstream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED)
/* fragment is advanced on data_received when byte limits are reached */
- if (gst_dash_demux_stream_has_next_fragment (stream))
- return GST_FLOW_OK;
- return GST_FLOW_EOS;
- }
+ return GST_FLOW_OK;
if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
return GST_FLOW_OK;
const gchar * property_name, gchar ** property_value);
static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
const gchar * property_name, gchar *** property_value);
-static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
- const gchar * property_name, gint default_val, gint * property_value);
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
const gchar * property_name, guint default_val, guint * property_value);
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
gchar ** content);
static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
const gchar * prefix);
-static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
- gchar ** content);
/* XML node parsing */
static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
}
static gboolean
-gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
- const gchar * property_name, gint default_val, gint * property_value)
-{
- xmlChar *prop_string;
- gboolean exists = FALSE;
-
- *property_value = default_val;
- prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
- if (prop_string) {
- if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
- exists = TRUE;
- GST_LOG (" - %s: %d", property_name, *property_value);
- } else {
- GST_WARNING
- ("failed to parse signed integer property %s from xml string %s",
- property_name, prop_string);
- }
- xmlFree (prop_string);
- }
-
- return exists;
-}
-
-static gboolean
gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
const gchar * property_name, guint default_val, guint * property_value)
{
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
- if (sscanf ((gchar *) prop_string, "%u", property_value) == 1) {
+ if (sscanf ((gchar *) prop_string, "%u", property_value)) {
exists = TRUE;
GST_LOG (" - %s: %u", property_name, *property_value);
} else {
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
- if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
- property_value) == 1) {
+ if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT, property_value)) {
exists = TRUE;
GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
} else {
exists = TRUE;
GST_LOG (" - %s:", property_name);
for (i = 0; i < *value_size; i++) {
- if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1) {
+ if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i])) {
GST_LOG (" %u", prop_uint_vector[i]);
} else {
GST_WARNING
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
- if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
+ if (sscanf ((gchar *) prop_string, "%lf", property_value)) {
exists = TRUE;
GST_LOG (" - %s: %lf", property_name, *property_value);
} else {
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
- if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
+ if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type)
&& prop_SAP_type <= 6) {
exists = TRUE;
*property_value = (GstSAPType) prop_SAP_type;
return exists;
}
-static gboolean
-gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
-{
- gboolean exists = FALSE;
- const char *txt_encoding;
- xmlOutputBufferPtr out_buf;
-
- txt_encoding = (const char *) a_node->doc->encoding;
- out_buf = xmlAllocOutputBuffer (NULL);
- g_assert (out_buf != NULL);
- xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
- xmlOutputBufferFlush (out_buf);
-#ifdef LIBXML2_NEW_BUFFER
- if (xmlOutputBufferGetSize (out_buf) > 0) {
- *content =
- (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
- xmlOutputBufferGetSize (out_buf));
- exists = TRUE;
- }
-#else
- if (out_buf->conv && out_buf->conv->use > 0) {
- *content =
- (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
- exists = TRUE;
- } else if (out_buf->buffer && out_buf->buffer->use > 0) {
- *content =
- (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
- exists = TRUE;
- }
-#endif // LIBXML2_NEW_BUFFER
- (void) xmlOutputBufferClose (out_buf);
-
- if (exists) {
- GST_LOG (" - %s: %s", a_node->name, *content);
- }
- return exists;
-}
-
static gchar *
gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
{
GST_LOG ("attributes of %s node:", a_node->name);
gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri",
&new_descriptor->schemeIdUri);
- if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
- &new_descriptor->value)) {
- /* if no value attribute, use XML string representation of the node */
- gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
- }
+ gst_mpdparser_get_xml_prop_string (a_node, "value", &new_descriptor->value);
}
static void
&new_s_node->t);
gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0,
&new_s_node->d);
- gst_mpdparser_get_xml_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "r", 0, &new_s_node->r);
}
static GstSegmentTimelineNode *
return stream->baseURL;
}
-static GstClockTime
-gst_mpdparser_get_segment_end_time (GstMpdClient * client, GPtrArray * segments,
- const GstMediaSegment * segment, gint index)
-{
- const GstStreamPeriod *stream_period;
- GstClockTime end;
-
- if (segment->repeat >= 0)
- return segment->start + (segment->repeat + 1) * segment->duration;
-
- if (index < segments->len - 1) {
- const GstMediaSegment *next_segment =
- g_ptr_array_index (segments, index + 1);
- end = next_segment->start;
- } else {
- stream_period = gst_mpdparser_get_stream_period (client);
- end = stream_period->start + stream_period->duration;
- }
- return end;
-}
-
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;
- gint repeat;
s = g_ptr_array_index (segments, i);
- if (s->repeat >= 0) {
- repeat = s->repeat;
- } else {
- GstClockTime start = s->start;
- GstClockTime end =
- gst_mpdparser_get_segment_end_time (client, segments, s, i);
- repeat = (guint) (end - start) / s->duration;
- }
- if (s->number + repeat >= index) {
+ if (s->number + s->repeat >= index) {
/* it is in this segment */
result->SegmentURL = s->SegmentURL;
result->number = index;
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 {
/* 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) {
if (S->t > 0) {
start = S->t;
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
+ start_time += PeriodStart;
}
if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
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 - PeriodStart, 0, PeriodEnd - PeriodStart)) {
+ PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
return FALSE;
}
} else {
/* build segment list */
i = mult_seg->startNumber;
start = 0;
- start_time = 0;
+ start_time = PeriodStart;
GST_LOG ("Building media segment list using this template: %s",
stream->cur_seg_template->media);
if (S->t > 0) {
start = S->t;
start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
+ start_time += PeriodStart;
}
if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
if (last_media_segment && GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
if (last_media_segment->start + last_media_segment->duration > PeriodEnd) {
- last_media_segment->duration =
- PeriodEnd - PeriodStart - last_media_segment->start;
+ 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));
}
gint index = 0;
gint repeat_index = 0;
GstMediaSegment *selectedChunk = NULL;
- gboolean in_segment;
g_return_val_if_fail (stream != NULL, 0);
GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
stream->segments->len);
- in_segment = FALSE;
- if (segment->start <= ts) {
- if (segment->repeat >= 0) {
- in_segment =
- ts < segment->start + (segment->repeat + 1) * segment->duration;
- } else {
- GstClockTime end =
- gst_mpdparser_get_segment_end_time (client, stream->segments,
- segment, index);
- in_segment = ts < end;
- }
- if (in_segment) {
- selectedChunk = segment;
- repeat_index = (ts - segment->start) / segment->duration;
- break;
- }
+ if (segment->start <= ts
+ && ts < segment->start + (segment->repeat + 1) * segment->duration) {
+ selectedChunk = segment;
+ repeat_index = (ts - segment->start) / segment->duration;
+ break;
}
}
GstActiveStream *stream;
gint segment_idx;
GstMediaSegment *currentChunk;
- GstStreamPeriod *stream_period;
GST_DEBUG ("Stream index: %i", stream_idx);
stream = g_list_nth_data (client->active_streams, stream_idx);
segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
currentChunk = g_ptr_array_index (stream->segments, segment_idx);
- if (currentChunk->repeat >= 0) {
- *ts =
- currentChunk->start + (currentChunk->duration * (1 +
- currentChunk->repeat));
- } else {
- /* 5.3.9.6.1: negative repeat means repeat till the end of the
- * period, or the next update of the MPD (which I think is
- * implicit, as this will all get deleted/recreated), or the
- * start of the next segment, if any. */
- stream_period = gst_mpdparser_get_stream_period (client);
- *ts = stream_period->start + stream_period->duration;
- }
+ *ts =
+ currentChunk->start + (currentChunk->duration * (1 +
+ currentChunk->repeat));
return TRUE;
}
GstClockTime duration =
gst_mpd_client_get_segment_duration (client, stream, NULL);
guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
+ GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
g_return_val_if_fail (stream->cur_seg_template->
MultSegBaseType->SegmentTimeline == NULL, FALSE);
&& stream->segment_index >= segments_count)) {
return FALSE;
}
- *ts = stream->segment_index * duration;
+ *ts = stream_period->start + stream->segment_index * duration;
}
return TRUE;
guint stream_idx)
{
GstActiveStream *stream = NULL;
+ GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
- g_return_val_if_fail (client != NULL, 0);
- g_return_val_if_fail (client->active_streams != NULL, 0);
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->active_streams != NULL, FALSE);
stream = g_list_nth_data (client->active_streams, stream_idx);
- g_return_val_if_fail (stream != NULL, 0);
-
- return stream->presentationTimeOffset;
-}
-
-GstClockTime
-gst_mpd_parser_get_period_start_time (GstMpdClient * client)
-{
- GstStreamPeriod *stream_period = NULL;
-
- g_return_val_if_fail (client != NULL, 0);
- stream_period = gst_mpdparser_get_stream_period (client);
- g_return_val_if_fail (stream_period != NULL, 0);
+ g_return_val_if_fail (stream != NULL, FALSE);
- return stream_period->start;
+ if (stream->presentationTimeOffset > stream_period->start)
+ return stream->presentationTimeOffset - stream_period->start;
+ else
+ return 0;
}
gboolean
}
}
} else {
+ GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
+
if (stream->cur_seg_template != NULL) {
mediaURL =
gst_mpdparser_build_URL_from_template (stream->
GST_DEBUG ("mediaURL = %s", mediaURL);
GST_DEBUG ("indexURL = %s", indexURL);
- fragment->timestamp = stream->segment_index * fragment->duration;
+ fragment->timestamp =
+ stream_period->start + stream->segment_index * fragment->duration;
}
base_url = gst_uri_from_string (stream->baseURL);
if (forward) {
guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
- if (segments_count > 0 && stream->segments
- && stream->segment_index + 1 == segments_count) {
- GstMediaSegment *segment;
-
- segment = g_ptr_array_index (stream->segments, stream->segment_index);
- if (segment->repeat >= 0
- && stream->segment_repeat_index >= segment->repeat)
- return FALSE;
- } else if (segments_count > 0
- && stream->segment_index + 1 >= segments_count) {
+#ifdef GST_DASHDEMUX_MODIFICATION
+ /* consider if segment count is only 1 */
+ if (!(segments_count == 1 && stream->segment_index == 0))
+ stream->segment_index += 1;
+ if (segments_count > 0 && stream->segment_index >= segments_count)
+#else
+ if (segments_count > 0 && stream->segment_index + 1 >= segments_count)
+#endif
return FALSE;
- }
} else {
if (stream->segment_index < 0)
return FALSE;
if (stream->segment_index >= segments_count) {
stream->segment_index = segments_count - 1;
segment = g_ptr_array_index (stream->segments, stream->segment_index);
- if (segment->repeat >= 0) {
- stream->segment_repeat_index = segment->repeat;
- } else {
- GstClockTime start = segment->start;
- GstClockTime end =
- gst_mpdparser_get_segment_end_time (client, stream->segments,
- segment,
- stream->segment_index);
- stream->segment_repeat_index =
- (guint) (end - start) / segment->duration;
- }
+ stream->segment_repeat_index = segment->repeat;
goto done;
}
}
/* for the normal cases we can get the segment safely here */
segment = g_ptr_array_index (stream->segments, stream->segment_index);
if (forward) {
- if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
+ if (stream->segment_repeat_index >= segment->repeat) {
stream->segment_repeat_index = 0;
stream->segment_index++;
if (segments_count > 0 && stream->segment_index >= segments_count) {
}
segment = g_ptr_array_index (stream->segments, stream->segment_index);
- /* negative repeats only seem to make sense at the end of a list,
- * so this one will probably not be. Needs some sanity checking
- * when loading the XML data. */
- if (segment->repeat >= 0) {
- stream->segment_repeat_index = segment->repeat;
- } else {
- GstClockTime start = segment->start;
- GstClockTime end =
- gst_mpdparser_get_segment_end_time (client, stream->segments,
- segment,
- stream->segment_index);
- stream->segment_repeat_index =
- (guint) (end - start) / segment->duration;
- }
+ stream->segment_repeat_index = segment->repeat;
} else {
stream->segment_repeat_index--;
}
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;
- }
+ if (availability_start_time == NULL)
+ return (GstDateTime *) NULL;
}
offset = (1 + seg_idx) * seg_duration;
{
guint64 t;
guint64 d;
- gint r;
+ guint r;
};
struct _GstSegmentTimelineNode
gboolean gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time);
gint gst_mpd_client_check_time_position (GstMpdClient * client, GstActiveStream * stream, GstClockTime ts, gint64 * diff);
GstClockTime gst_mpd_parser_get_stream_presentation_offset (GstMpdClient *client, guint stream_idx);
-GstClockTime gst_mpd_parser_get_period_start_time (GstMpdClient *client);
/* Period selection */
guint gst_mpd_client_get_period_index_at_time (GstMpdClient * client, GstDateTime * time);
stream);
static void gst_adaptive_demux_reset (GstAdaptiveDemux * demux);
static gboolean gst_adaptive_demux_expose_streams (GstAdaptiveDemux * demux,
- gboolean first_and_live);
+ gboolean first_segment);
static gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
static GstFlowReturn gst_adaptive_demux_stream_seek (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, GstClockTime ts);
}
if (demux->next_streams) {
- gst_adaptive_demux_expose_streams (demux,
- gst_adaptive_demux_is_live (demux));
+ gst_adaptive_demux_expose_streams (demux, TRUE);
gst_adaptive_demux_start_tasks (demux);
if (gst_adaptive_demux_is_live (demux)) {
/* Task to periodically update the manifest */
return klass->get_presentation_offset (demux, stream);
}
-static GstClockTime
-gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux)
-{
- GstAdaptiveDemuxClass *klass;
-
- klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
-
- if (klass->get_period_start_time == NULL)
- return 0;
-
- return klass->get_period_start_time (demux);
-}
-
static gboolean
gst_adaptive_demux_expose_streams (GstAdaptiveDemux * demux,
- gboolean first_and_live)
+ gboolean first_segment)
{
GList *iter;
GList *old_streams;
- GstClockTime period_start, min_pts = GST_CLOCK_TIME_NONE;
+ GstClockTime min_pts = GST_CLOCK_TIME_NONE;
g_return_val_if_fail (demux->next_streams != NULL, FALSE);
/* TODO act on error */
}
- if (first_and_live) {
+ if (first_segment) {
/* TODO we only need the first timestamp, maybe create a simple function */
gst_adaptive_demux_stream_update_fragment_info (demux, stream);
}
}
- /* For live streams, the subclass is supposed to seek to the current
- * fragment and then tell us its timestamp in stream->fragment.timestamp.
- * We now also have to seek our demuxer segment to reflect this.
- *
- * FIXME: This needs some refactoring at some point.
- */
- if (first_and_live) {
- gst_segment_do_seek (&demux->segment, demux->segment.rate, GST_FORMAT_TIME,
- GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, min_pts, GST_SEEK_TYPE_NONE, -1,
- NULL);
- }
-
- period_start = gst_adaptive_demux_get_period_start_time (demux);
-
+ if (first_segment)
+ demux->segment.start = demux->segment.position = demux->segment.time =
+ min_pts;
for (iter = demux->streams; iter; iter = g_list_next (iter)) {
GstAdaptiveDemuxStream *stream = iter->data;
GstClockTime offset;
offset = gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
stream->segment = demux->segment;
- /* The demuxer segment is just built from seek events, but for each stream
- * we have to adjust segments according to the current period and the
- * stream specific presentation time offset.
- *
- * For each period, buffer timestamps start again from 0. Additionally the
- * buffer timestamps are shifted by the stream specific presentation time
- * offset, so the first buffer timestamp of a period is 0 + presentation
- * time offset. If the stream contains timestamps itself, this is also
- * supposed to be the presentation time stored inside the stream.
- *
- * The stream time over periods is supposed to be continuous, that is the
- * buffer timestamp 0 + presentation time offset should map to the start
- * time of the current period.
- *
- *
- * The adjustment of the stream segments as such works the following.
- *
- * If the demuxer segment start is bigger than the period start, this
- * means that we have to drop some media at the beginning of the current
- * period, e.g. because a seek into the middle of the period has
- * happened. The amount of media to drop is the difference between the
- * period start and the demuxer segment start, and as each period starts
- * again from 0, this difference is going to be the actual stream's
- * segment start. As all timestamps of the stream are shifted by the
- * presentation time offset, we will also have to move the segment start
- * by that offset.
- *
- * Now the running time and stream time at the stream's segment start has to
- * be the one that is stored inside the demuxer's segment, which means
- * that segment.base and segment.time have to be copied over.
- *
- *
- * If the demuxer segment start is smaller than the period start time,
- * this means that the whole period is inside the segment. As each period
- * starts timestamps from 0, and additionally timestamps are shifted by
- * the presentation time offset, the stream's first timestamp (and as such
- * the stream's segment start) has to be the presentation time offset.
- * The stream time at the segment start is supposed to be the stream time
- * of the period start according to the demuxer segment, so the stream
- * segment's time would be set to that. The same goes for the stream
- * segment's base, which is supposed to be the running time of the period
- * start according to the demuxer's segment.
- *
- *
- * For the first case where not the complete period is inside the segment,
- * the segment time and base as calculated by the second case would be
- * equivalent.
- */
-
- if (demux->segment.start > period_start) {
- stream->segment.start = demux->segment.start - period_start + offset;
- stream->segment.position = offset;
- stream->segment.time = demux->segment.time;
- stream->segment.base = demux->segment.base;
- } else {
- stream->segment.start = offset;
- stream->segment.position = offset;
- stream->segment.time =
- gst_segment_to_stream_time (&demux->segment, GST_FORMAT_TIME,
- period_start);
+ if (first_segment)
+ demux->segment.start = demux->segment.position = demux->segment.time =
+ stream->fragment.timestamp;
+ stream->segment.start += offset;
+
+ if (first_segment)
stream->segment.base =
- gst_segment_to_running_time (&demux->segment, GST_FORMAT_TIME,
- period_start);
- }
+ gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
+ stream->segment.start);
stream->pending_segment = gst_event_new_segment (&stream->segment);
gst_event_set_seqnum (stream->pending_segment, demux->priv->segment_seqnum);
stream->pending_segment = NULL;
}
- if (stream->pending_events) {
- g_list_free_full (stream->pending_events, (GDestroyNotify) gst_event_unref);
- stream->pending_events = NULL;
- }
-
if (stream->src_srcpad) {
gst_object_unref (stream->src_srcpad);
stream->src_srcpad = NULL;
gst_adaptive_demux_expose_streams (demux, FALSE);
} else {
GList *iter;
- GstClockTime period_start =
- gst_adaptive_demux_get_period_start_time (demux);
for (iter = demux->streams; iter; iter = g_list_next (iter)) {
GstAdaptiveDemuxStream *stream = iter->data;
GstEvent *seg_evt;
GstClockTime offset;
- /* See comments in gst_adaptive_demux_get_period_start_time() for
- * an explanation of the segment modifications */
stream->segment = demux->segment;
offset =
gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
- stream->segment.start += offset - period_start;
- stream->segment.position = stream->segment.start;
+ stream->segment.start += offset;
seg_evt = gst_event_new_segment (&stream->segment);
gst_event_set_seqnum (seg_evt, demux->priv->segment_seqnum);
gst_event_replace (&stream->pending_segment, seg_evt);
}
}
GST_MANIFEST_UNLOCK (demux);
- GST_LOG_OBJECT (demux, "GST_QUERY_DURATION returns %s with duration %"
+ GST_DEBUG_OBJECT (demux, "GST_QUERY_DURATION returns %s with duration %"
GST_TIME_FORMAT, ret ? "TRUE" : "FALSE", GST_TIME_ARGS (duration));
break;
}
stream->pending_tags = tags;
}
-void
-gst_adaptive_demux_stream_queue_event (GstAdaptiveDemuxStream * stream,
- GstEvent * event)
-{
- stream->pending_events = g_list_append (stream->pending_events, event);
-}
-
static guint64
_update_average_bitrate (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, guint64 new_bitrate)
if (stream->first_fragment_buffer) {
GstClockTime offset =
gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
- GstClockTime period_start =
- gst_adaptive_demux_get_period_start_time (demux);
if (demux->segment.rate < 0)
/* Set DISCONT flag for every first buffer in reverse playback mode
if (GST_BUFFER_PTS_IS_VALID (buffer)) {
stream->segment.position = GST_BUFFER_PTS (buffer);
-
- /* Convert from position inside the stream's segment to the demuxer's
- * segment, they are not necessarily the same */
- if (stream->segment.position - offset + period_start >
- demux->segment.position)
- demux->segment.position =
- stream->segment.position - offset + period_start;
+ if (stream->segment.position > demux->segment.position)
+ demux->segment.position = stream->segment.position;
}
} else {
GST_BUFFER_PTS (buffer) = GST_CLOCK_TIME_NONE;
gst_pad_push_event (stream->pad, gst_event_new_tag (stream->pending_tags));
stream->pending_tags = NULL;
}
- while (stream->pending_events != NULL) {
- GstEvent *event = stream->pending_events->data;
-
- if (!gst_pad_push_event (stream->pad, event))
- GST_ERROR_OBJECT (stream->pad, "Failed to send pending event");
-
- stream->pending_events =
- g_list_delete_link (stream->pending_events, stream->pending_events);
- }
ret = gst_pad_push (stream->pad, buffer);
GST_LOG_OBJECT (stream->pad, "Push result: %d %s", ret,
if (stream->starting_fragment) {
GstClockTime offset =
gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
- GstClockTime period_start =
- gst_adaptive_demux_get_period_start_time (demux);
stream->starting_fragment = FALSE;
if (klass->start_fragment) {
if (GST_BUFFER_PTS_IS_VALID (buffer)) {
stream->segment.position = GST_BUFFER_PTS (buffer);
-
- /* Convert from position inside the stream's segment to the demuxer's
- * segment, they are not necessarily the same */
- if (stream->segment.position - offset + period_start >
- demux->segment.position)
- demux->segment.position =
- stream->segment.position - offset + period_start;
+ if (stream->segment.position > demux->segment.position)
+ demux->segment.position = stream->segment.position;
}
} else {
g_mutex_lock (&stream->fragment_download_lock);
stream->download_finished = TRUE;
- GST_DEBUG_OBJECT (stream->pad, "Download finish: %d %s - err: %p", ret,
- gst_flow_get_name (ret), err);
-
/* if we have an error, only replace last_ret if it was OK before to avoid
* overwriting the first error we got */
- if (stream->last_ret == GST_FLOW_OK) {
- stream->last_ret = ret;
- if (err) {
+ if (err) {
+ if (stream->last_ret == GST_FLOW_OK) {
+ stream->last_ret = ret;
g_clear_error (&stream->last_error);
stream->last_error = g_error_copy (err);
}
+ } else {
+ stream->last_ret = ret;
}
g_cond_signal (&stream->fragment_download_cond);
g_mutex_unlock (&stream->fragment_download_lock);
break;
}
- GST_DEBUG_OBJECT (demux, "No fragment left but live playlist, wait a bit");
- g_cond_wait (&demux->manifest_cond, GST_MANIFEST_GET_LOCK (demux));
-
/* Got a new fragment or not live anymore? */
if (gst_adaptive_demux_stream_has_next_fragment (demux, stream)) {
GST_DEBUG_OBJECT (demux, "new fragment available, "
ret = FALSE;
break;
}
+
+ GST_DEBUG_OBJECT (demux, "No fragment left but live playlist, wait a bit");
+ g_cond_wait (&demux->manifest_cond, GST_MANIFEST_GET_LOCK (demux));
}
GST_DEBUG_OBJECT (demux, "Retrying now");
return ret;
}
ret = stream->last_ret;
- GST_DEBUG_OBJECT (stream->pad, "Fragment download finished: %s %d %s",
- uri, stream->last_ret, gst_flow_get_name (stream->last_ret));
+ GST_DEBUG_OBJECT (stream->pad, "Fragment download finished: %s", uri);
}
g_mutex_unlock (&stream->fragment_download_lock);
} else {
/* Check if we're done with our segment */
if (demux->segment.rate > 0) {
if (GST_CLOCK_TIME_IS_VALID (demux->segment.stop)
- && stream->segment.position >= stream->segment.stop) {
+ && stream->segment.position >= demux->segment.stop) {
ret = GST_FLOW_EOS;
gst_task_stop (stream->download_task);
goto end_of_manifest;
}
} else {
if (GST_CLOCK_TIME_IS_VALID (demux->segment.start)
- && stream->segment.position < stream->segment.start) {
+ && stream->segment.position < demux->segment.start) {
ret = GST_FLOW_EOS;
gst_task_stop (stream->download_task);
goto end_of_manifest;
if (G_UNLIKELY (stream->restart_download)) {
GstSegment segment;
GstEvent *seg_event;
- GstClockTime cur, ts;
+ GstClockTime cur, ts, offset;
gint64 pos;
GST_DEBUG_OBJECT (stream->pad,
"Activating stream due to reconfigure event");
- cur = ts =
- gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
- stream->segment.position);
+ cur = ts = stream->segment.position;
if (gst_pad_peer_query_position (stream->pad, GST_FORMAT_TIME, &pos)) {
ts = (GstClockTime) pos;
gst_segment_copy_into (&demux->segment, &segment);
if (GST_CLOCK_TIME_IS_VALID (ts)) {
- GstClockTime offset, period_start;
-
- offset =
- gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
- period_start = gst_adaptive_demux_get_period_start_time (demux);
-
/* TODO check return */
gst_adaptive_demux_stream_seek (demux, stream, ts);
- segment.position = ts - period_start + offset;
+ if (cur < ts) {
+ segment.position = ts;
+ }
}
-
- /* The stream's segment is still correct except for
- * the position, so let's send a new one with the
- * updated position */
+ stream->segment = segment;
+ offset = gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
+ stream->segment.start += offset;
seg_event = gst_event_new_segment (&stream->segment);
gst_event_set_seqnum (seg_event, demux->priv->segment_seqnum);
GST_DEBUG_OBJECT (stream->pad, "Sending restart segment: %"
stream->download_total_time * GST_USECOND, NULL)));
if (GST_CLOCK_TIME_IS_VALID (duration)) {
- GstClockTime offset =
- gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
- GstClockTime period_start =
- gst_adaptive_demux_get_period_start_time (demux);
-
stream->segment.position += duration;
-
- /* Convert from position inside the stream's segment to the demuxer's
- * segment, they are not necessarily the same */
- if (stream->segment.position - offset + period_start >
- demux->segment.position)
- demux->segment.position =
- stream->segment.position - offset + period_start;
+ if (stream->segment.position > demux->segment.position)
+ demux->segment.position = stream->segment.position;
}
if (gst_adaptive_demux_is_live (demux)
GstEvent *pending_segment;
GstTagList *pending_tags;
gboolean need_header;
- GList *pending_events;
GstFlowReturn last_ret;
GError *last_error;
* @stream.
*/
GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemuxStream *stream);
-
- /**
- * get_period_start_time:
- * @demux: #GstAdaptiveDemux
- *
- * Gets the start time of the current period. Timestamps are resetting to 0
- * after each period but we have to maintain a continuous stream and running
- * time so need to know the start time of the current period.
- *
- * Return: a #GstClockTime representing the start time of the currently
- * selected period.
- */
- GstClockTime (*get_period_start_time) (GstAdaptiveDemux *demux);
};
GType gst_adaptive_demux_get_type (void);
GstFlowReturn
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, GstClockTime duration);
-void gst_adaptive_demux_stream_queue_event (GstAdaptiveDemuxStream * stream,
- GstEvent * event);
-
GstFlowReturn
gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, GstClockTime duration);