mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-23 15:48:23 +00:00
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:
parent
7b4fe1e02f
commit
a9e38d3fec
2 changed files with 105 additions and 7 deletions
|
@ -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);
|
||||||
|
|
||||||
ret = klass->finish_fragment (stream->demux, stream);
|
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);
|
||||||
|
}
|
||||||
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;
|
||||||
ret =
|
|
||||||
gst_adaptive_demux_stream_download_uri (demux, stream, url,
|
if (klass->need_another_chunk && klass->need_another_chunk (stream)
|
||||||
stream->fragment.range_start, stream->fragment.range_end, &http_status);
|
&& stream->fragment.chunk_size != 0) {
|
||||||
GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d (%d) %s",
|
gint64 range_start, range_end, chunk_start, chunk_end;
|
||||||
stream->last_ret, http_status, gst_flow_get_name (stream->last_ret));
|
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 =
|
||||||
|
gst_adaptive_demux_stream_download_uri (demux, stream, url,
|
||||||
|
stream->fragment.range_start, stream->fragment.range_end, &http_status);
|
||||||
|
GST_DEBUG_OBJECT (stream->pad, "Fragment download result: %d (%d) %s",
|
||||||
|
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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue