📄 gstbasesink.c
字号:
event = gst_event_new_qos (proportion, diff, time); /* send upstream */ res = gst_pad_push_event (basesink->sinkpad, event); return res;}static voidgst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped){ GstBaseSinkPrivate *priv; GstClockTime start, stop; GstClockTimeDiff jitter; GstClockTime pt, entered, left; GstClockTime duration; gdouble rate; priv = sink->priv; start = priv->current_rstart; /* if Quality-of-Service disabled, do nothing */ if (!g_atomic_int_get (&priv->qos_enabled) || start == -1) return; stop = priv->current_rstop; jitter = priv->current_jitter; /* this is the time the buffer entered the sink */ entered = start + jitter; /* this is the time the buffer left the sink */ left = start + (jitter < 0 ? 0 : jitter); /* calculate duration of the buffer */ if (stop != -1) duration = stop - start; else duration = -1; /* if we have the time when the last buffer left us, calculate * processing time */ if (priv->last_left != -1) { if (entered > priv->last_left) { pt = entered - priv->last_left; } else { pt = 0; } } else { pt = priv->avg_pt; } GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "start: %" GST_TIME_FORMAT ", entered %" GST_TIME_FORMAT ", left %" GST_TIME_FORMAT ", pt: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ",jitter %" G_GINT64_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt), GST_TIME_ARGS (duration), jitter); GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "avg_duration: %" GST_TIME_FORMAT ", avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g", GST_TIME_ARGS (priv->avg_duration), GST_TIME_ARGS (priv->avg_pt), priv->avg_rate); /* collect running averages. for first observations, we copy the * values */ if (priv->avg_duration == -1) priv->avg_duration = duration; else priv->avg_duration = UPDATE_RUNNING_AVG (priv->avg_duration, duration); if (priv->avg_pt == -1) priv->avg_pt = pt; else priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt); if (priv->avg_duration != 0) rate = gst_guint64_to_gdouble (priv->avg_pt) / gst_guint64_to_gdouble (priv->avg_duration); else rate = 0.0; if (priv->last_left != -1) { if (dropped || priv->avg_rate < 0.0) { priv->avg_rate = rate; } else { if (rate > 1.0) priv->avg_rate = UPDATE_RUNNING_AVG_N (priv->avg_rate, rate); else priv->avg_rate = UPDATE_RUNNING_AVG_P (priv->avg_rate, rate); } } GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "updated: avg_duration: %" GST_TIME_FORMAT ", avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g", GST_TIME_ARGS (priv->avg_duration), GST_TIME_ARGS (priv->avg_pt), priv->avg_rate); /* if we have a valid rate, start sending QoS messages */ if (priv->avg_rate >= 0.0) { gst_base_sink_send_qos (sink, priv->avg_rate, priv->current_rstart, priv->current_jitter); } /* record when this buffer will leave us */ priv->last_left = left;}/* reset all qos measuring */static voidgst_base_sink_reset_qos (GstBaseSink * sink){ GstBaseSinkPrivate *priv; priv = sink->priv; priv->last_in_time = -1; priv->last_left = -1; priv->avg_duration = -1; priv->avg_pt = -1; priv->avg_rate = -1.0; priv->avg_render = -1; priv->rendered = 0; priv->dropped = 0;}/* Checks if the object was scheduled too late. * * start/stop contain the raw timestamp start and stop values * of the object. * * status and jitter contain the return values from the clock wait. * * returns TRUE if the buffer was too late. */static gbooleangst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj, GstClockTime start, GstClockTime stop, GstClockReturn status, GstClockTimeDiff jitter){ gboolean late; gint64 max_lateness; GstBaseSinkPrivate *priv; priv = basesink->priv; late = FALSE; /* only for objects that were too late */ if (G_LIKELY (status != GST_CLOCK_EARLY)) goto in_time; max_lateness = basesink->abidata.ABI.max_lateness; /* check if frame dropping is enabled */ if (max_lateness == -1) goto no_drop; /* only check for buffers */ if (G_UNLIKELY (!GST_IS_BUFFER (obj))) goto not_buffer; /* can't do check if we don't have a timestamp */ if (G_UNLIKELY (start == -1)) goto no_timestamp; /* we can add a valid stop time */ if (stop != -1) max_lateness += stop; else max_lateness += start; /* if the jitter bigger than duration and lateness we are too late */ if ((late = start + jitter > max_lateness)) { GST_DEBUG_OBJECT (basesink, "buffer is too late %" GST_TIME_FORMAT " > %" GST_TIME_FORMAT, GST_TIME_ARGS (start + jitter), GST_TIME_ARGS (max_lateness)); /* !!emergency!!, if we did not receive anything valid for more than a * second, render it anyway so the user sees something */ if (priv->last_in_time && start - priv->last_in_time > GST_SECOND) { late = FALSE; GST_DEBUG_OBJECT (basesink, "**emergency** last buffer at %" GST_TIME_FORMAT " > GST_SECOND", GST_TIME_ARGS (priv->last_in_time)); } }done: if (!late) { priv->last_in_time = start; } return late; /* all is fine */in_time: { GST_DEBUG_OBJECT (basesink, "object was scheduled in time"); goto done; }no_drop: { GST_DEBUG_OBJECT (basesink, "frame dropping disabled"); goto done; }not_buffer: { GST_DEBUG_OBJECT (basesink, "object is not a buffer"); return FALSE; }no_timestamp: { GST_DEBUG_OBJECT (basesink, "buffer has no timestamp"); return FALSE; }}static voidgst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start){ GstBaseSinkPrivate *priv; priv = basesink->priv; if (start) { g_get_current_time (&priv->start); } else { GstClockTime elapsed; g_get_current_time (&priv->stop); elapsed = GST_TIMEVAL_TO_TIME (priv->stop) - GST_TIMEVAL_TO_TIME (priv->start); if (priv->avg_render == -1) priv->avg_render = elapsed; else priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed); GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink, "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render)); }}/* with STREAM_LOCK, PREROLL_LOCK, * * Synchronize the object on the clock and then render it. * * takes ownership of obj. */static GstFlowReturngst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad, GstMiniObject * obj){ GstFlowReturn ret = GST_FLOW_OK; GstBaseSinkClass *bclass; gboolean late = FALSE; GstBaseSinkPrivate *priv; priv = basesink->priv; /* synchronize this object, non syncable objects return OK * immediatly. */ ret = gst_base_sink_do_sync (basesink, pad, obj, &late); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto sync_failed; /* and now render, event or buffer. */ if (G_LIKELY (GST_IS_BUFFER (obj))) { GstBuffer *buf; /* drop late buffers unconditionally, let's hope it's unlikely */ if (G_UNLIKELY (late)) goto dropped; buf = GST_BUFFER_CAST (obj); gst_base_sink_set_last_buffer (basesink, buf); bclass = GST_BASE_SINK_GET_CLASS (basesink); if (G_LIKELY (bclass->render)) { gint do_qos; /* read once, to get same value before and after */ do_qos = g_atomic_int_get (&priv->qos_enabled); GST_DEBUG_OBJECT (basesink, "rendering buffer %p", obj); /* record rendering time for QoS and stats */ if (do_qos) gst_base_sink_do_render_stats (basesink, TRUE); ret = bclass->render (basesink, buf); priv->rendered++; if (do_qos) gst_base_sink_do_render_stats (basesink, FALSE); } } else { GstEvent *event = GST_EVENT_CAST (obj); gboolean event_res = TRUE; GstEventType type; bclass = GST_BASE_SINK_GET_CLASS (basesink); type = GST_EVENT_TYPE (event); GST_DEBUG_OBJECT (basesink, "rendering event %p, type %s", obj, gst_event_type_get_name (type)); if (bclass->event) event_res = bclass->event (basesink, event); if (G_LIKELY (event_res)) { switch (type) { case GST_EVENT_EOS: /* the EOS event is completely handled so we mark * ourselves as being in the EOS state. eos is also * protected by the object lock so we can read it when * answering the POSITION query. */ GST_OBJECT_LOCK (basesink); basesink->eos = TRUE; GST_OBJECT_UNLOCK (basesink); /* ok, now we can post the message */ GST_DEBUG_OBJECT (basesink, "Now posting EOS"); gst_element_post_message (GST_ELEMENT_CAST (basesink), gst_message_new_eos (GST_OBJECT_CAST (basesink))); break; case GST_EVENT_NEWSEGMENT: /* configure the segment */ gst_base_sink_configure_segment (basesink, pad, event, &basesink->segment); break; default: break; } } }done: gst_base_sink_perform_qos (basesink, late); GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj); gst_mini_object_unref (obj); return ret; /* ERRORS */sync_failed: { GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret)); goto done; }dropped: { priv->dropped++; GST_DEBUG_OBJECT (basesink, "buffer late, dropping"); goto done; }}/* with STREAM_LOCK, PREROLL_LOCK * * Perform preroll on the given object. For buffers this means * calling the preroll subclass method. * If that succeeds, the state will be commited. * * function does not take ownership of obj. */static GstFlowReturngst_base_sink_preroll_object (GstBaseSink * basesink, GstPad * pad, GstMiniObject * obj){ GstFlowReturn ret; GST_DEBUG_OBJECT (basesink, "do preroll %p", obj); /* if it's a buffer, we need to call the preroll method */ if (G_LIKELY (GST_IS_BUFFER (obj))) { GstBaseSinkClass *bclass; GstBuffer *buf; GstClockTime timestamp; buf = GST_BUFFER_CAST (obj); timestamp = GST_BUFFER_TIMESTAMP (buf); GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); gst_base_sink_set_last_buffer (basesink, buf); bclass = GST_BASE_SINK_GET_CLASS (basesink); if (bclass->preroll) if ((ret = bclass->preroll (basesink, buf)) != GST_FLOW_OK) goto preroll_failed; } /* commit state */ if (G_LIKELY (basesink->playing_async)) { if (G_UNLIKELY (!gst_base_sink_commit_state (basesink))) goto stopping; } return GST_FLOW_OK; /* ERRORS */preroll_failed: { GST_DEBUG_OBJECT (basesink, "preroll failed, abort state"); gst_element_abort_state (GST_ELEMENT_CAST (basesink)); return ret; }stopping: { GST_DEBUG_OBJECT (basesink, "stopping while commiting state"); return GST_FLOW_WRONG_STATE; }}/* with STREAM_LOCK, PREROLL_LOCK * * Queue an object for rendering. * The first prerollable object queued will complete the preroll. If the * preroll queue if filled, we render all the objects in the queue. * * This function takes ownership of the object. */static GstFlowReturngst_base_sink_queue_object_unlocked (GstB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -