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:
Thiago Santos 2015-01-15 17:44:45 -03:00
parent 15d51c1f6c
commit 229a15b393
6 changed files with 308 additions and 294 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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