mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
gst/playback/gstdecodebin.c: Also catch queue underruns but don't do anything yet.
Original commit message from CVS: * gst/playback/gstdecodebin.c: (try_to_link_1), (queue_enlarge), (queue_underrun_cb), (queue_filled_cb): Also catch queue underruns but don't do anything yet. Refactor and comment queue enlarging code a bit. * gst/playback/gstplaybasebin.c: (queue_overrun), (queue_threshold_reached), (queue_out_of_data), (gen_preroll_element): If a queue over/underruns check that we don't create nasty deadlocks when the min-threshold is not reached but the max-bytes is. In those cases disable max-bytes when we know that the queue is fed timed data. Add more comments.
This commit is contained in:
parent
7cae9d887f
commit
03c8d8ae43
3 changed files with 175 additions and 25 deletions
16
ChangeLog
16
ChangeLog
|
@ -1,3 +1,19 @@
|
|||
2006-05-11 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst/playback/gstdecodebin.c: (try_to_link_1), (queue_enlarge),
|
||||
(queue_underrun_cb), (queue_filled_cb):
|
||||
Also catch queue underruns but don't do anything yet.
|
||||
Refactor and comment queue enlarging code a bit.
|
||||
|
||||
* gst/playback/gstplaybasebin.c: (queue_overrun),
|
||||
(queue_threshold_reached), (queue_out_of_data),
|
||||
(gen_preroll_element):
|
||||
If a queue over/underruns check that we don't create nasty
|
||||
deadlocks when the min-threshold is not reached but the
|
||||
max-bytes is. In those cases disable max-bytes when we
|
||||
know that the queue is fed timed data.
|
||||
Add more comments.
|
||||
|
||||
2006-05-11 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* gst/playback/gstplaybin.c: (gen_audio_element):
|
||||
|
|
|
@ -138,6 +138,7 @@ static void new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic);
|
|||
static void no_more_pads (GstElement * element, GstDynamic * dynamic);
|
||||
|
||||
static void queue_filled_cb (GstElement * queue, GstDecodeBin * decode_bin);
|
||||
static void queue_underrun_cb (GstElement * queue, GstDecodeBin * decode_bin);
|
||||
|
||||
static GstElementClass *parent_class;
|
||||
static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 };
|
||||
|
@ -805,6 +806,8 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstElement * srcelement, GstPad * pad,
|
|||
decode_bin->queues = g_list_append (decode_bin->queues, queue);
|
||||
g_signal_connect (G_OBJECT (queue),
|
||||
"overrun", G_CALLBACK (queue_filled_cb), decode_bin);
|
||||
g_signal_connect (G_OBJECT (queue),
|
||||
"underrun", G_CALLBACK (queue_underrun_cb), decode_bin);
|
||||
}
|
||||
|
||||
/* The link worked, now figure out what it was that we connected */
|
||||
|
@ -990,6 +993,48 @@ remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad)
|
|||
gst_bin_remove (GST_BIN (decode_bin), elem);
|
||||
}
|
||||
|
||||
/* there are @bytes bytes in @queue, enlarge it
|
||||
*
|
||||
* Returns: new max number of bytes in @queue
|
||||
*/
|
||||
static guint
|
||||
queue_enlarge (GstElement * queue, guint bytes, GstDecodeBin * decode_bin)
|
||||
{
|
||||
/* Increase the queue size by 1Mbyte if it is over 1Mb, else double its current limit
|
||||
*/
|
||||
if (bytes > 1024 * 1024)
|
||||
bytes += 1024 * 1024;
|
||||
else
|
||||
bytes *= 2;
|
||||
|
||||
GST_DEBUG_OBJECT (decode_bin,
|
||||
"increasing queue %s max-size-bytes to %d", GST_ELEMENT_NAME (queue),
|
||||
bytes);
|
||||
g_object_set (G_OBJECT (queue), "max-size-bytes", bytes, NULL);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* this callback is called when our queues fills up or are empty
|
||||
* We then check the status of all other queues to make sure we
|
||||
* never have an empty and full queue at the same time since that
|
||||
* would block dataflow. In the case of a filled queue, we make
|
||||
* it larger.
|
||||
*/
|
||||
static void
|
||||
queue_underrun_cb (GstElement * queue, GstDecodeBin * decode_bin)
|
||||
{
|
||||
/* FIXME: we don't really do anything here for now. Ideally we should
|
||||
* see if some of the queues are filled and increase their values
|
||||
* in that case.
|
||||
* Note: be very carefull with thread safety here as this underrun
|
||||
* signal is done from the streaming thread of queue srcpad which
|
||||
* is different from the pad_added (where we add the queue to the
|
||||
* list) and the overrun signals that are signalled from the
|
||||
* demuxer thread.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Make sure we don't have a full queue and empty queue situation */
|
||||
static void
|
||||
queue_filled_cb (GstElement * queue, GstDecodeBin * decode_bin)
|
||||
|
@ -998,43 +1043,48 @@ queue_filled_cb (GstElement * queue, GstDecodeBin * decode_bin)
|
|||
gboolean increase = FALSE;
|
||||
guint bytes;
|
||||
|
||||
/* get current byte level from the queue that is filled */
|
||||
g_object_get (G_OBJECT (queue), "current-level-bytes", &bytes, NULL);
|
||||
GST_DEBUG_OBJECT (decode_bin, "One of the queues is full at %d bytes", bytes);
|
||||
|
||||
if (bytes > (20 * 1024 * 1024)) {
|
||||
GST_WARNING_OBJECT (decode_bin,
|
||||
"Queue is bigger than 20Mbytes, something else is going wrong");
|
||||
return;
|
||||
}
|
||||
/* we do not buffer more than 20Mb */
|
||||
if (bytes > (20 * 1024 * 1024))
|
||||
goto too_large;
|
||||
|
||||
/* check all other queue to see if one is empty, in that case
|
||||
* we need to enlarge @queue */
|
||||
for (tmp = decode_bin->queues; tmp; tmp = g_list_next (tmp)) {
|
||||
GstElement *aqueue = GST_ELEMENT (tmp->data);
|
||||
guint levelbytes = -1;
|
||||
|
||||
if (aqueue != queue) {
|
||||
g_object_get (G_OBJECT (aqueue),
|
||||
"current-level-bytes", &levelbytes, NULL);
|
||||
g_object_get (G_OBJECT (aqueue), "current-level-bytes", &levelbytes,
|
||||
NULL);
|
||||
if (levelbytes == 0) {
|
||||
/* yup, found an empty queue, we can stop the search and
|
||||
* need to enlarge the queue */
|
||||
increase = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (increase) {
|
||||
/*
|
||||
* Increase the queue size by 1Mbyte if it is over 1Mb, else double its current limit
|
||||
*/
|
||||
if (bytes > 1024 * 1024)
|
||||
bytes += 1024 * 1024;
|
||||
else
|
||||
bytes *= 2;
|
||||
GST_DEBUG_OBJECT (decode_bin,
|
||||
"One of the other queues is empty, increasing queue byte limit to %d",
|
||||
bytes);
|
||||
g_object_set (G_OBJECT (queue), "max-size-bytes", bytes, NULL);
|
||||
} else
|
||||
/* enlarge @queue */
|
||||
queue_enlarge (queue, bytes, decode_bin);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (decode_bin,
|
||||
"Queue is full but other queues are not empty, not doing anything");
|
||||
}
|
||||
return;
|
||||
|
||||
/* errors */
|
||||
too_large:
|
||||
{
|
||||
GST_WARNING_OBJECT (decode_bin,
|
||||
"Queue is bigger than 20Mbytes, something else is going wrong");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function will be called when a dynamic pad is created on an element.
|
||||
|
|
|
@ -496,21 +496,50 @@ check_queue (GstPad * pad, GstBuffer * data, gpointer user_data)
|
|||
|
||||
/* this signal will be fired when one of the queues with raw
|
||||
* data is filled. This means that the group building stage is over
|
||||
* and playback of the new queued group should start */
|
||||
* and playback of the new queued group should start
|
||||
*
|
||||
* If this queue overruns we can potentially create a deadlock when:
|
||||
*
|
||||
* 1) the max-bytes is hit and
|
||||
* 2) the min-time is not hit.
|
||||
*
|
||||
* We recover from this situation in the overrun callback by
|
||||
* setting the max-bytes to unlimited if we see that there is
|
||||
* a current-time-level (which means some sort of timestamping is
|
||||
* done).
|
||||
*/
|
||||
static void
|
||||
queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin)
|
||||
queue_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin)
|
||||
{
|
||||
GST_DEBUG ("queue %s overrun", GST_ELEMENT_NAME (element));
|
||||
GST_DEBUG ("queue %s overrun", GST_ELEMENT_NAME (queue));
|
||||
|
||||
/* if we're streaming, check if we had a deadlock with the
|
||||
* max-bytes <> min-time thresholds */
|
||||
if (play_base_bin->is_stream) {
|
||||
guint64 time, min_time;
|
||||
|
||||
g_object_get (G_OBJECT (queue), "current-level-time", &time,
|
||||
"min-threshold-time", &min_time, NULL);
|
||||
|
||||
GST_DEBUG_OBJECT (play_base_bin, "streaming mode, queue %s current %"
|
||||
GST_TIME_FORMAT ", min %" GST_TIME_FORMAT,
|
||||
GST_ELEMENT_NAME (queue),
|
||||
GST_TIME_ARGS (time), GST_TIME_ARGS (min_time));
|
||||
if (time != 0) {
|
||||
/* queue knows about time, disable bytes checking. */
|
||||
g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
group_commit (play_base_bin, FALSE,
|
||||
GST_OBJECT_PARENT (GST_OBJECT_CAST (element)) ==
|
||||
GST_OBJECT_PARENT (GST_OBJECT_CAST (queue)) ==
|
||||
GST_OBJECT (play_base_bin->subtitle));
|
||||
|
||||
g_signal_handlers_disconnect_by_func (element,
|
||||
g_signal_handlers_disconnect_by_func (queue,
|
||||
G_CALLBACK (queue_overrun), play_base_bin);
|
||||
/* We have disconnected this signal, remove the signal_id from the object
|
||||
data */
|
||||
g_object_set_data (G_OBJECT (element), "signal_id", NULL);
|
||||
g_object_set_data (G_OBJECT (queue), "signal_id", NULL);
|
||||
}
|
||||
|
||||
/* Used for time-based buffering. */
|
||||
|
@ -522,6 +551,8 @@ queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin)
|
|||
GST_DEBUG ("Running");
|
||||
|
||||
/* play */
|
||||
g_signal_handlers_disconnect_by_func (queue,
|
||||
G_CALLBACK (queue_threshold_reached), play_base_bin);
|
||||
g_object_set (queue, "min-threshold-time", (guint64) 0, NULL);
|
||||
|
||||
data = g_object_get_data (G_OBJECT (queue), "probe");
|
||||
|
@ -542,14 +573,41 @@ queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin)
|
|||
}
|
||||
}
|
||||
|
||||
/* this signal is only added when in streaming mode to catch underruns
|
||||
*/
|
||||
static void
|
||||
queue_out_of_data (GstElement * queue, GstPlayBaseBin * play_base_bin)
|
||||
{
|
||||
guint64 time, min_time;
|
||||
guint bytes, max_bytes;
|
||||
|
||||
GST_DEBUG ("Underrun, re-caching");
|
||||
|
||||
g_object_get (G_OBJECT (queue), "current-level-time", &time,
|
||||
"current-level-bytes", &bytes,
|
||||
"max-size-bytes", &max_bytes, "min-threshold-time", &min_time, NULL);
|
||||
|
||||
GST_DEBUG_OBJECT (play_base_bin, "streaming mode, queue %s current %"
|
||||
GST_TIME_FORMAT ", min %" GST_TIME_FORMAT
|
||||
", bytes %d",
|
||||
GST_ELEMENT_NAME (queue),
|
||||
GST_TIME_ARGS (time), GST_TIME_ARGS (min_time), bytes);
|
||||
|
||||
/* if the bytes in the queue represent time, we disable bytes
|
||||
* overrun checking to not cause deadlocks.
|
||||
*/
|
||||
if (bytes && time != 0 && time < min_time) {
|
||||
/* queue knows about time but is filled with bytes that do
|
||||
* not represent min-threshold time, disable bytes checking so
|
||||
* the queue can grow some more. */
|
||||
g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL);
|
||||
}
|
||||
|
||||
/* On underrun, we want to temoprarily pause playback, set a "min-size"
|
||||
* threshold and wait for the running signal and then play again. Take
|
||||
* care of possible deadlocks and so on, */
|
||||
g_signal_connect (G_OBJECT (queue), "running",
|
||||
G_CALLBACK (queue_threshold_reached), play_base_bin);
|
||||
g_object_set (queue, "min-threshold-time",
|
||||
(guint64) play_base_bin->queue_threshold, NULL);
|
||||
|
||||
|
@ -611,6 +669,19 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
|
|||
g_free (name);
|
||||
g_free (padname);
|
||||
|
||||
/* for buffering of raw data we ideally want to buffer a
|
||||
* very small amount of buffers since the memory used by
|
||||
* this raw data can be enormously huge.
|
||||
*
|
||||
* FIXME: we abuse this buffer to do network buffering since
|
||||
* we can then easily do time-based buffering. The better
|
||||
* solution would be to add a specific network queue right
|
||||
* after the source that measures the datarate and scales this
|
||||
* queue of encoded data instead.
|
||||
*
|
||||
* We use an upper limit of typically a few seconds here but
|
||||
* cap in case no timestamps are set on the raw data (bad!).
|
||||
*/
|
||||
g_object_set (G_OBJECT (preroll),
|
||||
"max-size-buffers", 0, "max-size-bytes",
|
||||
((type == GST_STREAM_TYPE_VIDEO) ? 25 : 1) * 1024 * 1024,
|
||||
|
@ -641,9 +712,22 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
|
|||
g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin);
|
||||
g_object_set_data (G_OBJECT (preroll), "probe", GINT_TO_POINTER (id));
|
||||
|
||||
/*
|
||||
* When we connect this queue, it will start running and immediatly
|
||||
* fire an underrun when:
|
||||
*
|
||||
* 1) the max-bytes is hit
|
||||
* 2) the min-time is not hit.
|
||||
*
|
||||
* We recover from this situation in the out_of_data callback by
|
||||
* setting the max-bytes to unlimited if we see that there is
|
||||
* a current-time-level (which means some sort of timestamping is
|
||||
* done).
|
||||
*/
|
||||
g_signal_connect (G_OBJECT (preroll), "underrun",
|
||||
G_CALLBACK (queue_out_of_data), play_base_bin);
|
||||
}
|
||||
|
||||
/* keep a ref to the signal id so that we can disconnect the signal callback
|
||||
* when we are done with the preroll */
|
||||
g_object_set_data (G_OBJECT (preroll), "signal_id", GINT_TO_POINTER (sig));
|
||||
|
|
Loading…
Reference in a new issue