mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-07-06 14:45:54 +00:00
adaptivedemux: refactor chunk downloading flow
Add more power to the chunk_received function (renamed to data_received) and also to the fragment_finish function. The data_received function must parse/decrypt the data if necessary and also push it using the new push_buffer function that is exposed now. The default implementation gets data from the stream adapter (all available) and pushes it. The fragment_finish function must also advance the fragment. The default implementation only advances the fragment. This allows the subsegment handling in dashdemux to continuously download the same file from the server instead of stopping at every subsegment boundary and starting a new request
This commit is contained in:
parent
15d51c1f6c
commit
229a15b393
|
@ -200,7 +200,7 @@ static GstFlowReturn gst_dash_demux_stream_seek (GstAdaptiveDemuxStream *
|
||||||
stream, GstClockTime ts);
|
stream, GstClockTime ts);
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_dash_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream);
|
gst_dash_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream);
|
||||||
static void
|
static gboolean
|
||||||
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream);
|
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream);
|
||||||
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream *
|
static gboolean gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream *
|
||||||
stream, guint64 bitrate);
|
stream, guint64 bitrate);
|
||||||
|
@ -213,8 +213,11 @@ gst_dash_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
|
||||||
stream);
|
stream);
|
||||||
static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
|
static void gst_dash_demux_advance_period (GstAdaptiveDemux * demux);
|
||||||
static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux);
|
static gboolean gst_dash_demux_has_next_period (GstAdaptiveDemux * demux);
|
||||||
static GstFlowReturn gst_dash_demux_chunk_received (GstAdaptiveDemux * demux,
|
static GstFlowReturn gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** chunk);
|
GstAdaptiveDemuxStream * stream);
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream);
|
||||||
|
|
||||||
/* GstDashDemux */
|
/* GstDashDemux */
|
||||||
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux);
|
static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux);
|
||||||
|
@ -600,7 +603,8 @@ gst_dash_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
|
||||||
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
|
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
|
||||||
klass = GST_ADAPTIVE_DEMUX_GET_CLASS (dashdemux);
|
klass = GST_ADAPTIVE_DEMUX_GET_CLASS (dashdemux);
|
||||||
|
|
||||||
klass->chunk_received = gst_dash_demux_chunk_received;
|
klass->data_received = gst_dash_demux_data_received;
|
||||||
|
klass->finish_fragment = gst_dash_demux_stream_fragment_finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gst_mpd_client_setup_media_presentation (dashdemux->client)) {
|
if (gst_mpd_client_setup_media_presentation (dashdemux->client)) {
|
||||||
|
@ -882,7 +886,7 @@ gst_dash_demux_stream_seek (GstAdaptiveDemuxStream * stream, GstClockTime ts)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream)
|
gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream)
|
||||||
{
|
{
|
||||||
GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
|
GstDashDemuxStream *dashstream = (GstDashDemuxStream *) stream;
|
||||||
|
@ -905,6 +909,7 @@ gst_dash_demux_stream_advance_subfragment (GstAdaptiveDemuxStream * stream)
|
||||||
if (!fragment_finished) {
|
if (!fragment_finished) {
|
||||||
dashstream->sidx_current_remaining = sidx->entries[sidx->entry_index].size;
|
dashstream->sidx_current_remaining = sidx->entries[sidx->entry_index].size;
|
||||||
}
|
}
|
||||||
|
return !fragment_finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
@ -914,17 +919,8 @@ gst_dash_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream)
|
||||||
GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
|
GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (stream->demux);
|
||||||
|
|
||||||
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
|
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
|
||||||
GstSidxBox *sidx = SIDX (dashstream);
|
if (gst_dash_demux_stream_advance_subfragment (stream))
|
||||||
|
return GST_FLOW_OK;
|
||||||
if (stream->demux->segment.rate > 0.0) {
|
|
||||||
if (sidx->entry_index < sidx->entries_count) {
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (sidx->entry_index >= 0) {
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_mpd_client_advance_segment (dashdemux->client,
|
return gst_mpd_client_advance_segment (dashdemux->client,
|
||||||
|
@ -986,22 +982,14 @@ gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
|
||||||
|
|
||||||
if (gst_mpd_client_has_isoff_ondemand_profile (demux->client)) {
|
if (gst_mpd_client_has_isoff_ondemand_profile (demux->client)) {
|
||||||
|
|
||||||
/* a new subsegment is going to start, cleanup any pending data from the
|
/* store our current position to change to the same one in a different
|
||||||
* previous one */
|
* representation if needed */
|
||||||
dashstream->sidx_index = SIDX (dashstream)->entry_index;
|
dashstream->sidx_index = SIDX (dashstream)->entry_index;
|
||||||
if (dashstream->pending_buffer) {
|
|
||||||
gst_buffer_unref (dashstream->pending_buffer);
|
|
||||||
dashstream->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* TODO cache indexes to avoid re-downloading and parsing */
|
/* TODO cache indexes to avoid re-downloading and parsing */
|
||||||
/* if we switched, we need a new index */
|
/* if we switched, we need a new index */
|
||||||
gst_isoff_sidx_parser_clear (&dashstream->sidx_parser);
|
gst_isoff_sidx_parser_clear (&dashstream->sidx_parser);
|
||||||
gst_isoff_sidx_parser_init (&dashstream->sidx_parser);
|
gst_isoff_sidx_parser_init (&dashstream->sidx_parser);
|
||||||
} else {
|
|
||||||
dashstream->sidx_current_remaining =
|
|
||||||
SIDX_ENTRY (dashstream, dashstream->sidx_index)->size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,11 +1059,6 @@ gst_dash_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
||||||
gst_isoff_sidx_parser_clear (&dashstream->sidx_parser);
|
gst_isoff_sidx_parser_clear (&dashstream->sidx_parser);
|
||||||
gst_isoff_sidx_parser_init (&dashstream->sidx_parser);
|
gst_isoff_sidx_parser_init (&dashstream->sidx_parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dashstream->pending_buffer) {
|
|
||||||
gst_buffer_unref (dashstream->pending_buffer);
|
|
||||||
dashstream->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
gst_dash_demux_stream_seek (iter->data, target_pos);
|
gst_dash_demux_stream_seek (iter->data, target_pos);
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -1247,88 +1230,112 @@ gst_dash_demux_advance_period (GstAdaptiveDemux * demux)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstBuffer *
|
static GstBuffer *
|
||||||
_gst_buffer_split (GstBuffer ** buffer, gint offset, gsize size)
|
_gst_buffer_split (GstBuffer * buffer, gint offset, gsize size)
|
||||||
{
|
{
|
||||||
GstBuffer *newbuf = gst_buffer_copy_region (*buffer,
|
GstBuffer *newbuf = gst_buffer_copy_region (buffer,
|
||||||
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META
|
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META
|
||||||
| GST_BUFFER_COPY_MEMORY, offset, size - offset);
|
| GST_BUFFER_COPY_MEMORY, offset, size - offset);
|
||||||
|
|
||||||
gst_buffer_resize (*buffer, 0, offset);
|
gst_buffer_resize (buffer, 0, offset);
|
||||||
|
|
||||||
return newbuf;
|
return newbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_dash_demux_chunk_received (GstAdaptiveDemux * demux,
|
gst_dash_demux_stream_fragment_finished (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** chunk)
|
GstAdaptiveDemuxStream * stream)
|
||||||
|
{
|
||||||
|
GstDashDemux *dashdemux = GST_DASH_DEMUX_CAST (demux);
|
||||||
|
|
||||||
|
if (gst_mpd_client_has_isoff_ondemand_profile (dashdemux->client)) {
|
||||||
|
/* fragment is advanced on data_received when byte limits are reached */
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
} else {
|
||||||
|
return gst_adaptive_demux_stream_advance_fragment (demux, stream,
|
||||||
|
stream->fragment.duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_dash_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream)
|
||||||
{
|
{
|
||||||
GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream;
|
GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
gsize available;
|
||||||
|
|
||||||
if (*chunk == NULL) {
|
if (stream->downloading_index) {
|
||||||
if (dash_stream->pending_buffer) {
|
|
||||||
*chunk = dash_stream->pending_buffer;
|
|
||||||
dash_stream->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dash_stream->pending_buffer) {
|
|
||||||
*chunk = gst_buffer_append (dash_stream->pending_buffer, *chunk);
|
|
||||||
dash_stream->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream->downloading_index
|
|
||||||
&& dash_stream->sidx_parser.status != GST_ISOFF_SIDX_PARSER_FINISHED) {
|
|
||||||
GstIsoffParserResult res;
|
GstIsoffParserResult res;
|
||||||
guint consumed;
|
guint consumed;
|
||||||
|
|
||||||
res =
|
available = gst_adapter_available (stream->adapter);
|
||||||
gst_isoff_sidx_parser_add_buffer (&dash_stream->sidx_parser, *chunk,
|
buffer = gst_adapter_take_buffer (stream->adapter, available);
|
||||||
&consumed);
|
|
||||||
|
|
||||||
if (res == GST_ISOFF_PARSER_ERROR) {
|
if (dash_stream->sidx_parser.status != GST_ISOFF_SIDX_PARSER_FINISHED) {
|
||||||
} else if (res == GST_ISOFF_PARSER_UNEXPECTED) {
|
res =
|
||||||
/* this is not a 'sidx' index, just skip it and continue playback */
|
gst_isoff_sidx_parser_add_buffer (&dash_stream->sidx_parser, buffer,
|
||||||
} else {
|
&consumed);
|
||||||
/* when finished, prepare for real data streaming */
|
|
||||||
if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
|
if (res == GST_ISOFF_PARSER_ERROR) {
|
||||||
if (GST_CLOCK_TIME_IS_VALID (dash_stream->pending_seek_ts)) {
|
} else if (res == GST_ISOFF_PARSER_UNEXPECTED) {
|
||||||
gst_dash_demux_stream_sidx_seek (dash_stream,
|
/* this is not a 'sidx' index, just skip it and continue playback */
|
||||||
dash_stream->pending_seek_ts);
|
} else {
|
||||||
dash_stream->pending_seek_ts = GST_CLOCK_TIME_NONE;
|
/* when finished, prepare for real data streaming */
|
||||||
} else {
|
if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
|
||||||
SIDX (dash_stream)->entry_index = dash_stream->sidx_index;
|
if (GST_CLOCK_TIME_IS_VALID (dash_stream->pending_seek_ts)) {
|
||||||
|
gst_dash_demux_stream_sidx_seek (dash_stream,
|
||||||
|
dash_stream->pending_seek_ts);
|
||||||
|
dash_stream->pending_seek_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
} else {
|
||||||
|
SIDX (dash_stream)->entry_index = dash_stream->sidx_index;
|
||||||
|
}
|
||||||
|
dash_stream->sidx_current_remaining =
|
||||||
|
SIDX_CURRENT_ENTRY (dash_stream)->size;
|
||||||
|
} else if (consumed < available) {
|
||||||
|
GstBuffer *pending;
|
||||||
|
/* we still need to keep some data around for the next parsing round
|
||||||
|
* so just push what was already processed by the parser */
|
||||||
|
pending = _gst_buffer_split (buffer, consumed, -1);
|
||||||
|
gst_adapter_push (stream->adapter, pending);
|
||||||
}
|
}
|
||||||
dash_stream->sidx_current_remaining =
|
|
||||||
SIDX_CURRENT_ENTRY (dash_stream)->size;
|
|
||||||
} else if (consumed < gst_buffer_get_size (*chunk)) {
|
|
||||||
dash_stream->pending_buffer = _gst_buffer_split (chunk, consumed, -1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ret = gst_adaptive_demux_stream_push_buffer (stream, buffer);
|
||||||
|
} else if (dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
|
||||||
|
|
||||||
/* check our position in subsegments */
|
while (ret == GST_FLOW_OK
|
||||||
if (!stream->downloading_index
|
&& ((available = gst_adapter_available (stream->adapter)) > 0)) {
|
||||||
&& dash_stream->sidx_parser.status == GST_ISOFF_SIDX_PARSER_FINISHED) {
|
gboolean advance = FALSE;
|
||||||
gsize size = gst_buffer_get_size (*chunk);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (stream->pad,
|
if (available < dash_stream->sidx_current_remaining) {
|
||||||
"Received buffer of size: %" G_GSIZE_FORMAT
|
buffer = gst_adapter_take_buffer (stream->adapter, available);
|
||||||
" - remaining in subsegment: %" G_GSIZE_FORMAT, size,
|
dash_stream->sidx_current_remaining -= available;
|
||||||
dash_stream->sidx_current_remaining);
|
} else {
|
||||||
if (size < dash_stream->sidx_current_remaining) {
|
buffer =
|
||||||
dash_stream->sidx_current_remaining -= size;
|
gst_adapter_take_buffer (stream->adapter,
|
||||||
} else if (size >= dash_stream->sidx_current_remaining) {
|
dash_stream->sidx_current_remaining);
|
||||||
if (size > dash_stream->sidx_current_remaining) {
|
dash_stream->sidx_current_remaining = 0;
|
||||||
dash_stream->pending_buffer =
|
advance = TRUE;
|
||||||
_gst_buffer_split (chunk, dash_stream->sidx_current_remaining,
|
|
||||||
size);
|
|
||||||
}
|
}
|
||||||
|
ret = gst_adaptive_demux_stream_push_buffer (stream, buffer);
|
||||||
|
if (advance) {
|
||||||
|
GstFlowReturn new_ret;
|
||||||
|
new_ret =
|
||||||
|
gst_adaptive_demux_stream_advance_fragment (demux, stream,
|
||||||
|
SIDX_CURRENT_ENTRY (dash_stream)->duration);
|
||||||
|
|
||||||
gst_dash_demux_stream_advance_subfragment (stream);
|
/* only overwrite if it was OK before */
|
||||||
ret = (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SUBSEGMENT_END;
|
if (ret == GST_FLOW_OK)
|
||||||
|
ret = new_ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* this should be the main header, just push it all */
|
||||||
|
ret =
|
||||||
|
gst_adaptive_demux_stream_push_buffer (stream,
|
||||||
|
gst_adapter_take_buffer (stream->adapter,
|
||||||
|
gst_adapter_available (stream->adapter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1340,6 +1347,4 @@ gst_dash_demux_stream_free (GstAdaptiveDemuxStream * stream)
|
||||||
GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream;
|
GstDashDemuxStream *dash_stream = (GstDashDemuxStream *) stream;
|
||||||
|
|
||||||
gst_isoff_sidx_parser_clear (&dash_stream->sidx_parser);
|
gst_isoff_sidx_parser_clear (&dash_stream->sidx_parser);
|
||||||
if (dash_stream->pending_buffer)
|
|
||||||
gst_buffer_unref (dash_stream->pending_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,6 @@ struct _GstDashDemuxStream
|
||||||
|
|
||||||
GstMediaFragmentInfo current_fragment;
|
GstMediaFragmentInfo current_fragment;
|
||||||
|
|
||||||
GstBuffer *pending_buffer;
|
|
||||||
|
|
||||||
/* index parsing */
|
/* index parsing */
|
||||||
GstSidxParser sidx_parser;
|
GstSidxParser sidx_parser;
|
||||||
gsize sidx_current_remaining;
|
gsize sidx_current_remaining;
|
||||||
|
|
|
@ -111,10 +111,10 @@ static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream);
|
GstAdaptiveDemuxStream * stream);
|
||||||
static void gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
static GstFlowReturn gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** buffer);
|
GstAdaptiveDemuxStream * stream);
|
||||||
static GstFlowReturn gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
static GstFlowReturn gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** chunk);
|
GstAdaptiveDemuxStream * stream);
|
||||||
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemuxStream *
|
static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemuxStream *
|
||||||
stream);
|
stream);
|
||||||
static GstFlowReturn gst_hls_demux_advance_fragment (GstAdaptiveDemuxStream *
|
static GstFlowReturn gst_hls_demux_advance_fragment (GstAdaptiveDemuxStream *
|
||||||
|
@ -216,7 +216,7 @@ gst_hls_demux_class_init (GstHLSDemuxClass * klass)
|
||||||
|
|
||||||
adaptivedemux_class->start_fragment = gst_hls_demux_start_fragment;
|
adaptivedemux_class->start_fragment = gst_hls_demux_start_fragment;
|
||||||
adaptivedemux_class->finish_fragment = gst_hls_demux_finish_fragment;
|
adaptivedemux_class->finish_fragment = gst_hls_demux_finish_fragment;
|
||||||
adaptivedemux_class->chunk_received = gst_hls_demux_chunk_received;
|
adaptivedemux_class->data_received = gst_hls_demux_data_received;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_hls_demux_debug, "hlsdemux", 0,
|
GST_DEBUG_CATEGORY_INIT (gst_hls_demux_debug, "hlsdemux", 0,
|
||||||
"hlsdemux element");
|
"hlsdemux element");
|
||||||
|
@ -289,9 +289,6 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition)
|
||||||
gst_hls_demux_reset (GST_ADAPTIVE_DEMUX_CAST (demux));
|
gst_hls_demux_reset (GST_ADAPTIVE_DEMUX_CAST (demux));
|
||||||
gst_uri_downloader_reset (demux->downloader);
|
gst_uri_downloader_reset (demux->downloader);
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
||||||
demux->adapter = gst_adapter_new ();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -302,10 +299,6 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition)
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
gst_hls_demux_reset (GST_ADAPTIVE_DEMUX_CAST (demux));
|
gst_hls_demux_reset (GST_ADAPTIVE_DEMUX_CAST (demux));
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
||||||
gst_object_unref (demux->adapter);
|
|
||||||
demux->adapter = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -364,11 +357,6 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
||||||
|
|
||||||
/* properly cleanup pending decryption status */
|
/* properly cleanup pending decryption status */
|
||||||
if (flags & GST_SEEK_FLAG_FLUSH) {
|
if (flags & GST_SEEK_FLAG_FLUSH) {
|
||||||
if (hlsdemux->adapter)
|
|
||||||
gst_adapter_clear (hlsdemux->adapter);
|
|
||||||
if (hlsdemux->pending_buffer)
|
|
||||||
gst_buffer_unref (hlsdemux->pending_buffer);
|
|
||||||
hlsdemux->pending_buffer = NULL;
|
|
||||||
gst_hls_demux_decrypt_end (hlsdemux);
|
gst_hls_demux_decrypt_end (hlsdemux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,9 +581,9 @@ key_failed:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static GstFlowReturn
|
||||||
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** buffer)
|
GstAdaptiveDemuxStream * stream)
|
||||||
{
|
{
|
||||||
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||||
|
|
||||||
|
@ -605,8 +593,8 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
/* ideally this should be empty, but this eos might have been
|
/* ideally this should be empty, but this eos might have been
|
||||||
* caused by an error on the source element */
|
* caused by an error on the source element */
|
||||||
GST_DEBUG_OBJECT (demux, "Data still on the adapter when EOS was received"
|
GST_DEBUG_OBJECT (demux, "Data still on the adapter when EOS was received"
|
||||||
": %" G_GSIZE_FORMAT, gst_adapter_available (hlsdemux->adapter));
|
": %" G_GSIZE_FORMAT, gst_adapter_available (stream->adapter));
|
||||||
gst_adapter_clear (hlsdemux->adapter);
|
gst_adapter_clear (stream->adapter);
|
||||||
|
|
||||||
/* pending buffer is only used for encrypted streams */
|
/* pending buffer is only used for encrypted streams */
|
||||||
if (stream->last_ret == GST_FLOW_OK) {
|
if (stream->last_ret == GST_FLOW_OK) {
|
||||||
|
@ -621,40 +609,41 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
gst_buffer_resize (hlsdemux->pending_buffer, 0, unpadded_size);
|
gst_buffer_resize (hlsdemux->pending_buffer, 0, unpadded_size);
|
||||||
|
|
||||||
*buffer = hlsdemux->pending_buffer;
|
gst_adaptive_demux_stream_push_buffer (stream, hlsdemux->pending_buffer);
|
||||||
hlsdemux->pending_buffer = NULL;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hlsdemux->pending_buffer)
|
if (hlsdemux->pending_buffer)
|
||||||
gst_buffer_unref (hlsdemux->pending_buffer);
|
gst_buffer_unref (hlsdemux->pending_buffer);
|
||||||
hlsdemux->pending_buffer = NULL;
|
hlsdemux->pending_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return gst_adaptive_demux_stream_advance_fragment (demux, stream,
|
||||||
|
stream->fragment.duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
gst_hls_demux_data_received (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstBuffer ** chunk)
|
GstAdaptiveDemuxStream * stream)
|
||||||
{
|
{
|
||||||
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||||
GstBuffer *buffer = *chunk;
|
gsize available;
|
||||||
|
GstBuffer *buffer = NULL;
|
||||||
|
|
||||||
|
available = gst_adapter_available (stream->adapter);
|
||||||
|
|
||||||
/* Is it encrypted? */
|
/* Is it encrypted? */
|
||||||
if (hlsdemux->current_key) {
|
if (hlsdemux->current_key) {
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
GstBuffer *tmp_buffer;
|
GstBuffer *tmp_buffer;
|
||||||
gsize available;
|
|
||||||
|
|
||||||
gst_adapter_push (hlsdemux->adapter, buffer);
|
|
||||||
*chunk = NULL;
|
|
||||||
|
|
||||||
/* must be a multiple of 16 */
|
/* must be a multiple of 16 */
|
||||||
available = gst_adapter_available (hlsdemux->adapter) & (~0xF);
|
available = available & (~0xF);
|
||||||
|
|
||||||
if (available == 0) {
|
if (available == 0) {
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = gst_adapter_take_buffer (hlsdemux->adapter, available);
|
buffer = gst_adapter_take_buffer (stream->adapter, available);
|
||||||
buffer = gst_hls_demux_decrypt_fragment (hlsdemux, buffer, &err);
|
buffer = gst_hls_demux_decrypt_fragment (hlsdemux, buffer, &err);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
|
GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
|
||||||
|
@ -665,14 +654,15 @@ gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
||||||
|
|
||||||
tmp_buffer = hlsdemux->pending_buffer;
|
tmp_buffer = hlsdemux->pending_buffer;
|
||||||
hlsdemux->pending_buffer = buffer;
|
hlsdemux->pending_buffer = buffer;
|
||||||
*chunk = tmp_buffer;
|
buffer = tmp_buffer;
|
||||||
} else if (hlsdemux->pending_buffer) {
|
} else {
|
||||||
*chunk = gst_buffer_append (hlsdemux->pending_buffer, buffer);
|
buffer = gst_adapter_take_buffer (stream->adapter, available);
|
||||||
hlsdemux->pending_buffer = NULL;
|
if (hlsdemux->pending_buffer) {
|
||||||
|
buffer = gst_buffer_append (hlsdemux->pending_buffer, buffer);
|
||||||
|
hlsdemux->pending_buffer = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = *chunk;
|
|
||||||
|
|
||||||
if (G_UNLIKELY (hlsdemux->do_typefind && buffer != NULL)) {
|
if (G_UNLIKELY (hlsdemux->do_typefind && buffer != NULL)) {
|
||||||
GstCaps *caps = NULL;
|
GstCaps *caps = NULL;
|
||||||
GstMapInfo info;
|
GstMapInfo info;
|
||||||
|
@ -697,6 +687,7 @@ gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
||||||
if (buffer_size > (2 * 1024 * 1024)) {
|
if (buffer_size > (2 * 1024 * 1024)) {
|
||||||
GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
|
GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
|
||||||
("Could not determine type of stream"), (NULL));
|
("Could not determine type of stream"), (NULL));
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
return GST_FLOW_NOT_NEGOTIATED;
|
return GST_FLOW_NOT_NEGOTIATED;
|
||||||
} else {
|
} else {
|
||||||
if (hlsdemux->pending_buffer)
|
if (hlsdemux->pending_buffer)
|
||||||
|
@ -704,7 +695,6 @@ gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
||||||
gst_buffer_append (buffer, hlsdemux->pending_buffer);
|
gst_buffer_append (buffer, hlsdemux->pending_buffer);
|
||||||
else
|
else
|
||||||
hlsdemux->pending_buffer = buffer;
|
hlsdemux->pending_buffer = buffer;
|
||||||
*chunk = NULL;
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,6 +712,9 @@ gst_hls_demux_chunk_received (GstAdaptiveDemux * demux,
|
||||||
hlsdemux->do_typefind = FALSE;
|
hlsdemux->do_typefind = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
return gst_adaptive_demux_stream_push_buffer (stream, buffer);
|
||||||
|
}
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,8 +835,6 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
|
||||||
demux->client = gst_m3u8_client_new ("", NULL);
|
demux->client = gst_m3u8_client_new ("", NULL);
|
||||||
|
|
||||||
demux->srcpad_counter = 0;
|
demux->srcpad_counter = 0;
|
||||||
if (demux->adapter)
|
|
||||||
gst_adapter_clear (demux->adapter);
|
|
||||||
if (demux->pending_buffer)
|
if (demux->pending_buffer)
|
||||||
gst_buffer_unref (demux->pending_buffer);
|
gst_buffer_unref (demux->pending_buffer);
|
||||||
demux->pending_buffer = NULL;
|
demux->pending_buffer = NULL;
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#define __GST_HLS_DEMUX_H__
|
#define __GST_HLS_DEMUX_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstadapter.h>
|
|
||||||
#include "m3u8.h"
|
#include "m3u8.h"
|
||||||
#include "gstfragmented.h"
|
#include "gstfragmented.h"
|
||||||
#include <gst/uridownloader/gsturidownloader.h>
|
#include <gst/uridownloader/gsturidownloader.h>
|
||||||
|
@ -96,7 +95,6 @@ struct _GstHLSDemux
|
||||||
#endif
|
#endif
|
||||||
gchar *current_key;
|
gchar *current_key;
|
||||||
guint8 *current_iv;
|
guint8 *current_iv;
|
||||||
GstAdapter *adapter; /* used to accumulate 16 bytes multiple chunks */
|
|
||||||
GstBuffer *pending_buffer; /* decryption scenario:
|
GstBuffer *pending_buffer; /* decryption scenario:
|
||||||
* the last buffer can only be pushed when
|
* the last buffer can only be pushed when
|
||||||
* resized, so need to store and wait for
|
* resized, so need to store and wait for
|
||||||
|
|
|
@ -87,6 +87,11 @@ GST_DEBUG_CATEGORY (adaptivedemux_debug);
|
||||||
#define MAX_DOWNLOAD_ERROR_COUNT 3
|
#define MAX_DOWNLOAD_ERROR_COUNT 3
|
||||||
#define DEFAULT_FAILED_COUNT 3
|
#define DEFAULT_FAILED_COUNT 3
|
||||||
|
|
||||||
|
enum GstAdaptiveDemuxFlowReturn
|
||||||
|
{
|
||||||
|
GST_ADAPTIVE_DEMUX_FLOW_SWITCH = GST_FLOW_CUSTOM_SUCCESS_2 + 1
|
||||||
|
};
|
||||||
|
|
||||||
struct _GstAdaptiveDemuxPrivate
|
struct _GstAdaptiveDemuxPrivate
|
||||||
{
|
{
|
||||||
GstAdapter *input_adapter;
|
GstAdapter *input_adapter;
|
||||||
|
@ -141,9 +146,6 @@ static GstFlowReturn gst_adaptive_demux_stream_seek (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream, GstClockTime ts);
|
GstAdaptiveDemuxStream * stream, GstClockTime ts);
|
||||||
static gboolean gst_adaptive_demux_stream_has_next_fragment (GstAdaptiveDemux *
|
static gboolean gst_adaptive_demux_stream_has_next_fragment (GstAdaptiveDemux *
|
||||||
demux, GstAdaptiveDemuxStream * stream);
|
demux, GstAdaptiveDemuxStream * stream);
|
||||||
static GstFlowReturn
|
|
||||||
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
|
|
||||||
GstAdaptiveDemuxStream * stream);
|
|
||||||
static gboolean gst_adaptive_demux_stream_select_bitrate (GstAdaptiveDemux *
|
static gboolean gst_adaptive_demux_stream_select_bitrate (GstAdaptiveDemux *
|
||||||
demux, GstAdaptiveDemuxStream * stream, guint64 bitrate);
|
demux, GstAdaptiveDemuxStream * stream, guint64 bitrate);
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
@ -171,6 +173,12 @@ static GstFlowReturn gst_adaptive_demux_combine_flows (GstAdaptiveDemux *
|
||||||
static void
|
static void
|
||||||
gst_adaptive_demux_stream_fragment_download_finish (GstAdaptiveDemuxStream *
|
gst_adaptive_demux_stream_fragment_download_finish (GstAdaptiveDemuxStream *
|
||||||
stream, GstFlowReturn ret, GError * err);
|
stream, GstFlowReturn ret, GError * err);
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_data_received_default (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream);
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_finish_fragment_default (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream);
|
||||||
|
|
||||||
|
|
||||||
/* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
|
/* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
|
||||||
|
@ -223,6 +231,9 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
|
||||||
gstelement_class->change_state = gst_adaptive_demux_change_state;
|
gstelement_class->change_state = gst_adaptive_demux_change_state;
|
||||||
|
|
||||||
gstbin_class->handle_message = gst_adaptive_demux_handle_message;
|
gstbin_class->handle_message = gst_adaptive_demux_handle_message;
|
||||||
|
|
||||||
|
klass->data_received = gst_adaptive_demux_stream_data_received_default;
|
||||||
|
klass->finish_fragment = gst_adaptive_demux_stream_finish_fragment_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -251,7 +262,6 @@ gst_adaptive_demux_init (GstAdaptiveDemux * demux,
|
||||||
g_cond_init (&demux->priv->updates_timed_cond);
|
g_cond_init (&demux->priv->updates_timed_cond);
|
||||||
|
|
||||||
g_cond_init (&demux->manifest_cond);
|
g_cond_init (&demux->manifest_cond);
|
||||||
|
|
||||||
g_mutex_init (&demux->manifest_lock);
|
g_mutex_init (&demux->manifest_lock);
|
||||||
|
|
||||||
pad_template =
|
pad_template =
|
||||||
|
@ -299,8 +309,6 @@ gst_adaptive_demux_change_state (GstElement * element,
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
gst_adaptive_demux_reset (demux);
|
gst_adaptive_demux_reset (demux);
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -704,6 +712,7 @@ gst_adaptive_demux_stream_new (GstAdaptiveDemux * demux, GstPad * pad)
|
||||||
gst_segment_init (&stream->segment, GST_FORMAT_TIME);
|
gst_segment_init (&stream->segment, GST_FORMAT_TIME);
|
||||||
g_cond_init (&stream->fragment_download_cond);
|
g_cond_init (&stream->fragment_download_cond);
|
||||||
g_mutex_init (&stream->fragment_download_lock);
|
g_mutex_init (&stream->fragment_download_lock);
|
||||||
|
stream->adapter = gst_adapter_new ();
|
||||||
|
|
||||||
demux->next_streams = g_list_append (demux->next_streams, stream);
|
demux->next_streams = g_list_append (demux->next_streams, stream);
|
||||||
|
|
||||||
|
@ -763,6 +772,9 @@ gst_adaptive_demux_stream_free (GstAdaptiveDemuxStream * stream)
|
||||||
}
|
}
|
||||||
if (stream->pending_caps)
|
if (stream->pending_caps)
|
||||||
gst_caps_unref (stream->pending_caps);
|
gst_caps_unref (stream->pending_caps);
|
||||||
|
|
||||||
|
g_object_unref (stream->adapter);
|
||||||
|
|
||||||
g_free (stream);
|
g_free (stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1110,6 +1122,7 @@ gst_adaptive_demux_stop_tasks (GstAdaptiveDemux * demux)
|
||||||
gst_task_join (stream->download_task);
|
gst_task_join (stream->download_task);
|
||||||
stream->download_error_count = 0;
|
stream->download_error_count = 0;
|
||||||
stream->need_header = TRUE;
|
stream->need_header = TRUE;
|
||||||
|
gst_adapter_clear (stream->adapter);
|
||||||
}
|
}
|
||||||
gst_task_join (demux->priv->updates_task);
|
gst_task_join (demux->priv->updates_task);
|
||||||
}
|
}
|
||||||
|
@ -1167,6 +1180,9 @@ gst_adaptive_demux_stream_update_current_bitrate (GstAdaptiveDemuxStream *
|
||||||
if (bitrate > G_MAXINT)
|
if (bitrate > G_MAXINT)
|
||||||
bitrate = G_MAXINT;
|
bitrate = G_MAXINT;
|
||||||
stream->current_download_rate = bitrate;
|
stream->current_download_rate = bitrate;
|
||||||
|
GST_DEBUG_OBJECT (stream->pad, "Bitrate: %" G_GUINT64_FORMAT
|
||||||
|
" (bytes: %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " microsecs",
|
||||||
|
bitrate, stream->download_total_bytes, stream->download_total_time);
|
||||||
|
|
||||||
return bitrate;
|
return bitrate;
|
||||||
}
|
}
|
||||||
|
@ -1202,54 +1218,13 @@ gst_adaptive_demux_combine_flows (GstAdaptiveDemux * demux)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
GstFlowReturn
|
||||||
_src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
gst_adaptive_demux_stream_push_buffer (GstAdaptiveDemuxStream * stream,
|
||||||
|
GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstPad *srcpad = (GstPad *) parent;
|
|
||||||
GstAdaptiveDemuxStream *stream = gst_pad_get_element_private (srcpad);
|
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
gboolean discont = FALSE;
|
gboolean discont = FALSE;
|
||||||
gboolean subsegment_end = FALSE;
|
|
||||||
|
|
||||||
if (stream->starting_fragment) {
|
|
||||||
stream->starting_fragment = FALSE;
|
|
||||||
if (klass->start_fragment) {
|
|
||||||
klass->start_fragment (demux, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_BUFFER_PTS (buffer) = stream->fragment.timestamp;
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (stream->pad, "set fragment pts=%" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
|
|
||||||
|
|
||||||
if (GST_BUFFER_PTS_IS_VALID (buffer))
|
|
||||||
stream->segment.position = GST_BUFFER_PTS (buffer);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
GST_BUFFER_PTS (buffer) = GST_CLOCK_TIME_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The subclass might need to decrypt or modify the buffer somehow
|
|
||||||
* before processing it */
|
|
||||||
if (klass->chunk_received) {
|
|
||||||
ret = klass->chunk_received (demux, stream, &buffer);
|
|
||||||
if (ret != GST_FLOW_OK) {
|
|
||||||
if (ret == (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SUBSEGMENT_END) {
|
|
||||||
ret = GST_FLOW_OK;
|
|
||||||
subsegment_end = TRUE;
|
|
||||||
} else {
|
|
||||||
if (buffer)
|
|
||||||
gst_buffer_unref (buffer);
|
|
||||||
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (stream->first_fragment_buffer) {
|
if (stream->first_fragment_buffer) {
|
||||||
if (demux->segment.rate < 0)
|
if (demux->segment.rate < 0)
|
||||||
|
@ -1280,15 +1255,6 @@ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||||
|
|
||||||
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
|
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
|
||||||
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
|
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
|
||||||
/* TODO what about live? how to count segments?
|
|
||||||
GST_BUFFER_OFFSET (buffer) =
|
|
||||||
gst_mpd_client_get_segment_index (stream->active_stream) - 1;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* accumulate time and size to get this chunk */
|
|
||||||
stream->download_total_time +=
|
|
||||||
g_get_monotonic_time () - stream->download_start_time;
|
|
||||||
stream->download_total_bytes += gst_buffer_get_size (buffer);
|
|
||||||
|
|
||||||
if (G_UNLIKELY (stream->pending_caps)) {
|
if (G_UNLIKELY (stream->pending_caps)) {
|
||||||
GST_DEBUG_OBJECT (stream->pad, "Setting pending caps: %" GST_PTR_FORMAT,
|
GST_DEBUG_OBJECT (stream->pad, "Setting pending caps: %" GST_PTR_FORMAT,
|
||||||
|
@ -1310,9 +1276,69 @@ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||||
stream->pending_tags = NULL;
|
stream->pending_tags = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gst_proxy_pad_chain_default (pad, parent, buffer);
|
ret = gst_pad_push (stream->pad, buffer);
|
||||||
stream->download_start_time = g_get_monotonic_time ();
|
GST_LOG_OBJECT (stream->pad, "Push result: %d %s", ret,
|
||||||
GST_LOG_OBJECT (pad, "Chain res: %d %s", ret, gst_flow_get_name (ret));
|
gst_flow_get_name (ret));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_finish_fragment_default (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream)
|
||||||
|
{
|
||||||
|
return gst_adaptive_demux_stream_advance_fragment (demux, stream,
|
||||||
|
stream->fragment.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_data_received_default (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream)
|
||||||
|
{
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
|
buffer = gst_adapter_take_buffer (stream->adapter,
|
||||||
|
gst_adapter_available (stream->adapter));
|
||||||
|
return gst_adaptive_demux_stream_push_buffer (stream, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
_src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
GstPad *srcpad = (GstPad *) parent;
|
||||||
|
GstAdaptiveDemuxStream *stream = gst_pad_get_element_private (srcpad);
|
||||||
|
GstAdaptiveDemux *demux = stream->demux;
|
||||||
|
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
||||||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
|
if (stream->starting_fragment) {
|
||||||
|
stream->starting_fragment = FALSE;
|
||||||
|
if (klass->start_fragment) {
|
||||||
|
klass->start_fragment (demux, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_BUFFER_PTS (buffer) = stream->fragment.timestamp;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (stream->pad, "set fragment pts=%" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
|
||||||
|
|
||||||
|
if (GST_BUFFER_PTS_IS_VALID (buffer))
|
||||||
|
stream->segment.position = GST_BUFFER_PTS (buffer);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
GST_BUFFER_PTS (buffer) = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->download_total_time +=
|
||||||
|
g_get_monotonic_time () - stream->download_chunk_start_time;
|
||||||
|
stream->download_total_bytes += gst_buffer_get_size (buffer);
|
||||||
|
|
||||||
|
gst_adapter_push (stream->adapter, buffer);
|
||||||
|
GST_DEBUG_OBJECT (stream->pad, "Received buffer of size %" G_GSIZE_FORMAT
|
||||||
|
". Now %" G_GSIZE_FORMAT " on adapter", gst_buffer_get_size (buffer),
|
||||||
|
gst_adapter_available (stream->adapter));
|
||||||
|
ret = klass->data_received (demux, stream);
|
||||||
|
stream->download_chunk_start_time = g_get_monotonic_time ();
|
||||||
|
|
||||||
if (ret != GST_FLOW_OK) {
|
if (ret != GST_FLOW_OK) {
|
||||||
if (ret < GST_FLOW_EOS) {
|
if (ret < GST_FLOW_EOS) {
|
||||||
|
@ -1327,34 +1353,13 @@ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
|
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
|
||||||
} else if (subsegment_end) {
|
if (ret == (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH)
|
||||||
/* tell upstream that we are done here */
|
ret = GST_FLOW_EOS; /* return EOS to make the source stop */
|
||||||
ret = GST_FLOW_EOS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_adaptive_demux_stream_fragment_eos (GstAdaptiveDemuxStream * stream)
|
|
||||||
{
|
|
||||||
GstAdaptiveDemux *demux = stream->demux;
|
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
|
|
||||||
GstBuffer *buffer = NULL;
|
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
|
||||||
|
|
||||||
if (klass->finish_fragment) {
|
|
||||||
klass->finish_fragment (demux, stream, &buffer);
|
|
||||||
if (buffer) {
|
|
||||||
stream->download_total_time +=
|
|
||||||
g_get_monotonic_time () - stream->download_start_time;
|
|
||||||
stream->download_total_bytes += gst_buffer_get_size (buffer);
|
|
||||||
ret = gst_pad_push (stream->pad, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_adaptive_demux_stream_fragment_download_finish (GstAdaptiveDemuxStream *
|
gst_adaptive_demux_stream_fragment_download_finish (GstAdaptiveDemuxStream *
|
||||||
stream, GstFlowReturn ret, GError * err)
|
stream, GstFlowReturn ret, GError * err)
|
||||||
|
@ -1386,9 +1391,11 @@ _src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_EOS:{
|
case GST_EVENT_EOS:{
|
||||||
|
GstAdaptiveDemuxClass *klass;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
|
||||||
ret = gst_adaptive_demux_stream_fragment_eos (stream);
|
klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
|
||||||
|
ret = klass->finish_fragment (stream->demux, stream);
|
||||||
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
|
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1629,6 +1636,7 @@ gst_adaptive_demux_stream_download_uri (GstAdaptiveDemux * demux,
|
||||||
g_mutex_lock (&stream->fragment_download_lock);
|
g_mutex_lock (&stream->fragment_download_lock);
|
||||||
if (G_LIKELY (stream->last_ret == GST_FLOW_OK)) {
|
if (G_LIKELY (stream->last_ret == GST_FLOW_OK)) {
|
||||||
stream->download_start_time = g_get_monotonic_time ();
|
stream->download_start_time = g_get_monotonic_time ();
|
||||||
|
stream->download_chunk_start_time = g_get_monotonic_time ();
|
||||||
g_mutex_unlock (&stream->fragment_download_lock);
|
g_mutex_unlock (&stream->fragment_download_lock);
|
||||||
gst_element_sync_state_with_parent (stream->src);
|
gst_element_sync_state_with_parent (stream->src);
|
||||||
g_mutex_lock (&stream->fragment_download_lock);
|
g_mutex_lock (&stream->fragment_download_lock);
|
||||||
|
@ -1734,20 +1742,7 @@ gst_adaptive_demux_stream_download_fragment (GstAdaptiveDemuxStream * stream)
|
||||||
stream->fragment.range_start, stream->fragment.range_end);
|
stream->fragment.range_start, stream->fragment.range_end);
|
||||||
GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d %s",
|
GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d %s",
|
||||||
stream->last_ret, gst_flow_get_name (stream->last_ret));
|
stream->last_ret, gst_flow_get_name (stream->last_ret));
|
||||||
if (ret == GST_FLOW_OK) {
|
if (ret != GST_FLOW_OK) {
|
||||||
gst_element_post_message (GST_ELEMENT_CAST (demux),
|
|
||||||
gst_message_new_element (GST_OBJECT_CAST (demux),
|
|
||||||
gst_structure_new (STATISTICS_MESSAGE_NAME,
|
|
||||||
"manifest-uri", G_TYPE_STRING,
|
|
||||||
demux->manifest_uri, "uri", G_TYPE_STRING,
|
|
||||||
url, "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->download_total_time * GST_USECOND, NULL)));
|
|
||||||
} else {
|
|
||||||
GST_INFO_OBJECT (demux, "No fragment downloaded");
|
GST_INFO_OBJECT (demux, "No fragment downloaded");
|
||||||
/* TODO check if we are truly stoping */
|
/* TODO check if we are truly stoping */
|
||||||
if (ret != GST_FLOW_ERROR && gst_adaptive_demux_is_live (demux)) {
|
if (ret != GST_FLOW_ERROR && gst_adaptive_demux_is_live (demux)) {
|
||||||
|
@ -1758,8 +1753,6 @@ gst_adaptive_demux_stream_download_fragment (GstAdaptiveDemuxStream * stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
no_url_error:
|
no_url_error:
|
||||||
|
@ -1932,44 +1925,6 @@ gst_adaptive_demux_stream_download_loop (GstAdaptiveDemuxStream * stream)
|
||||||
GST_MANIFEST_LOCK (demux);
|
GST_MANIFEST_LOCK (demux);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == GST_FLOW_OK) {
|
|
||||||
stream->download_error_count = 0;
|
|
||||||
g_clear_error (&stream->last_error);
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (stream->fragment.duration))
|
|
||||||
stream->segment.position += stream->fragment.duration;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (stream->pad, "Advancing to next fragment");
|
|
||||||
if (gst_adaptive_demux_stream_has_next_fragment (demux, stream)) {
|
|
||||||
ret = gst_adaptive_demux_stream_advance_fragment (demux, stream);
|
|
||||||
GST_DEBUG_OBJECT (stream->pad, "Advanced. Result: %d %s", ret,
|
|
||||||
gst_flow_get_name (ret));
|
|
||||||
|
|
||||||
if (ret == GST_FLOW_OK) {
|
|
||||||
if (gst_adaptive_demux_stream_select_bitrate (demux, stream,
|
|
||||||
gst_adaptive_demux_stream_update_current_bitrate (stream))) {
|
|
||||||
stream->need_header = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the subclass might want to switch pads */
|
|
||||||
if (G_UNLIKELY (demux->next_streams)) {
|
|
||||||
gst_task_stop (stream->download_task);
|
|
||||||
/* TODO only allow switching streams if other downloads are not ongoing */
|
|
||||||
GST_DEBUG_OBJECT (demux, "Subclass wants new pads "
|
|
||||||
"to do bitrate switching");
|
|
||||||
gst_adaptive_demux_expose_streams (demux, FALSE);
|
|
||||||
gst_adaptive_demux_start_tasks (demux);
|
|
||||||
ret = GST_FLOW_EOS;
|
|
||||||
GST_MANIFEST_UNLOCK (demux);
|
|
||||||
goto end_of_manifest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (stream->pad, "No next fragment -> EOS");
|
|
||||||
ret = GST_FLOW_EOS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->last_ret = ret;
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case GST_FLOW_OK:
|
case GST_FLOW_OK:
|
||||||
break; /* all is good, let's go */
|
break; /* all is good, let's go */
|
||||||
|
@ -2239,15 +2194,80 @@ gst_adaptive_demux_stream_has_next_fragment (GstAdaptiveDemux * demux,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
GstFlowReturn
|
||||||
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
|
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
|
||||||
GstAdaptiveDemuxStream * stream)
|
GstAdaptiveDemuxStream * stream, GstClockTime duration)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret;
|
||||||
|
|
||||||
|
GST_MANIFEST_LOCK (demux);
|
||||||
|
if (stream->last_ret == GST_FLOW_OK) {
|
||||||
|
stream->last_ret =
|
||||||
|
gst_adaptive_demux_stream_advance_fragment_unlocked (demux, stream,
|
||||||
|
duration);
|
||||||
|
}
|
||||||
|
ret = stream->last_ret;
|
||||||
|
GST_MANIFEST_UNLOCK (demux);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream, GstClockTime duration)
|
||||||
{
|
{
|
||||||
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
|
||||||
|
GstFlowReturn ret;
|
||||||
|
|
||||||
g_return_val_if_fail (klass->stream_advance_fragment != NULL, GST_FLOW_ERROR);
|
g_return_val_if_fail (klass->stream_advance_fragment != NULL, GST_FLOW_ERROR);
|
||||||
|
|
||||||
return klass->stream_advance_fragment (stream);
|
stream->download_error_count = 0;
|
||||||
|
g_clear_error (&stream->last_error);
|
||||||
|
stream->download_total_time +=
|
||||||
|
g_get_monotonic_time () - stream->download_chunk_start_time;
|
||||||
|
|
||||||
|
/* FIXME - url has no indication of byte ranges for subsegments */
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (demux),
|
||||||
|
gst_message_new_element (GST_OBJECT_CAST (demux),
|
||||||
|
gst_structure_new (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->download_total_time * GST_USECOND, NULL)));
|
||||||
|
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (duration))
|
||||||
|
stream->segment.position += duration;
|
||||||
|
ret = klass->stream_advance_fragment (stream);
|
||||||
|
|
||||||
|
stream->download_start_time = stream->download_chunk_start_time =
|
||||||
|
g_get_monotonic_time ();
|
||||||
|
|
||||||
|
if (ret == GST_FLOW_OK) {
|
||||||
|
if (gst_adaptive_demux_stream_select_bitrate (demux, stream,
|
||||||
|
gst_adaptive_demux_stream_update_current_bitrate (stream))) {
|
||||||
|
stream->need_header = TRUE;
|
||||||
|
gst_adapter_clear (stream->adapter);
|
||||||
|
ret = GST_ADAPTIVE_DEMUX_FLOW_SWITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the subclass might want to switch pads */
|
||||||
|
if (G_UNLIKELY (demux->next_streams)) {
|
||||||
|
gst_task_stop (stream->download_task);
|
||||||
|
/* TODO only allow switching streams if other downloads are not ongoing */
|
||||||
|
GST_DEBUG_OBJECT (demux, "Subclass wants new pads "
|
||||||
|
"to do bitrate switching");
|
||||||
|
gst_adaptive_demux_expose_streams (demux, FALSE);
|
||||||
|
gst_adaptive_demux_start_tasks (demux);
|
||||||
|
ret = GST_FLOW_EOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|
|
@ -77,10 +77,6 @@ G_BEGIN_DECLS
|
||||||
g_clear_error (&err); \
|
g_clear_error (&err); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
enum _GstAdaptiveDemuxFlowReturn {
|
|
||||||
GST_ADAPTIVE_DEMUX_FLOW_SUBSEGMENT_END = GST_FLOW_CUSTOM_SUCCESS + 100
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _GstAdaptiveDemuxStreamFragment GstAdaptiveDemuxStreamFragment;
|
typedef struct _GstAdaptiveDemuxStreamFragment GstAdaptiveDemuxStreamFragment;
|
||||||
typedef struct _GstAdaptiveDemuxStream GstAdaptiveDemuxStream;
|
typedef struct _GstAdaptiveDemuxStream GstAdaptiveDemuxStream;
|
||||||
typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
|
typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
|
||||||
|
@ -115,6 +111,8 @@ struct _GstAdaptiveDemuxStream
|
||||||
|
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
|
||||||
|
GstAdapter *adapter;
|
||||||
|
|
||||||
GstCaps *pending_caps;
|
GstCaps *pending_caps;
|
||||||
GstEvent *pending_segment;
|
GstEvent *pending_segment;
|
||||||
GstTagList *pending_tags;
|
GstTagList *pending_tags;
|
||||||
|
@ -141,6 +139,7 @@ struct _GstAdaptiveDemuxStream
|
||||||
gboolean starting_fragment;
|
gboolean starting_fragment;
|
||||||
gboolean first_fragment_buffer;
|
gboolean first_fragment_buffer;
|
||||||
gint64 download_start_time;
|
gint64 download_start_time;
|
||||||
|
gint64 download_chunk_start_time;
|
||||||
gint64 download_total_time;
|
gint64 download_total_time;
|
||||||
gint64 download_total_bytes;
|
gint64 download_total_bytes;
|
||||||
gint current_download_rate;
|
gint current_download_rate;
|
||||||
|
@ -335,28 +334,23 @@ struct _GstAdaptiveDemuxClass
|
||||||
* finish_fragment:
|
* finish_fragment:
|
||||||
* @demux: #GstAdaptiveDemux
|
* @demux: #GstAdaptiveDemux
|
||||||
* @stream: #GstAdaptiveDemuxStream
|
* @stream: #GstAdaptiveDemuxStream
|
||||||
* @buffer: Pointer to store and pending data that should be pushed.
|
|
||||||
*
|
*
|
||||||
* Notifies the subclass that a fragment download was finished.
|
* Notifies the subclass that a fragment download was finished.
|
||||||
* It can be used to cleanup internal state after a fragment and also
|
* It can be used to cleanup internal state after a fragment and
|
||||||
* provides a pointer for the subclass to return some pending data
|
* also push any pending data before moving to the next fragment.
|
||||||
* that should be pushed before starting the next fragment. This
|
|
||||||
* covers the use case of finishing the decryption of the last chunk
|
|
||||||
* of an encrypted fragment.
|
|
||||||
*/
|
*/
|
||||||
void (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemuxStream * stream, GstBuffer ** buffer);
|
GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemuxStream * stream);
|
||||||
/**
|
/**
|
||||||
* chunk_received:
|
* data_received:
|
||||||
* @demux: #GstAdaptiveDemux
|
* @demux: #GstAdaptiveDemux
|
||||||
* @stream: #GstAdaptiveDemuxStream
|
* @stream: #GstAdaptiveDemuxStream
|
||||||
* @buffer: Pointer containing the received chunk, also used to return modified data
|
|
||||||
*
|
*
|
||||||
* Notifies the subclass that a fragment chunk was downloaded. The subclass can
|
* Notifies the subclass that a fragment chunk was downloaded. The subclass
|
||||||
* modify the buffer and return a new one if needed. Used for decryption.
|
* can look at the data at the adapter and modify/push data as desired.
|
||||||
*
|
*
|
||||||
* Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
|
* Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
|
||||||
*/
|
*/
|
||||||
GstFlowReturn (*chunk_received) (GstAdaptiveDemux * demux, GstAdaptiveDemuxStream * stream, GstBuffer ** buffer);
|
GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemuxStream * stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_live_seek_range:
|
* get_live_seek_range:
|
||||||
|
@ -385,6 +379,14 @@ void gst_adaptive_demux_stream_set_tags (GstAdaptiveDemuxStream * stream,
|
||||||
GstTagList * tags);
|
GstTagList * tags);
|
||||||
void gst_adaptive_demux_stream_fragment_clear (GstAdaptiveDemuxStreamFragment * f);
|
void gst_adaptive_demux_stream_fragment_clear (GstAdaptiveDemuxStreamFragment * f);
|
||||||
|
|
||||||
|
GstFlowReturn gst_adaptive_demux_stream_push_buffer (GstAdaptiveDemuxStream * stream, GstBuffer * buffer);
|
||||||
|
GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream, GstClockTime duration);
|
||||||
|
GstFlowReturn
|
||||||
|
gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
|
||||||
|
GstAdaptiveDemuxStream * stream, GstClockTime duration);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue