GValue * value, GParamSpec * pspec);
static void gst_dash_demux_dispose (GObject * obj);
-/* GstAdaptiveDemux */
-static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux);
-static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux);
-static void gst_dash_demux_reset (GstAdaptiveDemux * ademux);
-static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux,
- GstBuffer * buf);
-static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
+/* GstAdaptiveDemuxStream */
static GstFlowReturn
gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream *
- stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
- GstClockTimeDiff * final_ts);
+static GstClockTime
+gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
+ stream);
static gboolean gst_dash_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
* stream);
static GstFlowReturn
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemux2Stream * stream);
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
stream, guint64 bitrate);
+static GstClockTime
+gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
+ stream);
+static GstFlowReturn
+gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
+ GstBuffer * buffer);
+static gboolean gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream *
+ stream);
+static GstFlowReturn
+gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream);
+static gboolean
+gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream);
+
+/* GstAdaptiveDemux */
+static GstClockTime gst_dash_demux_get_duration (GstAdaptiveDemux * ademux);
+static gboolean gst_dash_demux_is_live (GstAdaptiveDemux * ademux);
+static void gst_dash_demux_reset (GstAdaptiveDemux * ademux);
+static gboolean gst_dash_demux_process_manifest (GstAdaptiveDemux * ademux,
+ GstBuffer * buf);
+static gboolean gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
+static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemux2Stream *
+ stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
+ GstClockTimeDiff * final_ts);
static gint64 gst_dash_demux_get_manifest_update_interval (GstAdaptiveDemux *
demux);
static GstFlowReturn gst_dash_demux_update_manifest_data (GstAdaptiveDemux *
demux, GstBuffer * buf);
-static GstClockTime
-gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
- stream);
static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux);
-static GstFlowReturn gst_dash_demux_data_received (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
-static gboolean
-gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn
-gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static gboolean gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream *
- stream);
/* GstDashDemux2 */
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux2 * demux);
gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
+ GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
gobject_class->finalize = gst_dash_demux_stream_finalize;
+
+ adaptivedemux2stream_class->update_fragment_info =
+ gst_dash_demux_stream_update_fragment_info;
+ adaptivedemux2stream_class->has_next_fragment =
+ gst_dash_demux_stream_has_next_fragment;
+ adaptivedemux2stream_class->advance_fragment =
+ gst_dash_demux_stream_advance_fragment;
+ adaptivedemux2stream_class->get_fragment_waiting_time =
+ gst_dash_demux_stream_get_fragment_waiting_time;
+ adaptivedemux2stream_class->select_bitrate =
+ gst_dash_demux_stream_select_bitrate;
+ adaptivedemux2stream_class->get_presentation_offset =
+ gst_dash_demux_stream_get_presentation_offset;
+
+ adaptivedemux2stream_class->start_fragment =
+ gst_dash_demux_stream_fragment_start;
+ adaptivedemux2stream_class->finish_fragment =
+ gst_dash_demux_stream_fragment_finished;
+ adaptivedemux2stream_class->data_received =
+ gst_dash_demux_stream_data_received;
+ adaptivedemux2stream_class->need_another_chunk =
+ gst_dash_demux_stream_need_another_chunk;
}
}
static GstClockTime
-gst_dash_demux_get_presentation_offset (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
{
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
- GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
+ GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
return gst_mpd_client2_get_stream_presentation_offset (dashdemux->client,
dashstream->index);
gstadaptivedemux_class->has_next_period = gst_dash_demux_has_next_period;
gstadaptivedemux_class->advance_period = gst_dash_demux_advance_period;
- gstadaptivedemux_class->stream_has_next_fragment =
- gst_dash_demux_stream_has_next_fragment;
- gstadaptivedemux_class->stream_advance_fragment =
- gst_dash_demux_stream_advance_fragment;
- gstadaptivedemux_class->stream_get_fragment_waiting_time =
- gst_dash_demux_stream_get_fragment_waiting_time;
+
gstadaptivedemux_class->stream_seek = gst_dash_demux_stream_seek;
- gstadaptivedemux_class->stream_select_bitrate =
- gst_dash_demux_stream_select_bitrate;
- gstadaptivedemux_class->stream_update_fragment_info =
- gst_dash_demux_stream_update_fragment_info;
gstadaptivedemux_class->get_live_seek_range =
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->start_fragment = gst_dash_demux_stream_fragment_start;
- gstadaptivedemux_class->finish_fragment =
- gst_dash_demux_stream_fragment_finished;
- gstadaptivedemux_class->data_received = gst_dash_demux_data_received;
- gstadaptivedemux_class->need_another_chunk =
- gst_dash_demux_need_another_chunk;
}
static void
GstClockTime deadline;
GstClockTime upstream_earliest_time;
GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
- gdouble play_rate = gst_adaptive_demux_play_rate (stream->demux);
+ gdouble play_rate = gst_adaptive_demux_play_rate (demux);
GstClockTime period_start = gst_dash_demux_get_period_start_time (demux);
GstClockTime pts_offset =
- gst_dash_demux_get_presentation_offset (demux, stream);
+ gst_dash_demux_stream_get_presentation_offset (stream);
g_assert (min_skip > 0);
}
static gboolean
-gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_dash_demux_stream_fragment_start (GstAdaptiveDemux2Stream * stream)
{
- GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
+ GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
GST_LOG_OBJECT (stream, "Actual position %" GST_TIME_FORMAT,
* buffer. We need offsets to be consistent between moof and mdat
*/
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
- && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux)
+ && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux)
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
stream->discont = TRUE;
}
static GstFlowReturn
-gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux2Stream * stream)
{
GstClockTime consumed_duration;
- GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (demux);
+ GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
/* We need to mark every first buffer of a key unit as discont,
* buffer. We need offsets to be consistent between moof and mdat
*/
if (dashstream->is_isobmff && dashdemux->allow_trickmode_key_units
- && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (demux)
+ && GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS (stream->demux)
&& dashstream->active_stream->mimeType == GST_STREAM_VIDEO)
stream->discont = TRUE;
consumed_duration =
(stream->fragment.stream_time + stream->fragment.duration) -
stream->current_position;
- GST_LOG_OBJECT (demux, "Consumed duration after seeking: %"
+ GST_LOG_OBJECT (stream, "Consumed duration after seeking: %"
GST_TIMEP_FORMAT, &consumed_duration);
} else {
consumed_duration = stream->fragment.duration;
}
- return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
+ return gst_adaptive_demux2_stream_advance_fragment (stream,
consumed_duration);
}
static gboolean
-gst_dash_demux_need_another_chunk (GstAdaptiveDemux2Stream * stream)
+gst_dash_demux_stream_need_another_chunk (GstAdaptiveDemux2Stream * stream)
{
- GstDashDemux2 *dashdemux = (GstDashDemux2 *) stream->demux;
+ GstDashDemux2 *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
GstAdaptiveDemux *demux = stream->demux;
GstDashDemux2Stream *dashstream = (GstDashDemux2Stream *) stream;
gboolean playing_forward = (demux->segment.rate > 0.0);
static GstFlowReturn
-gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_dash_demux_stream_handle_isobmff (GstAdaptiveDemux2Stream * stream)
{
+ GstAdaptiveDemux *demux = stream->demux;
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *buffer;
if (sidx_advance) {
ret =
- gst_adaptive_demux2_stream_advance_fragment (demux, stream,
+ gst_adaptive_demux2_stream_advance_fragment (stream,
SIDX_CURRENT_ENTRY (dash_stream)->duration);
if (ret != GST_FLOW_OK)
return ret;
/* If we still have data available, recurse and use it up if possible */
if (gst_adapter_available (dash_stream->adapter) > 0)
- return gst_dash_demux_handle_isobmff (demux, stream);
+ return gst_dash_demux_stream_handle_isobmff (stream);
}
return ret;
}
static GstFlowReturn
-gst_dash_demux_data_received (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
+gst_dash_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
+ GstBuffer * buffer)
{
GstDashDemux2Stream *dash_stream = (GstDashDemux2Stream *) stream;
GstFlowReturn ret = GST_FLOW_OK;
if (dash_stream->is_isobmff || stream->downloading_index) {
/* SIDX index is also ISOBMMF */
- ret = gst_dash_demux_handle_isobmff (demux, stream);
+ ret = gst_dash_demux_stream_handle_isobmff (stream);
} else if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
gsize available;
if (has_next) {
GstFlowReturn new_ret;
new_ret =
- gst_adaptive_demux2_stream_advance_fragment (demux, stream,
+ gst_adaptive_demux2_stream_advance_fragment (stream,
SIDX_CURRENT_ENTRY (dash_stream)->duration);
/* only overwrite if it was OK before */
#ifndef _GST_ADAPTIVE_DEMUX_PRIVATE_H_
#define _GST_ADAPTIVE_DEMUX_PRIVATE_H_
+#include <gst/gst.h>
#include <gst/base/gstadapter.h>
#include <gst/base/gstflowcombiner.h>
+#include "gstadaptivedemux-types.h"
+#include "gstadaptivedemux.h"
+#include "gstadaptivedemuxutils.h"
+
+G_BEGIN_DECLS
+
#define NUM_LOOKBACK_FRAGMENTS 3
#define MAX_DOWNLOAD_ERROR_COUNT 3
void gst_adaptive_demux2_stream_wants_manifest_update (GstAdaptiveDemux * demux);
void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err);
-GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
- demux, GstAdaptiveDemux2Stream * stream);
-GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
+GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream * stream);
+GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux);
gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
void gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream);
void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
-gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
+gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream);
GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags,
GstClockTimeDiff ts, GstClockTimeDiff * final_ts);
gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux);
void gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux);
+GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
void gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream);
gboolean gst_adaptive_demux_handle_lost_sync (GstAdaptiveDemux * demux);
gboolean gst_adaptive_demux_period_has_pending_tracks (GstAdaptiveDemuxPeriod * period);
void gst_adaptive_demux_period_check_input_wakeup_locked (GstAdaptiveDemuxPeriod * period, GstClockTimeDiff current_output_position);
+G_END_DECLS
+
#endif
#include "config.h"
#endif
-#include "gstadaptivedemux.h"
+#include "gstadaptivedemux-stream.h"
#include "gstadaptivedemux-private.h"
#include <glib/gi18n-lib.h>
static void gst_adaptive_demux2_stream_finalize (GObject * object);
static void gst_adaptive_demux2_stream_error (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream *
+ stream, GstBuffer * buffer);
+static GstFlowReturn
+gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream *
+ stream);
+
+guint64
+gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
+ stream);
+static void gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream
+ * stream);
#define gst_adaptive_demux2_stream_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE (GstAdaptiveDemux2Stream, gst_adaptive_demux2_stream,
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gst_adaptive_demux2_stream_finalize;
+
+ klass->data_received = gst_adaptive_demux2_stream_data_received_default;
+ klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default;
}
static GType tsdemux_type = 0;
}
static void
-drain_inactive_tracks (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+drain_inactive_tracks (GstAdaptiveDemux2Stream * stream)
{
GList *iter;
+ GstAdaptiveDemux *demux = stream->demux;
TRACKS_LOCK (demux);
for (iter = stream->tracks; iter; iter = iter->next) {
gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
stream, GstFlowReturn ret, GError * err)
{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
- GstAdaptiveDemux *demux = stream->demux;
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GST_DEBUG_OBJECT (stream,
"%s download finish: %d %s - err: %p", uritype (stream), ret,
if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) {
/* We lost sync, seek back to live and return */
GST_WARNING_OBJECT (stream, "Lost sync when downloading");
- gst_adaptive_demux_handle_lost_sync (demux);
+ gst_adaptive_demux_handle_lost_sync (stream->demux);
return;
} else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) {
/* The sub-class wants to stop the fragment immediately */
stream->fragment.finished = TRUE;
- ret = klass->finish_fragment (demux, stream);
+ ret = klass->finish_fragment (stream);
GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
gst_flow_get_name (ret));
|| !klass->need_another_chunk (stream)
|| stream->fragment.chunk_size == 0) {
stream->fragment.finished = TRUE;
- ret = klass->finish_fragment (stream->demux, stream);
+ ret = klass->finish_fragment (stream);
GST_DEBUG_OBJECT (stream, "finish_fragment ret %d %s", ret,
gst_flow_get_name (ret));
/* For HLS, we might be enqueueing data into tracks that aren't
* selected. Drain those ones out */
- drain_inactive_tracks (stream->demux, stream);
+ drain_inactive_tracks (stream);
/* Now that we've called finish_fragment we can clear these flags the
* sub-class might have checked */
GST_LOG_OBJECT (stream, "Scheduling next_download() call");
stream->pending_cb_id =
- gst_adaptive_demux_loop_call (demux->priv->scheduler_task,
+ gst_adaptive_demux_loop_call (stream->demux->priv->scheduler_task,
(GSourceFunc) gst_adaptive_demux2_stream_next_download,
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
}
}
static void
-gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, gboolean first_and_live)
+gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux2Stream * stream,
+ gboolean first_and_live)
{
+ GstAdaptiveDemux *demux = stream->demux;
GstClockTime period_start = gst_adaptive_demux_get_period_start_time (demux);
GstClockTime offset =
- gst_adaptive_demux2_stream_get_presentation_offset (demux, stream);
+ gst_adaptive_demux2_stream_get_presentation_offset (stream);
+ /* FIXME: Add a helper function to retrieve the demuxer segment
+ * using the SEGMENT_LOCK */
stream->parse_segment = demux->segment;
/* The demuxer segment is just built from seek events, but for each stream
* the segment time and base as calculated by the second case would be
* equivalent.
*/
- GST_DEBUG_OBJECT (demux, "Using demux segment %" GST_SEGMENT_FORMAT,
- &demux->segment);
+ GST_DEBUG_OBJECT (stream, "Using demux segment %" GST_SEGMENT_FORMAT,
+ &stream->parse_segment);
+
GST_DEBUG_OBJECT (demux,
"period_start: %" GST_TIME_FORMAT " offset: %" GST_TIME_FORMAT,
GST_TIME_ARGS (period_start), GST_TIME_ARGS (offset));
if (GST_CLOCK_STIME_IS_VALID (pos)) {
GstClockTime offset =
- gst_adaptive_demux2_stream_get_presentation_offset (demux, stream);
+ gst_adaptive_demux2_stream_get_presentation_offset (stream);
pos += offset;
GList *pending_events = NULL;
if (stream->compute_segment) {
- gst_adaptive_demux2_stream_prepare_segment (demux, stream,
- stream->first_and_live);
+ gst_adaptive_demux2_stream_prepare_segment (stream, stream->first_and_live);
stream->compute_segment = FALSE;
stream->first_and_live = FALSE;
}
GstBuffer * buffer)
{
GstAdaptiveDemux *demux = stream->demux;
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
GstFlowReturn ret = GST_FLOW_OK;
/* do not make any changes if the stream is cancelled */
* including the *actual* fragment ! */
if (stream->starting_fragment) {
stream->starting_fragment = FALSE;
- if (klass->start_fragment != NULL && !klass->start_fragment (demux, stream))
+ if (klass->start_fragment != NULL && !klass->start_fragment (stream))
return GST_FLOW_ERROR;
}
"Received %s buffer of size %" G_GSIZE_FORMAT, uritype (stream),
gst_buffer_get_size (buffer));
- ret = klass->data_received (demux, stream, buffer);
+ ret = klass->data_received (stream, buffer);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (stream, "data_received returned %s",
GST_DEBUG_OBJECT (stream, "Pushing EOS to parser");
/* TODO push this on all pads */
- gst_event_set_seqnum (eos, stream->demux->priv->segment_seqnum);
+ gst_event_set_seqnum (eos, demux->priv->segment_seqnum);
gst_pad_send_event (stream->parsebin_sink, eos);
ret = GST_FLOW_ERROR;
|| last_status_code / 100 == 5)) {
/* 4xx/5xx */
/* if current position is before available start, switch to next */
- if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream))
+ if (!gst_adaptive_demux2_stream_has_next_fragment (stream))
goto flushing;
if (live) {
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
- ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
+ ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream, "update_fragment_info ret: %s",
gst_flow_get_name (ret));
} else if (demux->segment.position > range_stop) {
/* wait a bit to be in range, we don't have any locks at that point */
GstClockTime wait_time =
- gst_adaptive_demux2_stream_get_fragment_waiting_time (demux,
- stream);
+ gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
if (wait_time > 0) {
GST_DEBUG_OBJECT (stream,
"Download waiting for %" GST_TIME_FORMAT,
gst_adaptive_demux2_stream_handle_playlist_eos (stream);
return;
}
- } else if (!gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) {
+ } else if (!gst_adaptive_demux2_stream_has_next_fragment (stream)) {
/* If this is the last fragment, consider failures EOS and not actual
* errors. Due to rounding errors in the durations, the last fragment
* might not actually exist */
gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
{
GstAdaptiveDemux *demux = stream->demux;
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
gchar *url = NULL;
/* FIXME : */
case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE:
/* Get information about the fragment to download */
GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
- ret = gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
+ ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream,
"Fragment info update result: %d %s", ret, gst_flow_get_name (ret));
/* wait for live fragments to be available */
if (live) {
GstClockTime wait_time =
- gst_adaptive_demux2_stream_get_fragment_waiting_time (demux, stream);
+ gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
if (wait_time > 0) {
GST_DEBUG_OBJECT (stream,
"Download waiting for %" GST_TIME_FORMAT,
static gboolean
gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream)
{
- GstAdaptiveDemux *demux = stream->demux;
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
- if (!klass->stream_can_start)
+ if (!klass->can_start)
return TRUE;
- return klass->stream_can_start (demux, stream);
+ return klass->can_start (stream);
}
/**
return ret;
}
+
+/* Called from the scheduler task */
+GstClockTime
+gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
+ stream)
+{
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+
+ if (klass->get_presentation_offset == NULL)
+ return 0;
+
+ return klass->get_presentation_offset (stream);
+}
+
+GstFlowReturn
+gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux2Stream *
+ stream)
+{
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+ GstFlowReturn ret;
+
+ g_return_val_if_fail (klass->update_fragment_info != NULL, GST_FLOW_ERROR);
+
+ /* Make sure the sub-class will update bitrate, or else
+ * we will later */
+ stream->fragment.finished = FALSE;
+
+ GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (stream->current_position));
+
+ ret = klass->update_fragment_info (stream);
+
+ GST_LOG_OBJECT (stream, "ret:%s uri:%s",
+ gst_flow_get_name (ret), stream->fragment.uri);
+ if (ret == GST_FLOW_OK) {
+ GST_LOG_OBJECT (stream,
+ "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
+ GST_STIME_ARGS (stream->fragment.stream_time),
+ GST_TIME_ARGS (stream->fragment.duration));
+ GST_LOG_OBJECT (stream,
+ "range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT,
+ stream->fragment.range_start, stream->fragment.range_end);
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux2Stream *
+ stream, GstBuffer * buffer)
+{
+ return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
+}
+
+static GstFlowReturn
+gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux2Stream *
+ stream)
+{
+ /* No need to advance, this isn't a real fragment */
+ if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
+ return GST_FLOW_OK;
+
+ return gst_adaptive_demux2_stream_advance_fragment (stream,
+ stream->fragment.duration);
+}
+
+/* must be called from the scheduler */
+gboolean
+gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
+{
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+ gboolean ret = TRUE;
+
+ if (klass->has_next_fragment)
+ ret = klass->has_next_fragment (stream);
+
+ return ret;
+}
+
+static gboolean
+gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
+ demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate)
+{
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+
+ if (klass->select_bitrate)
+ return klass->select_bitrate (stream, bitrate);
+ return FALSE;
+}
+
+GstClockTime
+gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
+ stream)
+{
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+
+ if (klass->get_fragment_waiting_time)
+ return klass->get_fragment_waiting_time (stream);
+ return 0;
+}
+
+/* must be called from the scheduler */
+/* Called from: the ::finish_fragment() handlers when an *actual* fragment is
+ * done
+ *
+ * @duration: Is the duration of the advancement starting from
+ * stream->current_position which might not be the fragment duration after a
+ * seek.
+ */
+GstFlowReturn
+gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stream * stream,
+ GstClockTime duration)
+{
+ if (stream->last_ret != GST_FLOW_OK)
+ return stream->last_ret;
+
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+ GstAdaptiveDemux *demux = stream->demux;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ g_assert (klass->advance_fragment != NULL);
+
+ GST_LOG_OBJECT (stream,
+ "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
+ GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration));
+
+ stream->download_error_count = 0;
+ g_clear_error (&stream->last_error);
+
+#if 0
+ /* FIXME - url has no indication of byte ranges for subsegments */
+ /* FIXME: Reenable statistics sending? */
+ gst_element_post_message (GST_ELEMENT_CAST (demux),
+ gst_message_new_element (GST_OBJECT_CAST (demux),
+ gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
+ "manifest-uri", G_TYPE_STRING,
+ demux->manifest_uri, "uri", G_TYPE_STRING,
+ stream->fragment.uri, "fragment-start-time",
+ GST_TYPE_CLOCK_TIME, stream->download_start_time,
+ "fragment-stop-time", GST_TYPE_CLOCK_TIME,
+ gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64,
+ stream->download_total_bytes, "fragment-download-time",
+ GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL)));
+#endif
+
+ /* Don't update to the end of the segment if in reverse playback */
+ GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux);
+ if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) {
+ stream->parse_segment.position += duration;
+ stream->current_position += duration;
+
+ GST_DEBUG_OBJECT (stream,
+ "stream position now %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (stream->current_position));
+ }
+ GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
+
+ /* When advancing with a non 1.0 rate on live streams, we need to check
+ * the live seeking range again to make sure we can still advance to
+ * that position */
+ if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) {
+ if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream))
+ ret = GST_FLOW_EOS;
+ else
+ ret = klass->advance_fragment (stream);
+ } else if (gst_adaptive_demux_is_live (demux)
+ || gst_adaptive_demux2_stream_has_next_fragment (stream)) {
+ ret = klass->advance_fragment (stream);
+ } else {
+ ret = GST_FLOW_EOS;
+ }
+
+ stream->download_start_time =
+ GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux));
+
+ /* Always check if we need to switch bitrate on OK, or when live
+ * (it's normal to have EOS on advancing in live when we hit the
+ * end of the manifest) */
+ if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) {
+ GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change");
+ if (gst_adaptive_demux2_stream_select_bitrate (demux, stream,
+ gst_adaptive_demux2_stream_update_current_bitrate (stream))) {
+ GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH");
+ stream->need_header = TRUE;
+ ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH;
+ }
+ }
+
+ stream->last_ret = ret;
+ return stream->last_ret;
+}
+
+/* TRACKS_LOCK held */
+static GstAdaptiveDemuxTrack *
+gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream,
+ GstStreamType stream_type)
+{
+ GList *iter;
+
+ for (iter = stream->tracks; iter; iter = iter->next) {
+ GstAdaptiveDemuxTrack *track = iter->data;
+
+ if (track->type == stream_type)
+ return track;
+ }
+
+ return NULL;
+}
+
+/* TRACKS lock held */
+static void
+gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream * stream)
+{
+ guint i;
+
+ GST_DEBUG_OBJECT (stream, "Updating track information from collection");
+
+ for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
+ i++) {
+ GstStream *gst_stream =
+ gst_stream_collection_get_stream (stream->stream_collection, i);
+ GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
+ GstAdaptiveDemuxTrack *track;
+
+ if (stream_type == GST_STREAM_TYPE_UNKNOWN)
+ continue;
+ track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type);
+ if (!track) {
+ GST_DEBUG_OBJECT (stream,
+ "We don't have an existing track to handle stream %" GST_PTR_FORMAT,
+ gst_stream);
+ continue;
+ }
+
+ if (track->upstream_stream_id)
+ g_free (track->upstream_stream_id);
+ track->upstream_stream_id =
+ g_strdup (gst_stream_get_stream_id (gst_stream));
+ }
+
+}
+
+static gboolean
+tags_have_language_info (GstTagList * tags)
+{
+ const gchar *language = NULL;
+
+ if (tags == NULL)
+ return FALSE;
+
+ if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0,
+ &language))
+ return TRUE;
+ if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0,
+ &language))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+can_handle_collection (GstAdaptiveDemux2Stream * stream,
+ GstStreamCollection * collection)
+{
+ guint i;
+ guint nb_audio, nb_video, nb_text;
+ gboolean have_audio_languages = TRUE;
+ gboolean have_text_languages = TRUE;
+
+ nb_audio = nb_video = nb_text = 0;
+
+ for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
+ GstStream *gst_stream = gst_stream_collection_get_stream (collection, i);
+ GstTagList *tags = gst_stream_get_tags (gst_stream);
+
+ GST_DEBUG_OBJECT (stream,
+ "Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream);
+ switch (gst_stream_get_stream_type (gst_stream)) {
+ case GST_STREAM_TYPE_AUDIO:
+ have_audio_languages &= tags_have_language_info (tags);
+ nb_audio++;
+ break;
+ case GST_STREAM_TYPE_VIDEO:
+ nb_video++;
+ break;
+ case GST_STREAM_TYPE_TEXT:
+ have_text_languages &= tags_have_language_info (tags);
+ nb_text++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Check that we either have at most 1 of each track type, or that
+ * we have language tags for each to tell which is which */
+ if (nb_video > 1 ||
+ (nb_audio > 1 && !have_audio_languages) ||
+ (nb_text > 1 && !have_text_languages)) {
+ GST_WARNING
+ ("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)",
+ nb_audio, nb_video, nb_text);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Called from the demuxer when it receives a GstStreamCollection on the bus
+ * for this stream. */
+/* TRACKS lock held */
+gboolean
+gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream * stream,
+ GstStreamCollection * collection, gboolean * had_pending_tracks)
+{
+ g_assert (had_pending_tracks != NULL);
+
+ /* Check whether the collection is "sane" or not.
+ *
+ * In the context of adaptive streaming, we can only handle multiplexed
+ * content where the output sub-streams can be matched reliably to the various
+ * tracks. That is, only a single stream of each type, or if there are
+ * multiple audio/subtitle tracks, they can be differentiated by language
+ * (and possibly in the future by codec).
+ */
+ if (!can_handle_collection (stream, collection)) {
+ return FALSE;
+ }
+
+ /* Store the collection on the stream */
+ gst_object_replace ((GstObject **) & stream->stream_collection,
+ (GstObject *) collection);
+
+ /* If stream is marked as having pending_tracks, ask the subclass to
+ * handle that and create the tracks now */
+ if (stream->pending_tracks) {
+ GstAdaptiveDemux2StreamClass *klass =
+ GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+ g_assert (klass->create_tracks);
+ klass->create_tracks (stream);
+ stream->pending_tracks = FALSE;
+ *had_pending_tracks = TRUE;
+ } else {
+ g_assert (stream->tracks);
+
+ /* Now we should have assigned tracks, match them to the
+ * collection and update the pending upstream stream_id
+ * for each of them based on the collection information. */
+ gst_adaptive_demux2_stream_update_track_ids (stream);
+ }
+
+ return TRUE;
+}
+
+static guint64
+_update_average_bitrate (GstAdaptiveDemux2Stream * stream, guint64 new_bitrate)
+{
+ gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS;
+
+ stream->moving_bitrate -= stream->fragment_bitrates[index];
+ stream->fragment_bitrates[index] = new_bitrate;
+ stream->moving_bitrate += new_bitrate;
+
+ stream->moving_index += 1;
+
+ if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS)
+ return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS;
+ return stream->moving_bitrate / stream->moving_index;
+}
+
+guint64
+gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
+ stream)
+{
+ guint64 average_bitrate;
+ guint64 fragment_bitrate;
+ guint connection_speed, min_bitrate, max_bitrate, target_download_rate;
+
+ fragment_bitrate = stream->last_bitrate;
+ GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps",
+ fragment_bitrate);
+
+ average_bitrate = _update_average_bitrate (stream, fragment_bitrate);
+
+ GST_INFO_OBJECT (stream,
+ "last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate);
+ GST_INFO_OBJECT (stream,
+ "Last %u fragments average bitrate is %" G_GUINT64_FORMAT,
+ NUM_LOOKBACK_FRAGMENTS, average_bitrate);
+
+ /* Conservative approach, make sure we don't upgrade too fast */
+ stream->current_download_rate = MIN (average_bitrate, fragment_bitrate);
+
+ /* For the video stream, update the demuxer reported download
+ * rate. FIXME: Move all bandwidth estimation to the
+ * download helper and make it the demuxer's responsibility
+ * to select the right set of things to download within
+ * that bandwidth */
+ GstAdaptiveDemux *demux = stream->demux;
+ GST_OBJECT_LOCK (demux);
+
+ /* If this is stream containing our video, update the overall demuxer
+ * reported bitrate and notify, to give the application a
+ * chance to choose a new connection-bitrate */
+ if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) {
+ demux->current_download_rate = stream->current_download_rate;
+ GST_OBJECT_UNLOCK (demux);
+ g_object_notify (G_OBJECT (demux), "current-bandwidth");
+ GST_OBJECT_LOCK (demux);
+ }
+
+ connection_speed = demux->connection_speed;
+ min_bitrate = demux->min_bitrate;
+ max_bitrate = demux->max_bitrate;
+ GST_OBJECT_UNLOCK (demux);
+
+ if (connection_speed) {
+ GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it",
+ connection_speed / 1000);
+ return connection_speed;
+ }
+
+ /* No explicit connection_speed, so choose the new variant to use as a
+ * fraction of the measured download rate */
+ target_download_rate =
+ CLAMP (stream->current_download_rate, 0,
+ G_MAXUINT) * demux->bandwidth_target_ratio;
+
+ GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u",
+ demux->bandwidth_target_ratio, target_download_rate);
+
+#if 0
+ /* Debugging code, modulate the bitrate every few fragments */
+ {
+ static guint ctr = 0;
+ if (ctr % 3 == 0) {
+ GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging");
+ target_download_rate /= 2;
+ }
+ ctr++;
+ }
+#endif
+
+ if (min_bitrate > 0 && target_download_rate < min_bitrate) {
+ target_download_rate = min_bitrate;
+ GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s",
+ min_bitrate);
+ }
+
+ if (max_bitrate > 0 && target_download_rate > max_bitrate) {
+ target_download_rate = max_bitrate;
+ GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s",
+ max_bitrate);
+ }
+
+ GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps",
+ target_download_rate);
+
+ return target_download_rate;
+}
+
+void
+gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f)
+{
+ g_free (f->uri);
+ f->uri = NULL;
+ f->range_start = 0;
+ f->range_end = -1;
+
+ g_free (f->header_uri);
+ f->header_uri = NULL;
+ f->header_range_start = 0;
+ f->header_range_end = -1;
+
+ g_free (f->index_uri);
+ f->index_uri = NULL;
+ f->index_range_start = 0;
+ f->index_range_end = -1;
+
+ f->stream_time = GST_CLOCK_STIME_NONE;
+ f->duration = GST_CLOCK_TIME_NONE;
+ f->finished = FALSE;
+}
--- /dev/null
+/* GStreamer
+ *
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
+ * Author: Thiago Santos <thiagoss@osg.samsung.com>
+ *
+ * Copyright (C) 2021-2022 Centricular Ltd
+ * Author: Edward Hervey <edward@centricular.com>
+ * Author: Jan Schmidt <jan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_ADAPTIVE_DEMUX_STREAM_H_
+#define _GST_ADAPTIVE_DEMUX_STREAM_H_
+
+#include <gst/gst.h>
+#include "gstadaptivedemux-types.h"
+#include "downloadrequest.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \
+ (gst_adaptive_demux2_stream_get_type())
+#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream))
+#define GST_ADAPTIVE_DEMUX2_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2StreamClass))
+#define GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2StreamClass))
+#define GST_IS_ADAPTIVE_DEMUX2_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM))
+#define GST_IS_ADAPTIVE_DEMUX2_STREAM_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX2_STREAM))
+#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj)
+
+
+#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header)
+
+typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState;
+
+typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment;
+
+struct _GstAdaptiveDemux2StreamFragment
+{
+ /* The period-local stream time for the given fragment. */
+ GstClockTimeDiff stream_time;
+ GstClockTime duration;
+
+ gchar *uri;
+ gint64 range_start;
+ gint64 range_end;
+
+ /* when chunked downloading is used, may be be updated need_another_chunk() */
+ gint chunk_size;
+
+ /* when headers are needed */
+ gchar *header_uri;
+ gint64 header_range_start;
+ gint64 header_range_end;
+
+ /* when index is needed */
+ gchar *index_uri;
+ gint64 index_range_start;
+ gint64 index_range_end;
+
+ gboolean finished;
+};
+
+enum _GstAdaptiveDemux2StreamState {
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS,
+ GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED
+};
+
+struct _GstAdaptiveDemux2StreamClass
+{
+ GstObjectClass parent_class;
+
+ /**
+ * update_fragment_info:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * Requests the stream to set the information about the current fragment to its
+ * current fragment struct
+ *
+ * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS
+ * if there is no fragment.
+ */
+ GstFlowReturn (*update_fragment_info) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * finish_fragment:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * Notifies the subclass that a fragment download was finished.
+ * It can be used to cleanup internal state after a fragment and
+ * also push any pending data before moving to the next fragment.
+ */
+ GstFlowReturn (*finish_fragment) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * data_received:
+ * @stream: #GstAdaptiveDemux2Stream
+ * @buffer: #GstBuffer
+ *
+ * Notifies the subclass that a fragment chunk was downloaded. The subclass
+ * can look at the data and modify/push data as desired.
+ *
+ * Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
+ */
+ GstFlowReturn (*data_received) (GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
+
+ gboolean (*has_next_fragment) (GstAdaptiveDemux2Stream * stream);
+ GstFlowReturn (*advance_fragment) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * can_start:
+ * @stream: a #GstAdaptiveDemux2Stream
+ *
+ * Called before starting a @stream. sub-classes can return %FALSE if more
+ * information is required before it can be started. Sub-classes will have to
+ * call gst_adaptive_demux2_stream_start() when the stream should be started.
+ */
+ gboolean (*can_start) (GstAdaptiveDemux2Stream *stream);
+
+ /**
+ * create_tracks:
+ * @stream: A #GstAdaptiveDemux2Stream
+ *
+ * Called whenever the base class collected a @collection on a @stream which has
+ * pending tracks to be created. Subclasses should override this if they
+ * create streams without tracks.
+ *
+ * * create the various tracks by analyzing the @stream stream_collection
+ * * Set the track upstream_stream_id to the corresponding stream_id from the collection
+ */
+ void (*create_tracks) (GstAdaptiveDemux2Stream *stream);
+
+ /**
+ * need_another_chunk:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * If chunked downloading is used (chunk_size != 0) this is called once a
+ * chunk is finished to decide whether more has to be downloaded or not.
+ * May update chunk_size to a different value
+ */
+ gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * select_bitrate:
+ * @stream: #GstAdaptiveDemux2Stream
+ * @bitrate: the bitrate to select (in bytes per second)
+ *
+ * The stream should try to select the bitrate that is the greater, but not
+ * greater than the requested bitrate. If it needs a codec change it should
+ * create the new stream using gst_adaptive_demux2_stream_new(). If it only
+ * needs a caps change it should set the new caps using
+ * gst_adaptive_demux2_stream_set_caps().
+ *
+ * Returns: %TRUE if the stream changed bitrate, %FALSE otherwise
+ */
+ gboolean (*select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate);
+
+ /**
+ * get_fragment_waiting_time:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * For live streams, requests how much time should be waited before starting
+ * to download the fragment. This is useful to avoid downloading a fragment that
+ * isn't available yet.
+ *
+ * Returns: The waiting time in as a #GstClockTime
+ */
+ GstClockTime (*get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * start_fragment:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * Notifies the subclass that the given stream is starting the download
+ * of a new fragment. Can be used to reset/init internal state that is
+ * needed before each fragment, like decryption engines.
+ *
+ * Returns: %TRUE if successful.
+ */
+ gboolean (*start_fragment) (GstAdaptiveDemux2Stream * stream);
+
+ /**
+ * get_presentation_offset:
+ * @stream: #GstAdaptiveDemux2Stream
+ *
+ * Gets the delay to apply to @stream.
+ *
+ * Return: a #GstClockTime representing the (positive) time offset to apply to
+ * @stream.
+ */
+ GstClockTime (*get_presentation_offset) (GstAdaptiveDemux2Stream *stream);
+};
+
+struct _GstAdaptiveDemux2Stream
+{
+ GstObject object;
+
+ /* FIXME : transition to gstobject->parent */
+ GstAdaptiveDemux *demux;
+
+ /* The period to which the stream belongs, set when adding the stream to the
+ * demuxer */
+ GstAdaptiveDemuxPeriod *period;
+
+ /* The tracks this stream targets */
+ GList *tracks;
+
+ /* The internal parsebin, forward data to track */
+ GstElement *parsebin;
+ GstPad *parsebin_sink;
+
+ gulong pad_added_id, pad_removed_id;
+
+ GstSegment parse_segment;
+
+ /* TRUE if the current stream GstSegment should be sent downstream */
+ gboolean send_segment;
+ /* TRUE if the stream GstSegment requires recalculation (from demuxer
+ segment) */
+ gboolean compute_segment;
+ /* first_and_live applies to compute_segment */
+ gboolean first_and_live;
+
+ /* When restarting, what is the target position (in demux segment) to
+ * begin at */
+ GstClockTime start_position;
+
+ /* Track the current position (in demux segment) of the current fragment */
+ GstClockTime current_position;
+
+ GstCaps *pending_caps;
+ GstTagList *pending_tags;
+
+ GList *pending_events;
+
+ GstFlowReturn last_ret;
+ GError *last_error;
+
+ gboolean discont;
+
+ /* download tooling */
+ gboolean need_header;
+ gboolean need_index;
+
+ gboolean downloading_header;
+ gboolean downloading_index;
+
+ /* persistent, reused download request for fragment data */
+ DownloadRequest *download_request;
+
+ GstAdaptiveDemux2StreamState state;
+ guint pending_cb_id;
+ gboolean download_active;
+ /* The (global output) time at which this stream should be woken
+ * to download more input */
+ GstClockTimeDiff next_input_wakeup_time;
+
+ guint last_status_code;
+
+ gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */
+ gboolean download_finished;
+
+ gboolean starting_fragment;
+ gboolean first_fragment_buffer;
+ gint64 download_start_time;
+ gint64 download_total_bytes;
+ gint64 download_end_offset;
+ guint64 current_download_rate;
+
+ /* bitrate of the previous fragment (pre-queue2) */
+ guint64 last_bitrate;
+
+ /* Total last download time, from request to completion */
+ GstClockTime last_download_time;
+
+ /* Average for the last fragments */
+ guint64 moving_bitrate;
+ guint moving_index;
+ guint64 *fragment_bitrates;
+
+ GstAdaptiveDemux2StreamFragment fragment;
+
+ guint download_error_count;
+
+ /* Last collection provided by parsebin */
+ GstStreamCollection *stream_collection;
+
+ /* OR'd set of stream types in this stream */
+ GstStreamType stream_type;
+
+ /* The buffering threshold recommended by the subclass */
+ GstClockTime recommended_buffering_threshold;
+};
+
+GType gst_adaptive_demux2_stream_get_type (void);
+
+void gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream);
+
+void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
+ GstEvent * event);
+
+gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream);
+gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream);
+
+void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream,
+ GstCaps * caps);
+
+void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream,
+ GstTagList * tags);
+
+GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
+ GstBuffer * buffer);
+
+GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stream * stream,
+ GstClockTime duration);
+
+gboolean gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream *stream,
+ GstStreamCollection *collection, gboolean *had_pending_tracks);
+
+void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* GStreamer
+ *
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
+ * Author: Thiago Santos <thiagoss@osg.samsung.com>
+ *
+ * Copyright (C) 2021-2022 Centricular Ltd
+ * Author: Edward Hervey <edward@centricular.com>
+ * Author: Jan Schmidt <jan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_ADAPTIVE_DEMUX_TYPES_H_
+#define _GST_ADAPTIVE_DEMUX_TYPES_H_
+
+G_BEGIN_DECLS
+
+typedef struct _GstAdaptiveDemux2StreamClass GstAdaptiveDemux2StreamClass;
+typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream;
+typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack;
+typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod;
+typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
+typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass;
+
+#define GST_TYPE_ADAPTIVE_DEMUX \
+ (gst_adaptive_demux_ng_get_type())
+#define GST_ADAPTIVE_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux))
+#define GST_ADAPTIVE_DEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
+#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
+#define GST_IS_ADAPTIVE_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX))
+#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX))
+#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj)
+
+G_END_DECLS
+
+#endif
+
static gboolean gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
gboolean first_and_live);
-static gboolean gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
- demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate);
static GstFlowReturn
gst_adaptive_demux_update_manifest_default (GstAdaptiveDemux * demux);
static void gst_adaptive_demux_start_tasks (GstAdaptiveDemux * demux);
static void gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux,
gboolean stop_updates);
-static GstFlowReturn
-gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
-static GstFlowReturn
-gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn
-gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstClockTime duration);
-
-static void
-gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
static gboolean
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
gst_element_class_add_static_pad_template (gstelement_class,
&gst_adaptive_demux_subtitlesrc_template);
-
gstelement_class->change_state = gst_adaptive_demux_change_state;
gstelement_class->query = gst_adaptive_demux_query;
gstelement_class->send_event = gst_adaptive_demux_send_event;
gstbin_class->handle_message = gst_adaptive_demux_handle_message;
- klass->data_received = gst_adaptive_demux2_stream_data_received_default;
- klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default;
klass->update_manifest = gst_adaptive_demux_update_manifest_default;
klass->requires_periodical_playlist_update =
gst_adaptive_demux_requires_periodical_playlist_update_default;
- klass->stream_update_tracks = gst_adaptive_demux2_stream_update_tracks;
gst_type_mark_as_plugin_api (GST_TYPE_ADAPTIVE_DEMUX, 0);
}
return NULL;
}
-/* TRACKS_LOCK held */
-static GstAdaptiveDemuxTrack *
-gst_adaptive_demux2_stream_find_track_of_type (GstAdaptiveDemux2Stream * stream,
- GstStreamType stream_type)
-{
- GList *iter;
-
- for (iter = stream->tracks; iter; iter = iter->next) {
- GstAdaptiveDemuxTrack *track = iter->data;
-
- if (track->type == stream_type)
- return track;
- }
-
- return NULL;
-}
-
-/* MANIFEST and TRACKS lock held */
-static void
-gst_adaptive_demux2_stream_update_tracks (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- guint i;
-
- GST_DEBUG_OBJECT (stream, "Updating track information from collection");
-
- for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
- i++) {
- GstStream *gst_stream =
- gst_stream_collection_get_stream (stream->stream_collection, i);
- GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
- GstAdaptiveDemuxTrack *track;
-
- if (stream_type == GST_STREAM_TYPE_UNKNOWN)
- continue;
- track = gst_adaptive_demux2_stream_find_track_of_type (stream, stream_type);
- if (!track) {
- GST_DEBUG_OBJECT (stream,
- "We don't have an existing track to handle stream %" GST_PTR_FORMAT,
- gst_stream);
- continue;
- }
-
- if (track->upstream_stream_id)
- g_free (track->upstream_stream_id);
- track->upstream_stream_id =
- g_strdup (gst_stream_get_stream_id (gst_stream));
- }
-
-}
-
-static gboolean
-tags_have_language_info (GstTagList * tags)
-{
- const gchar *language = NULL;
-
- if (tags == NULL)
- return FALSE;
-
- if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_CODE, 0,
- &language))
- return TRUE;
- if (gst_tag_list_peek_string_index (tags, GST_TAG_LANGUAGE_NAME, 0,
- &language))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-can_handle_collection (GstAdaptiveDemux2Stream * stream,
- GstStreamCollection * collection)
-{
- guint i;
- guint nb_audio, nb_video, nb_text;
- gboolean have_audio_languages = TRUE;
- gboolean have_text_languages = TRUE;
-
- nb_audio = nb_video = nb_text = 0;
-
- for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
- GstStream *gst_stream = gst_stream_collection_get_stream (collection, i);
- GstTagList *tags = gst_stream_get_tags (gst_stream);
-
- GST_DEBUG_OBJECT (stream,
- "Internal collection stream #%d %" GST_PTR_FORMAT, i, gst_stream);
- switch (gst_stream_get_stream_type (gst_stream)) {
- case GST_STREAM_TYPE_AUDIO:
- have_audio_languages &= tags_have_language_info (tags);
- nb_audio++;
- break;
- case GST_STREAM_TYPE_VIDEO:
- nb_video++;
- break;
- case GST_STREAM_TYPE_TEXT:
- have_text_languages &= tags_have_language_info (tags);
- nb_text++;
- break;
- default:
- break;
- }
- }
-
- /* Check that we either have at most 1 of each track type, or that
- * we have language tags for each to tell which is which */
- if (nb_video > 1 ||
- (nb_audio > 1 && !have_audio_languages) ||
- (nb_text > 1 && !have_text_languages)) {
- GST_WARNING
- ("Collection can't be handled (nb_audio:%d, nb_video:%d, nb_text:%d)",
- nb_audio, nb_video, nb_text);
- return FALSE;
- }
-
- return TRUE;
-}
-
static void
gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
GstMessage * msg)
if (!collection)
goto beach;
- /* Check whether the collection is "sane" or not.
- *
- * In the context of adaptive streaming, we can only handle multiplexed
- * content that provides at most one stream of valid types (audio, video,
- * text). Without this we cannot reliably match the output of this multiplex
- * to the various tracks.
- *
- * FIXME : In the future and *IF* we encounter such streams, we could envision
- * supporting multiple streams of the same type if, and only if, they have
- * tags that allow differentiating them (ex: languages).
- */
- if (!can_handle_collection (stream, collection)) {
+ TRACKS_LOCK (demux);
+
+ if (!gst_adaptive_demux2_stream_handle_collection (stream, collection,
+ &pending_tracks_activated)) {
+ TRACKS_UNLOCK (demux);
+
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
(_("Stream format can't be handled")),
("The streams provided by the multiplex are ambiguous"));
goto beach;
}
- /* Store the collection on the stream */
- gst_object_replace ((GstObject **) & stream->stream_collection,
- (GstObject *) collection);
-
- /* IF there are pending tracks, ask the subclass to handle that */
- if (stream->pending_tracks) {
- GstAdaptiveDemuxClass *demux_class = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
- g_assert (demux_class->stream_update_tracks);
- demux_class->stream_update_tracks (demux, stream);
- TRACKS_LOCK (demux);
- stream->pending_tracks = FALSE;
- pending_tracks_activated = TRUE;
+ if (pending_tracks_activated) {
+ /* If pending tracks were handled, then update the demuxer collection */
if (gst_adaptive_demux_update_collection (demux, demux->input_period) &&
- demux->input_period == demux->output_period)
+ demux->input_period == demux->output_period) {
gst_adaptive_demux_post_collection (demux);
- } else {
- g_assert (stream->tracks);
- TRACKS_LOCK (demux);
- /* If we already have assigned tracks, update the pending upstream stream_id
- * for each of them based on the collection information. */
- gst_adaptive_demux2_stream_update_tracks (demux, stream);
- }
+ }
+
+ /* If we discovered pending tracks and we no longer have any, we can ensure
+ * selected tracks are started */
+ if (!gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
+ GList *iter = demux->input_period->streams;
+ for (; iter; iter = iter->next) {
+ GstAdaptiveDemux2Stream *new_stream = iter->data;
- /* If we discovered pending tracks and we no longer have any, we can ensure
- * selected tracks are started */
- if (pending_tracks_activated
- && !gst_adaptive_demux_period_has_pending_tracks (demux->input_period)) {
- GList *iter = demux->input_period->streams;
- for (; iter; iter = iter->next) {
- GstAdaptiveDemux2Stream *new_stream = iter->data;
-
- /* The stream that posted this collection was already started. If a
- * different stream is now selected, start it */
- if (stream != new_stream
- && gst_adaptive_demux2_stream_is_selected_locked (new_stream))
- gst_adaptive_demux2_stream_start (new_stream);
+ /* The stream that posted this collection was already started. If a
+ * different stream is now selected, start it */
+ if (stream != new_stream
+ && gst_adaptive_demux2_stream_is_selected_locked (new_stream))
+ gst_adaptive_demux2_stream_start (new_stream);
+ }
}
}
TRACKS_UNLOCK (demux);
/* must be called with manifest_lock taken */
GstClockTime
-gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- GstAdaptiveDemuxClass *klass;
-
- klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
-
- if (klass->get_presentation_offset == NULL)
- return 0;
-
- return klass->get_presentation_offset (demux, stream);
-}
-
-/* must be called with manifest_lock taken */
-GstClockTime
gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux)
{
GstAdaptiveDemuxClass *klass;
/* TODO we only need the first timestamp, maybe create a simple function to
* get the current PTS of a fragment ? */
GST_DEBUG_OBJECT (stream, "Calling update_fragment_info");
- gst_adaptive_demux2_stream_update_fragment_info (demux, stream);
+ gst_adaptive_demux2_stream_update_fragment_info (stream);
GST_DEBUG_OBJECT (stream,
"Got stream time %" GST_STIME_FORMAT,
stream->pending_events = g_list_append (stream->pending_events, event);
}
-static guint64
-_update_average_bitrate (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, guint64 new_bitrate)
-{
- gint index = stream->moving_index % NUM_LOOKBACK_FRAGMENTS;
-
- stream->moving_bitrate -= stream->fragment_bitrates[index];
- stream->fragment_bitrates[index] = new_bitrate;
- stream->moving_bitrate += new_bitrate;
-
- stream->moving_index += 1;
-
- if (stream->moving_index > NUM_LOOKBACK_FRAGMENTS)
- return stream->moving_bitrate / NUM_LOOKBACK_FRAGMENTS;
- return stream->moving_bitrate / stream->moving_index;
-}
-
-static guint64
-gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- guint64 average_bitrate;
- guint64 fragment_bitrate;
- guint connection_speed, min_bitrate, max_bitrate, target_download_rate;
-
- fragment_bitrate = stream->last_bitrate;
- GST_DEBUG_OBJECT (stream, "Download bitrate is : %" G_GUINT64_FORMAT " bps",
- fragment_bitrate);
-
- average_bitrate = _update_average_bitrate (demux, stream, fragment_bitrate);
-
- GST_INFO_OBJECT (stream,
- "last fragment bitrate was %" G_GUINT64_FORMAT, fragment_bitrate);
- GST_INFO_OBJECT (stream,
- "Last %u fragments average bitrate is %" G_GUINT64_FORMAT,
- NUM_LOOKBACK_FRAGMENTS, average_bitrate);
-
- /* Conservative approach, make sure we don't upgrade too fast */
- GST_OBJECT_LOCK (demux);
- stream->current_download_rate = MIN (average_bitrate, fragment_bitrate);
-
- /* If this is the/a video stream update the overall demuxer
- * reported bitrate and notify, to give the application a
- * chance to choose a new connection-bitrate */
- if ((stream->stream_type & GST_STREAM_TYPE_VIDEO) != 0) {
- demux->current_download_rate = stream->current_download_rate;
- GST_OBJECT_UNLOCK (demux);
- g_object_notify (G_OBJECT (demux), "current-bandwidth");
- GST_OBJECT_LOCK (demux);
- }
-
- connection_speed = demux->connection_speed;
- min_bitrate = demux->min_bitrate;
- max_bitrate = demux->max_bitrate;
- GST_OBJECT_UNLOCK (demux);
-
- if (connection_speed) {
- GST_LOG_OBJECT (stream, "connection-speed is set to %u kbps, using it",
- connection_speed / 1000);
- return connection_speed;
- }
-
- /* No explicit connection_speed, so choose the new variant to use as a
- * fraction of the measured download rate */
- target_download_rate =
- CLAMP (stream->current_download_rate, 0,
- G_MAXUINT) * demux->bandwidth_target_ratio;
-
- GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u",
- demux->bandwidth_target_ratio, target_download_rate);
-
-#if 0
- /* Debugging code, modulate the bitrate every few fragments */
- {
- static guint ctr = 0;
- if (ctr % 3 == 0) {
- GST_INFO_OBJECT (stream, "Halving reported bitrate for debugging");
- target_download_rate /= 2;
- }
- ctr++;
- }
-#endif
-
- if (min_bitrate > 0 && target_download_rate < min_bitrate) {
- target_download_rate = min_bitrate;
- GST_LOG_OBJECT (stream, "Bitrate adjusted due to min-bitrate : %u bits/s",
- min_bitrate);
- }
-
- if (max_bitrate > 0 && target_download_rate > max_bitrate) {
- target_download_rate = max_bitrate;
- GST_LOG_OBJECT (stream, "Bitrate adjusted due to max-bitrate : %u bits/s",
- max_bitrate);
- }
-
- GST_DEBUG_OBJECT (stream, "Returning target download rate of %u bps",
- target_download_rate);
-
- return target_download_rate;
-}
-
-/* must be called with manifest_lock taken */
-static GstFlowReturn
-gst_adaptive_demux2_stream_finish_fragment_default (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- /* No need to advance, this isn't a real fragment */
- if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
- return GST_FLOW_OK;
-
- return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
- stream->fragment.duration);
-}
-
-/* must be called with manifest_lock taken.
- * Can temporarily release manifest_lock
- */
-static GstFlowReturn
-gst_adaptive_demux2_stream_data_received_default (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
-{
- return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
-}
-
static gboolean
gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
* demux)
return GST_FLOW_ERROR;
}
-/* must be called from the scheduler */
-gboolean
-gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
- gboolean ret = TRUE;
-
- if (klass->stream_has_next_fragment)
- ret = klass->stream_has_next_fragment (stream);
-
- return ret;
-}
-
-/* must be called from the scheduler */
-/* Called from: the ::finish_fragment() handlers when an *actual* fragment is
- * done
- *
- * @duration: Is the duration of the advancement starting from
- * stream->current_position which might not be the fragment duration after a
- * seek.
- */
-GstFlowReturn
-gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstClockTime duration)
-{
- if (stream->last_ret != GST_FLOW_OK)
- return stream->last_ret;
-
- stream->last_ret =
- gst_adaptive_demux2_stream_advance_fragment_unlocked (demux, stream,
- duration);
-
- return stream->last_ret;
-}
-
-/* must be called with manifest_lock taken */
-static GstFlowReturn
-gst_adaptive_demux2_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstClockTime duration)
-{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
- GstFlowReturn ret;
-
- g_return_val_if_fail (klass->stream_advance_fragment != NULL, GST_FLOW_ERROR);
-
- GST_LOG_OBJECT (stream,
- "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
- GST_STIME_ARGS (stream->fragment.stream_time), GST_TIME_ARGS (duration));
-
- stream->download_error_count = 0;
- g_clear_error (&stream->last_error);
-
-#if 0
- /* FIXME - url has no indication of byte ranges for subsegments */
- /* FIXME: Reenable statistics sending? */
- gst_element_post_message (GST_ELEMENT_CAST (demux),
- gst_message_new_element (GST_OBJECT_CAST (demux),
- gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
- "manifest-uri", G_TYPE_STRING,
- demux->manifest_uri, "uri", G_TYPE_STRING,
- stream->fragment.uri, "fragment-start-time",
- GST_TYPE_CLOCK_TIME, stream->download_start_time,
- "fragment-stop-time", GST_TYPE_CLOCK_TIME,
- gst_util_get_timestamp (), "fragment-size", G_TYPE_UINT64,
- stream->download_total_bytes, "fragment-download-time",
- GST_TYPE_CLOCK_TIME, stream->last_download_time, NULL)));
-#endif
-
- /* Don't update to the end of the segment if in reverse playback */
- GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux);
- if (GST_CLOCK_TIME_IS_VALID (duration) && demux->segment.rate > 0) {
- stream->parse_segment.position += duration;
- stream->current_position += duration;
-
- GST_DEBUG_OBJECT (stream,
- "stream position now %" GST_TIME_FORMAT,
- GST_TIME_ARGS (stream->current_position));
- }
- GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
-
- /* When advancing with a non 1.0 rate on live streams, we need to check
- * the live seeking range again to make sure we can still advance to
- * that position */
- if (demux->segment.rate != 1.0 && gst_adaptive_demux_is_live (demux)) {
- if (!gst_adaptive_demux2_stream_in_live_seek_range (demux, stream))
- ret = GST_FLOW_EOS;
- else
- ret = klass->stream_advance_fragment (stream);
- } else if (gst_adaptive_demux_is_live (demux)
- || gst_adaptive_demux2_stream_has_next_fragment (demux, stream)) {
- ret = klass->stream_advance_fragment (stream);
- } else {
- ret = GST_FLOW_EOS;
- }
-
- stream->download_start_time =
- GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time (demux));
-
- /* Always check if we need to switch bitrate on OK, or when live
- * (it's normal to have EOS on advancing in live when we hit the
- * end of the manifest) */
- if (ret == GST_FLOW_OK || gst_adaptive_demux_is_live (demux)) {
- GST_DEBUG_OBJECT (stream, "checking if stream requires bitrate change");
- if (gst_adaptive_demux2_stream_select_bitrate (demux, stream,
- gst_adaptive_demux2_stream_update_current_bitrate (demux,
- stream))) {
- GST_DEBUG_OBJECT (stream, "Bitrate changed. Returning FLOW_SWITCH");
- stream->need_header = TRUE;
- ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH;
- }
- }
-
- return ret;
-}
-
-/* must be called with manifest_lock taken */
-static gboolean
-gst_adaptive_demux2_stream_select_bitrate (GstAdaptiveDemux *
- demux, GstAdaptiveDemux2Stream * stream, guint64 bitrate)
-{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
-
- if (klass->stream_select_bitrate)
- return klass->stream_select_bitrate (stream, bitrate);
- return FALSE;
-}
-
-/* must be called with manifest_lock taken */
-GstFlowReturn
-gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
-{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
- GstFlowReturn ret;
-
- g_return_val_if_fail (klass->stream_update_fragment_info != NULL,
- GST_FLOW_ERROR);
-
- /* Make sure the sub-class will update bitrate, or else
- * we will later */
- stream->fragment.finished = FALSE;
-
- GST_LOG_OBJECT (stream, "position %" GST_TIME_FORMAT,
- GST_TIME_ARGS (stream->current_position));
-
- ret = klass->stream_update_fragment_info (stream);
-
- GST_LOG_OBJECT (stream, "ret:%s uri:%s",
- gst_flow_get_name (ret), stream->fragment.uri);
- if (ret == GST_FLOW_OK) {
- GST_LOG_OBJECT (stream,
- "stream_time %" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT,
- GST_STIME_ARGS (stream->fragment.stream_time),
- GST_TIME_ARGS (stream->fragment.duration));
- GST_LOG_OBJECT (stream,
- "range start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT,
- stream->fragment.range_start, stream->fragment.range_end);
- }
-
- return ret;
-}
-
-/* must be called with manifest_lock taken */
-GstClockTime
-gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
- demux, GstAdaptiveDemux2Stream * stream)
-{
- GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
-
- if (klass->stream_get_fragment_waiting_time)
- return klass->stream_get_fragment_waiting_time (stream);
- return 0;
-}
-
static void
handle_manifest_download_complete (DownloadRequest * request,
DownloadRequestState state, GstAdaptiveDemux * demux)
return ret;
}
-void
-gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f)
-{
- g_free (f->uri);
- f->uri = NULL;
- f->range_start = 0;
- f->range_end = -1;
-
- g_free (f->header_uri);
- f->header_uri = NULL;
- f->header_range_start = 0;
- f->header_range_end = -1;
-
- g_free (f->index_uri);
- f->index_uri = NULL;
- f->index_range_start = 0;
- f->index_range_end = -1;
-
- f->stream_time = GST_CLOCK_STIME_NONE;
- f->duration = GST_CLOCK_TIME_NONE;
- f->finished = FALSE;
-}
-
/* must be called with manifest_lock taken */
gboolean
gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)
#include "gstadaptivedemuxutils.h"
-G_BEGIN_DECLS
-
-#define GST_TYPE_ADAPTIVE_DEMUX \
- (gst_adaptive_demux_ng_get_type())
-#define GST_ADAPTIVE_DEMUX(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux))
-#define GST_ADAPTIVE_DEMUX_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
-#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
-#define GST_IS_ADAPTIVE_DEMUX(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX))
-#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX))
-#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj)
-
-#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \
- (gst_adaptive_demux2_stream_get_type())
-#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream))
-#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj)
-
-typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream;
-typedef GstObjectClass GstAdaptiveDemux2StreamClass;
+#include "gstadaptivedemux-types.h"
+#include "gstadaptivedemux-stream.h"
+G_BEGIN_DECLS
/**
* GST_ADAPTIVE_DEMUX_SINK_NAME:
#define GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS(obj) ((((GstAdaptiveDemux*)(obj))->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) == GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)
-#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header)
-
/**
* GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME:
*
#define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1
/* Current fragment download should be aborted and restarted. The parent class
- * will call ::update_fragment_info() again to get the updated information.
+ * will call ::update_fragment_info() on the stream again to get the updated information.
*/
#define GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_2
/* The live stream has lost synchronization and the demuxer needs to be resetted */
#define GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_2 + 1
-typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState;
-
-typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment;
-typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack;
-typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod;
-typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
-typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass;
typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate;
-struct _GstAdaptiveDemux2StreamFragment
-{
- /* The period-local stream time for the given fragment. */
- GstClockTimeDiff stream_time;
- GstClockTime duration;
-
- gchar *uri;
- gint64 range_start;
- gint64 range_end;
-
- /* when chunked downloading is used, may be be updated need_another_chunk() */
- gint chunk_size;
-
- /* when headers are needed */
- gchar *header_uri;
- gint64 header_range_start;
- gint64 header_range_end;
-
- /* when index is needed */
- gchar *index_uri;
- gint64 index_range_start;
- gint64 index_range_end;
-
- gboolean finished;
-};
-
struct _GstAdaptiveDemuxTrack
{
gint ref_count;
gboolean output_discont;
};
-enum _GstAdaptiveDemux2StreamState {
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS,
- GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED
-};
-
-struct _GstAdaptiveDemux2Stream
-{
- GstObject object;
-
- /* FIXME : transition to gstobject->parent */
- GstAdaptiveDemux *demux;
-
- /* The period to which the stream belongs, set when adding the stream to the
- * demuxer */
- GstAdaptiveDemuxPeriod *period;
-
- /* The tracks this stream targets */
- GList *tracks;
-
- /* The internal parsebin, forward data to track */
- GstElement *parsebin;
- GstPad *parsebin_sink;
-
- gulong pad_added_id, pad_removed_id;
-
- GstSegment parse_segment;
-
- /* TRUE if the current stream GstSegment should be sent downstream */
- gboolean send_segment;
- /* TRUE if the stream GstSegment requires recalculation (from demuxer
- segment) */
- gboolean compute_segment;
- /* first_and_live applies to compute_segment */
- gboolean first_and_live;
-
- /* When restarting, what is the target position (in demux segment) to
- * begin at */
- GstClockTime start_position;
-
- /* Track the current position (in demux segment) of the current fragment */
- GstClockTime current_position;
-
- GstCaps *pending_caps;
- GstTagList *pending_tags;
-
- GList *pending_events;
-
- GstFlowReturn last_ret;
- GError *last_error;
-
- gboolean discont;
-
- /* download tooling */
- gboolean need_header;
- gboolean need_index;
-
- gboolean downloading_header;
- gboolean downloading_index;
-
- /* persistent, reused download request for fragment data */
- DownloadRequest *download_request;
-
- GstAdaptiveDemux2StreamState state;
- guint pending_cb_id;
- gboolean download_active;
- /* The (global output) time at which this stream should be woken
- * to download more input */
- GstClockTimeDiff next_input_wakeup_time;
-
- guint last_status_code;
-
- gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */
- gboolean download_finished;
-
- gboolean starting_fragment;
- gboolean first_fragment_buffer;
- gint64 download_start_time;
- gint64 download_total_bytes;
- gint64 download_end_offset;
- guint64 current_download_rate;
-
- /* bitrate of the previous fragment (pre-queue2) */
- guint64 last_bitrate;
-
- /* Total last download time, from request to completion */
- GstClockTime last_download_time;
-
- /* Average for the last fragments */
- guint64 moving_bitrate;
- guint moving_index;
- guint64 *fragment_bitrates;
-
- GstAdaptiveDemux2StreamFragment fragment;
-
- guint download_error_count;
-
- /* Last collection provided by parsebin */
- GstStreamCollection *stream_collection;
-
- /* OR'd set of stream types in this stream */
- GstStreamType stream_type;
-
- /* The buffering threshold recommended by the subclass */
- GstClockTime recommended_buffering_threshold;
-};
-
/**
* GstAdaptiveDemuxPeriod:
*
GstSeekFlags flags,
GstClockTimeDiff target_ts,
GstClockTimeDiff * final_ts);
- gboolean (*stream_has_next_fragment) (GstAdaptiveDemux2Stream * stream);
- GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemux2Stream * stream);
-
- /**
- * stream_can_start:
- * @demux: The #GstAdaptiveDemux
- * @stream: a #GstAdaptiveDemux2Stream
- *
- * Called before starting a @stream. sub-classes can return %FALSE if more
- * information is required before it can be started. Sub-classes will have to
- * call gst_adaptive_demux2_stream_start() when the stream should be started.
- */
- gboolean (*stream_can_start) (GstAdaptiveDemux *demux,
- GstAdaptiveDemux2Stream *stream);
-
- /**
- * stream_update_tracks:
- * @demux: The #GstAdaptiveDemux
- * @stream: A #GstAdaptiveDemux2Stream
- *
- * Called whenever the base class collected a @collection on a @stream which has
- * pending tracks to be created. Subclasses should override this if they
- * create streams without tracks.
- *
- * * create the various tracks by analyzing the @stream stream_collection
- * * Set the track upstream_stream_id to the corresponding stream_id from the collection
- */
- void (*stream_update_tracks) (GstAdaptiveDemux *demux,
- GstAdaptiveDemux2Stream *stream);
- /**
- * need_another_chunk:
- * @stream: #GstAdaptiveDemux2Stream
- *
- * If chunked downloading is used (chunk_size != 0) this is called once a
- * chunk is finished to decide whether more has to be downloaded or not.
- * May update chunk_size to a different value
- */
- gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream);
-
- /**
- * stream_update_fragment_info:
- * @stream: #GstAdaptiveDemux2Stream
- *
- * Requests the stream to set the information about the current fragment to its
- * current fragment struct
- *
- * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS
- * if there is no fragment.
- */
- GstFlowReturn (*stream_update_fragment_info) (GstAdaptiveDemux2Stream * stream);
- /**
- * stream_select_bitrate:
- * @stream: #GstAdaptiveDemux2Stream
- * @bitrate: the bitrate to select (in bytes per second)
- *
- * The stream should try to select the bitrate that is the greater, but not
- * greater than the requested bitrate. If it needs a codec change it should
- * create the new stream using gst_adaptive_demux2_stream_new(). If it only
- * needs a caps change it should set the new caps using
- * gst_adaptive_demux2_stream_set_caps().
- *
- * Returns: %TRUE if the stream changed bitrate, %FALSE otherwise
- */
- gboolean (*stream_select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate);
- /**
- * stream_get_fragment_waiting_time:
- * @stream: #GstAdaptiveDemux2Stream
- *
- * For live streams, requests how much time should be waited before starting
- * to download the fragment. This is useful to avoid downloading a fragment that
- * isn't available yet.
- *
- * Returns: The waiting time in as a #GstClockTime
- */
- GstClockTime (*stream_get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream);
-
- /**
- * start_fragment:
- * @demux: #GstAdaptiveDemux
- * @stream: #GstAdaptiveDemux2Stream
- *
- * Notifies the subclass that the given stream is starting the download
- * of a new fragment. Can be used to reset/init internal state that is
- * needed before each fragment, like decryption engines.
- *
- * Returns: %TRUE if successful.
- */
- gboolean (*start_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
- /**
- * finish_fragment:
- * @demux: #GstAdaptiveDemux
- * @stream: #GstAdaptiveDemux2Stream
- *
- * Notifies the subclass that a fragment download was finished.
- * It can be used to cleanup internal state after a fragment and
- * also push any pending data before moving to the next fragment.
- */
- GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
- /**
- * data_received:
- * @demux: #GstAdaptiveDemux
- * @stream: #GstAdaptiveDemux2Stream
- * @buffer: #GstBuffer
- *
- * Notifies the subclass that a fragment chunk was downloaded. The subclass
- * can look at the data and modify/push data as desired.
- *
- * Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
- */
- GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
/**
* get_live_seek_range:
gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop);
/**
- * get_presentation_offset:
- * @demux: #GstAdaptiveDemux
- * @stream: #GstAdaptiveDemux2Stream
- *
- * Gets the delay to apply to @stream.
- *
- * Return: a #GstClockTime representing the (positive) time offset to apply to
- * @stream.
- */
- GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream);
-
- /**
* get_period_start_time:
* @demux: #GstAdaptiveDemux
*
GType gst_adaptive_demux_ng_get_type (void);
-GType gst_adaptive_demux2_stream_get_type (void);
-
gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux,
GstAdaptiveDemux2Stream *stream);
void gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track);
-void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream,
- GstCaps * caps);
-
-void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream,
- GstTagList * tags);
-
-void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f);
-
-GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
- GstBuffer * buffer);
-
-GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream,
- GstClockTime duration);
-
gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux);
-void
-gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream);
-
-void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
- GstEvent * event);
-
-gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream);
-gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream);
-
GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux);
-
GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux);
gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux);
static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
+
static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream *
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
GstClockTimeDiff * final_ts);
+
static gboolean
-gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn gst_hls_demux_data_received (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
+gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream
+ * stream, GstBuffer * buffer);
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
* stream);
-static GstFlowReturn gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream *
+static GstFlowReturn
+gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
+static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream *
stream);
-static GstFlowReturn gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream
- * stream);
-static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static void gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream);
-static gboolean gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream,
- guint64 bitrate);
-static void gst_hls_demux_reset (GstAdaptiveDemux * demux);
-static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux,
- gint64 * start, gint64 * stop);
-static GstClockTime gst_hls_demux_get_presentation_offset (GstAdaptiveDemux *
- demux, GstAdaptiveDemux2Stream * stream);
-static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
- GstHLSVariantStream * variant);
+static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream *
+ stream);
+static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
+ stream, guint64 bitrate);
+static GstClockTime
+gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
static void gst_hls_demux_stream_finalize (GObject * object);
gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
+ GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
gobject_class->finalize = gst_hls_demux_stream_finalize;
+
+ adaptivedemux2stream_class->update_fragment_info =
+ gst_hls_demux_stream_update_fragment_info;
+ adaptivedemux2stream_class->has_next_fragment =
+ gst_hls_demux_stream_has_next_fragment;
+ adaptivedemux2stream_class->advance_fragment =
+ gst_hls_demux_stream_advance_fragment;
+ adaptivedemux2stream_class->select_bitrate =
+ gst_hls_demux_stream_select_bitrate;
+ adaptivedemux2stream_class->can_start = gst_hls_demux_stream_can_start;
+ adaptivedemux2stream_class->create_tracks =
+ gst_hls_demux_stream_create_tracks;
+
+ adaptivedemux2stream_class->start_fragment =
+ gst_hls_demux_stream_start_fragment;
+ adaptivedemux2stream_class->finish_fragment =
+ gst_hls_demux_stream_finish_fragment;
+ adaptivedemux2stream_class->data_received =
+ gst_hls_demux_stream_data_received;
+ adaptivedemux2stream_class->get_presentation_offset =
+ gst_hls_demux_stream_get_presentation_offset;
}
static void
G_DEFINE_TYPE_WITH_CODE (GstHLSDemux2, gst_hls_demux2, GST_TYPE_ADAPTIVE_DEMUX,
hls2_element_init ());
+static void gst_hls_demux_reset (GstAdaptiveDemux * demux);
+static gboolean gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux,
+ gint64 * start, gint64 * stop);
+static void gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
+ GstHLSVariantStream * variant);
+
static void
gst_hls_demux_finalize (GObject * obj)
{
adaptivedemux_class->is_live = gst_hls_demux_is_live;
adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range;
- adaptivedemux_class->get_presentation_offset =
- gst_hls_demux_get_presentation_offset;
adaptivedemux_class->get_duration = gst_hls_demux_get_duration;
adaptivedemux_class->get_manifest_update_interval =
gst_hls_demux_get_manifest_update_interval;
adaptivedemux_class->reset = gst_hls_demux_reset;
adaptivedemux_class->seek = gst_hls_demux_seek;
adaptivedemux_class->stream_seek = gst_hls_demux_stream_seek;
- adaptivedemux_class->stream_has_next_fragment =
- gst_hls_demux_stream_has_next_fragment;
- adaptivedemux_class->stream_advance_fragment = gst_hls_demux_advance_fragment;
- adaptivedemux_class->stream_update_fragment_info =
- gst_hls_demux_update_fragment_info;
- adaptivedemux_class->stream_select_bitrate = gst_hls_demux_select_bitrate;
- adaptivedemux_class->stream_can_start = gst_hls_demux_stream_can_start;
- adaptivedemux_class->stream_update_tracks =
- gst_hls_demux_stream_update_tracks;
-
- adaptivedemux_class->start_fragment = gst_hls_demux_start_fragment;
- adaptivedemux_class->finish_fragment = gst_hls_demux_finish_fragment;
- adaptivedemux_class->data_received = gst_hls_demux_data_received;
}
static void
}
static void
-gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
{
- GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
+ GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
guint i;
GstStreamType uriless_types = 0;
GstCaps *variant_caps = NULL;
- GST_DEBUG_OBJECT (demux, "Update tracks of variant stream");
+ GST_DEBUG_OBJECT (stream, "Update tracks of variant stream");
if (hlsdemux->master->have_codecs) {
variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master);
if (embedded_media) {
GstTagList *tags = gst_stream_get_tags (gst_stream);
- GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream",
+ GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
embedded_media->name);
track =
new_track_for_rendition (hlsdemux, embedded_media, manifest_caps,
g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type),
i);
- GST_DEBUG_OBJECT (demux, "Adding track '%s' to main variant stream",
+ GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
stream_id);
track =
- gst_adaptive_demux_track_new (demux, stream_type,
+ gst_adaptive_demux_track_new (stream->demux, stream_type,
flags, stream_id, manifest_caps, NULL);
g_free (stream_id);
}
}
static gboolean
-gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
- GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
+ GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
const GstHLSKey *key;
GstHLSMediaPlaylist *m3u8;
key_failed:
{
- GST_ELEMENT_ERROR (demux, STREAM, DECRYPT_NOKEY,
+ GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY,
("Couldn't retrieve key for decryption"), (NULL));
- GST_WARNING_OBJECT (demux, "Failed to decrypt data");
+ GST_WARNING_OBJECT (hlsdemux, "Failed to decrypt data");
return FALSE;
}
decrypt_start_failed:
{
- GST_ELEMENT_ERROR (demux, STREAM, DECRYPT, ("Failed to start decrypt"),
+ GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT, ("Failed to start decrypt"),
("Couldn't set key and IV or plugin was built without crypto library"));
return FALSE;
}
}
static GstFlowReturn
-gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer, gboolean at_eos)
+gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream,
+ GstBuffer * buffer, gboolean at_eos)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
- GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
+ GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *pending_header_data = NULL;
}
static GstFlowReturn
-gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); // FIXME: pass HlsStream into function
GstFlowReturn ret = GST_FLOW_OK;
}
ret =
- gst_hls_demux_handle_buffer (demux, stream,
+ gst_hls_demux_stream_handle_buffer (stream,
hls_stream->pending_decrypted_buffer, TRUE);
hls_stream->pending_decrypted_buffer = NULL;
}
GstBuffer *buf = hls_stream->pending_typefind_buffer;
hls_stream->pending_typefind_buffer = NULL;
- gst_hls_demux_handle_buffer (demux, stream, buf, TRUE);
+ gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
}
if (hls_stream->pending_segment_data) {
GstBuffer *buf = hls_stream->pending_segment_data;
hls_stream->pending_segment_data = NULL;
- ret = gst_hls_demux_handle_buffer (demux, stream, buf, TRUE);
+ ret = gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
}
}
}
* before advancing. Note that we don't have any period so we can set the
* stream_time as-is on the stream current position */
stream->current_position = hls_stream->current_segment->stream_time;
- return gst_adaptive_demux2_stream_advance_fragment (demux, stream,
+ return gst_adaptive_demux2_stream_advance_fragment (stream,
hls_stream->current_segment->duration);
}
return ret;
}
static GstFlowReturn
-gst_hls_demux_data_received (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
+gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
+ GstBuffer * buffer)
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
- GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
+ GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
if (hls_stream->current_segment == NULL)
return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
decrypted_buffer =
gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
if (err) {
- GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
+ GST_ELEMENT_ERROR (hlsdemux, STREAM, DECODE, ("Failed to decrypt buffer"),
("decryption failed %s", err->message));
g_error_free (err);
return GST_FLOW_ERROR;
return GST_FLOW_OK;
}
- return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE);
+ return gst_hls_demux_stream_handle_buffer (stream, buffer, FALSE);
}
static void
}
static GstFlowReturn
-gst_hls_demux_advance_fragment (GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
{
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
}
static GstFlowReturn
-gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
{
GstFlowReturn ret = GST_FLOW_OK;
GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
}
static gboolean
-gst_hls_demux_stream_can_start (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream)
{
- GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
+ GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
GList *tmp;
- GST_DEBUG_OBJECT (demux, "is_variant:%d mappings:%p", hls_stream->is_variant,
+ GST_DEBUG_OBJECT (stream, "is_variant:%d mappings:%p", hls_stream->is_variant,
hlsdemux->mappings);
/* Variant streams can always start straight away */
}
static gboolean
-gst_hls_demux_select_bitrate (GstAdaptiveDemux2Stream * stream, guint64 bitrate)
+gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
+ guint64 bitrate)
{
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
}
static GstClockTime
-gst_hls_demux_get_presentation_offset (GstAdaptiveDemux * demux,
- GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
{
- GstHLSDemux *hlsdemux = (GstHLSDemux *) demux;
+ GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,