adaptivedemux: Add API for allowing subclasses to download URLs in chunks

This allows to gradually download part of a fragment when the final size is
not known and only a part of it should be downloaded. For example when only
the moof should be parsed and/or a single keyframe should be downloaded.

https://bugzilla.gnome.org/show_bug.cgi?id=741104
This commit is contained in:
Sebastian Dröge 2016-07-12 10:22:43 +03:00
parent 7b4fe1e02f
commit a9e38d3fec
2 changed files with 105 additions and 7 deletions

View file

@ -2241,6 +2241,7 @@ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
ret = GST_FLOW_EOS; /* return EOS to make the source stop */ ret = GST_FLOW_EOS; /* return EOS to make the source stop */
} else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) { } else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) {
/* Behaves like an EOS event from upstream */ /* Behaves like an EOS event from upstream */
stream->fragment.finished = TRUE;
ret = klass->finish_fragment (demux, stream); ret = klass->finish_fragment (demux, stream);
if (ret == (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH) { if (ret == (GstFlowReturn) GST_ADAPTIVE_DEMUX_FLOW_SWITCH) {
ret = GST_FLOW_EOS; /* return EOS to make the source stop */ ret = GST_FLOW_EOS; /* return EOS to make the source stop */
@ -2288,11 +2289,17 @@ gst_adaptive_demux_stream_fragment_download_finish (GstAdaptiveDemuxStream *
static GstFlowReturn static GstFlowReturn
gst_adaptive_demux_eos_handling (GstAdaptiveDemuxStream * stream) gst_adaptive_demux_eos_handling (GstAdaptiveDemuxStream * stream)
{ {
GstFlowReturn ret; GstFlowReturn ret = GST_FLOW_OK;
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux); GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (stream->demux);
if (!klass->need_another_chunk || stream->fragment.chunk_size == -1
|| !klass->need_another_chunk (stream)
|| stream->fragment.chunk_size == 0) {
stream->fragment.finished = TRUE;
ret = klass->finish_fragment (stream->demux, stream); ret = klass->finish_fragment (stream->demux, stream);
}
gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL); gst_adaptive_demux_stream_fragment_download_finish (stream, ret, NULL);
return ret; return ret;
} }
@ -2820,6 +2827,7 @@ static GstFlowReturn
gst_adaptive_demux_stream_download_fragment (GstAdaptiveDemuxStream * stream) gst_adaptive_demux_stream_download_fragment (GstAdaptiveDemuxStream * stream)
{ {
GstAdaptiveDemux *demux = stream->demux; GstAdaptiveDemux *demux = stream->demux;
GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
gchar *url = NULL; gchar *url = NULL;
GstFlowReturn ret; GstFlowReturn ret;
gboolean retried_once = FALSE, live; gboolean retried_once = FALSE, live;
@ -2851,11 +2859,82 @@ again:
stream->last_ret = GST_FLOW_OK; stream->last_ret = GST_FLOW_OK;
http_status = 200; http_status = 200;
if (klass->need_another_chunk && klass->need_another_chunk (stream)
&& stream->fragment.chunk_size != 0) {
gint64 range_start, range_end, chunk_start, chunk_end;
guint64 download_total_bytes;
gint chunk_size = stream->fragment.chunk_size;
range_start = chunk_start = stream->fragment.range_start;
range_end = stream->fragment.range_end;
/* HTTP ranges are inclusive for the end */
if (chunk_size != -1)
chunk_end = range_start + chunk_size - 1;
else
chunk_end = range_end;
if (range_end != -1)
chunk_end = MIN (chunk_end, range_end);
while (!stream->fragment.finished && (chunk_start <= range_end
|| range_end == -1)) {
download_total_bytes = stream->download_total_bytes;
ret =
gst_adaptive_demux_stream_download_uri (demux, stream, url,
chunk_start, chunk_end, &http_status);
GST_DEBUG_OBJECT (stream->pad,
"Fragment chunk download result: %d (%d) %s", stream->last_ret,
http_status, gst_flow_get_name (stream->last_ret));
/* Don't retry for any chunks except the first. We would have sent
* data downstream already otherwise and it's difficult to recover
* from that in a meaningful way */
if (chunk_start > range_start)
retried_once = TRUE;
/* FIXME: Check for 416 Range Not Satisfiable here and fall back to
* downloading up to -1. We don't know the full duration.
* Needs https://bugzilla.gnome.org/show_bug.cgi?id=756806 */
if (ret != GST_FLOW_OK && chunk_end == -1) {
break;
} else if (ret != GST_FLOW_OK) {
chunk_end = -1;
stream->last_ret = GST_FLOW_OK;
continue;
}
if (chunk_end == -1)
break;
/* Short read, we're at the end now */
if (stream->download_total_bytes - download_total_bytes <
chunk_end + 1 - chunk_start)
break;
if (!klass->need_another_chunk (stream))
break;
/* HTTP ranges are inclusive for the end */
chunk_start += chunk_size;
chunk_size = stream->fragment.chunk_size;
if (chunk_size != -1)
chunk_end = chunk_start + chunk_size - 1;
else
chunk_end = range_end;
if (range_end != -1)
chunk_end = MIN (chunk_end, range_end);
}
} else {
ret = ret =
gst_adaptive_demux_stream_download_uri (demux, stream, url, gst_adaptive_demux_stream_download_uri (demux, stream, url,
stream->fragment.range_start, stream->fragment.range_end, &http_status); stream->fragment.range_start, stream->fragment.range_end, &http_status);
GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d (%d) %s", GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d (%d) %s",
stream->last_ret, http_status, gst_flow_get_name (stream->last_ret)); stream->last_ret, http_status, gst_flow_get_name (stream->last_ret));
}
if (ret == GST_FLOW_OK) if (ret == GST_FLOW_OK)
goto beach; goto beach;
@ -3649,6 +3728,7 @@ gst_adaptive_demux_stream_update_fragment_info (GstAdaptiveDemux * demux,
/* Make sure the sub-class will update bitrate, or else /* Make sure the sub-class will update bitrate, or else
* we will later */ * we will later */
stream->fragment.bitrate = 0; stream->fragment.bitrate = 0;
stream->fragment.finished = FALSE;
return klass->stream_update_fragment_info (stream); return klass->stream_update_fragment_info (stream);
} }
@ -3745,6 +3825,8 @@ gst_adaptive_demux_stream_fragment_clear (GstAdaptiveDemuxStreamFragment * f)
f->index_uri = NULL; f->index_uri = NULL;
f->index_range_start = 0; f->index_range_start = 0;
f->index_range_end = -1; f->index_range_end = -1;
f->finished = FALSE;
} }
/* must be called with manifest_lock taken */ /* must be called with manifest_lock taken */

