mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
adaptivedemux2: Refactor stream methods into the stream
Unlike the legacy elements, GstAdaptiveDemuxStream is a GObject now, so a bunch of things that were actually stream methods on the parent demux object can directly become stream methods now. Move the stream class out to a header of its own. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
This commit is contained in:
parent
9f89b8e3ef
commit
2fe641353d
8 changed files with 1139 additions and 1023 deletions
|
@ -351,18 +351,12 @@ static void gst_dash_demux_get_property (GObject * object, guint prop_id,
|
|||
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
|
||||
|
@ -371,25 +365,35 @@ static gboolean
|
|||
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);
|
||||
|
@ -464,8 +468,32 @@ static void
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -547,11 +575,10 @@ gst_dash_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
|
|||
}
|
||||
|
||||
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);
|
||||
|
@ -628,30 +655,12 @@ gst_dash_demux2_class_init (GstDashDemux2Class * klass)
|
|||
|
||||
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
|
||||
|
@ -1827,10 +1836,10 @@ gst_dash_demux_stream_get_target_time (GstDashDemux2 * dashdemux,
|
|||
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);
|
||||
|
||||
|
@ -2706,10 +2715,9 @@ _gst_buffer_split (GstBuffer * buffer, gint offset, gsize size)
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -2725,7 +2733,7 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
|
|||
* 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;
|
||||
|
||||
|
@ -2733,11 +2741,10 @@ gst_dash_demux_stream_fragment_start (GstAdaptiveDemux * demux,
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -2747,7 +2754,7 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
|||
* 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;
|
||||
|
||||
|
@ -2774,20 +2781,20 @@ gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
|||
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);
|
||||
|
@ -3371,9 +3378,9 @@ gst_dash_demux_find_sync_samples (GstAdaptiveDemux * demux,
|
|||
|
||||
|
||||
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;
|
||||
|
@ -3527,22 +3534,22 @@ gst_dash_demux_handle_isobmff (GstAdaptiveDemux * demux,
|
|||
|
||||
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;
|
||||
|
@ -3574,7 +3581,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
|||
|
||||
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;
|
||||
|
||||
|
@ -3622,7 +3629,7 @@ gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
|||
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 */
|
||||
|
|
|
@ -25,9 +25,16 @@
|
|||
#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
|
||||
|
||||
|
@ -166,10 +173,8 @@ GstFlowReturn gst_adaptive_demux_update_manifest (GstAdaptiveDemux *demux);
|
|||
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);
|
||||
|
@ -177,10 +182,7 @@ 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);
|
||||
|
@ -193,6 +195,7 @@ gboolean gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream
|
|||
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);
|
||||
|
@ -240,4 +243,6 @@ GstFlowReturn gst_adaptive_demux_period_combine_stream_flows (GstAdap
|
|||
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
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstadaptivedemux.h"
|
||||
#include "gstadaptivedemux-stream.h"
|
||||
#include "gstadaptivedemux-private.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
@ -38,6 +38,18 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
|
|||
|
||||
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,
|
||||
|
@ -49,6 +61,9 @@ gst_adaptive_demux2_stream_class_init (GstAdaptiveDemux2StreamClass * klass)
|
|||
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;
|
||||
|
@ -255,10 +270,10 @@ schedule_another_chunk (GstAdaptiveDemux2Stream * stream)
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -278,8 +293,8 @@ static void
|
|||
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,
|
||||
|
@ -313,12 +328,12 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
|||
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));
|
||||
|
@ -331,7 +346,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
|||
|| !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));
|
||||
|
@ -343,7 +358,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
|||
|
||||
/* 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 */
|
||||
|
@ -387,7 +402,7 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream *
|
|||
|
||||
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);
|
||||
}
|
||||
|
@ -414,13 +429,16 @@ gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream * stream,
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -478,8 +496,9 @@ gst_adaptive_demux2_stream_prepare_segment (GstAdaptiveDemux * demux,
|
|||
* 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));
|
||||
|
@ -542,7 +561,7 @@ update_buffer_pts_and_demux_position_locked (GstAdaptiveDemux * demux,
|
|||
|
||||
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;
|
||||
|
||||
|
@ -574,8 +593,7 @@ gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
|
|||
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;
|
||||
}
|
||||
|
@ -711,7 +729,8 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
|
|||
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 */
|
||||
|
@ -728,7 +747,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
|
|||
* 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;
|
||||
}
|
||||
|
||||
|
@ -738,7 +757,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
|
|||
"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",
|
||||
|
@ -759,7 +778,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream,
|
|||
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;
|
||||
|
||||
|
@ -1177,7 +1196,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
|||
|| 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) {
|
||||
|
@ -1195,7 +1214,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
|||
|
||||
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));
|
||||
|
||||
|
@ -1205,8 +1224,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
|||
} 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,
|
||||
|
@ -1230,7 +1248,7 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
|
|||
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 */
|
||||
|
@ -1421,7 +1439,8 @@ static GstFlowReturn
|
|||
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 : */
|
||||
|
@ -1760,7 +1779,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
|
|||
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));
|
||||
|
||||
|
@ -1795,7 +1814,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
|
|||
/* 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,
|
||||
|
@ -1967,12 +1986,12 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream)
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2096,3 +2115,492 @@ gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream * 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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/* 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
|
|
@ -0,0 +1,55 @@
|
|||
/* 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
|
||||
|
|
@ -229,8 +229,6 @@ static void gst_adaptive_demux_reset (GstAdaptiveDemux * demux);
|
|||
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);
|
||||
|
||||
|
@ -242,19 +240,6 @@ static void gst_adaptive_demux_start_manifest_update_task (GstAdaptiveDemux *
|
|||
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
|
||||
|
@ -532,19 +517,15 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -1468,123 +1449,6 @@ find_stream_for_element_locked (GstAdaptiveDemux * demux, GstObject * o)
|
|||
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)
|
||||
|
@ -1606,60 +1470,38 @@ gst_adaptive_demux_handle_stream_collection_msg (GstAdaptiveDemux * demux,
|
|||
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 (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;
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
@ -1741,21 +1583,6 @@ gst_adaptive_demux_handle_message (GstBin * bin, GstMessage * msg)
|
|||
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
|
@ -1813,7 +1640,7 @@ gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
|
|||
/* 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,
|
||||
|
@ -3008,130 +2835,6 @@ gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
|
|||
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)
|
||||
|
@ -3877,181 +3580,6 @@ gst_adaptive_demux2_stream_seek (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)
|
||||
|
@ -4160,29 +3688,6 @@ gst_adaptive_demux_update_manifest (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)
|
||||
|
|
|
@ -34,32 +34,11 @@
|
|||
|
||||
#include "gstadaptivedemuxutils.h"
|
||||
|
||||
#include "gstadaptivedemux-types.h"
|
||||
#include "gstadaptivedemux-stream.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;
|
||||
|
||||
|
||||
/**
|
||||
* GST_ADAPTIVE_DEMUX_SINK_NAME:
|
||||
*
|
||||
|
@ -77,8 +56,6 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass;
|
|||
|
||||
#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:
|
||||
*
|
||||
|
@ -101,48 +78,15 @@ typedef GstObjectClass GstAdaptiveDemux2StreamClass;
|
|||
#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;
|
||||
|
@ -249,119 +193,6 @@ struct _GstAdaptiveDemuxTrack
|
|||
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:
|
||||
*
|
||||
|
@ -574,116 +405,6 @@ struct _GstAdaptiveDemuxClass
|
|||
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:
|
||||
|
@ -697,18 +418,6 @@ struct _GstAdaptiveDemuxClass
|
|||
*/
|
||||
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
|
||||
|
@ -739,8 +448,6 @@ struct _GstAdaptiveDemuxClass
|
|||
|
||||
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);
|
||||
|
||||
|
@ -757,34 +464,9 @@ GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *trac
|
|||
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);
|
||||
|
|
|
@ -116,36 +116,32 @@ gst_hls_update_time_mappings (GstHLSDemux * 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);
|
||||
|
||||
|
@ -161,8 +157,31 @@ static void
|
|||
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
|
||||
|
@ -181,6 +200,12 @@ typedef struct _GstHLSDemux2Class GstHLSDemux2Class;
|
|||
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)
|
||||
{
|
||||
|
@ -263,8 +288,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
|
|||
|
||||
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;
|
||||
|
@ -273,19 +296,6 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
|
|||
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
|
||||
|
@ -617,16 +627,15 @@ get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype)
|
|||
}
|
||||
|
||||
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);
|
||||
|
@ -662,7 +671,7 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
|
|||
|
||||
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,
|
||||
|
@ -673,10 +682,10 @@ gst_hls_demux_stream_update_tracks (GstAdaptiveDemux * demux,
|
|||
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);
|
||||
}
|
||||
|
@ -1037,11 +1046,10 @@ out:
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1069,14 +1077,14 @@ gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
|||
|
||||
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;
|
||||
}
|
||||
|
@ -1480,11 +1488,11 @@ out_resync:
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1596,8 +1604,7 @@ out:
|
|||
}
|
||||
|
||||
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;
|
||||
|
@ -1626,7 +1633,7 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1636,14 +1643,14 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1664,18 +1671,18 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
|||
* 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;
|
||||
|
@ -1707,7 +1714,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
|||
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;
|
||||
|
@ -1720,7 +1727,7 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
|||
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
|
||||
|
@ -1794,7 +1801,7 @@ gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
|
|||
}
|
||||
|
||||
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;
|
||||
|
@ -2330,7 +2337,7 @@ gst_hls_demux_stream_update_variant_playlist (GstHLSDemux * 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);
|
||||
|
@ -2456,14 +2463,13 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * 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 */
|
||||
|
@ -2556,7 +2562,8 @@ gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
|
|||
}
|
||||
|
||||
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);
|
||||
|
@ -2964,10 +2971,9 @@ gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * 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,
|
||||
|
|
Loading…
Reference in a new issue