View file

@ -98,6 +98,9 @@ struct _GstAdaptiveDemuxStreamFragment
gint64 range_start; gint64 range_start;
gint64 range_end; gint64 range_end;
/* when chunked downloading is used, may be be updated need_another_chunk() */
guint chunk_size;
/* when headers are needed */ /* when headers are needed */
gchar *header_uri; gchar *header_uri;
gint64 header_range_start; gint64 header_range_start;
@ -111,6 +114,8 @@ struct _GstAdaptiveDemuxStreamFragment
/* Nominal bitrate as provided by /* Nominal bitrate as provided by
* sub-class or calculated by base-class */ * sub-class or calculated by base-class */
guint bitrate; guint bitrate;
gboolean finished;
}; };
struct _GstAdaptiveDemuxStream struct _GstAdaptiveDemuxStream
@ -326,6 +331,17 @@ struct _GstAdaptiveDemuxClass
GstFlowReturn (*stream_seek) (GstAdaptiveDemuxStream * stream, gboolean forward, GstSeekFlags flags, GstClockTime target_ts, GstClockTime * final_ts); GstFlowReturn (*stream_seek) (GstAdaptiveDemuxStream * stream, gboolean forward, GstSeekFlags flags, GstClockTime target_ts, GstClockTime * final_ts);
gboolean (*stream_has_next_fragment) (GstAdaptiveDemuxStream * stream); gboolean (*stream_has_next_fragment) (GstAdaptiveDemuxStream * stream);
GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemuxStream * stream); GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemuxStream * stream);
/**
* need_another_chunk:
* @stream: #GstAdaptiveDemuxStream
*
* If chunked downloading is used (chunk_size != 0) this is called once as
* chunk is finished to decide whether more has to be downloaded or not.
* May update chunk_size to a different value
*/
gboolean (*need_another_chunk) (GstAdaptiveDemuxStream * stream);
/** /**
* stream_update_fragment_info: * stream_update_fragment_info:
* @stream: #GstAdaptiveDemuxStream * @stream: #GstAdaptiveDemuxStream