📄 gstbasesink.c
字号:
sstart = sstop = priv->current_sstop; rstart = rstop = priv->eos_rtime; *do_sync = rstart != -1; GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT, GST_TIME_ARGS (rstart)); goto done; /* other events do not need syncing */ /* FIXME, maybe NEWSEGMENT might need synchronisation * since the POSITION query depends on accumulated times and * we cannot accumulate the current segment before the previous * one completed. */ default: return FALSE; } } /* else do buffer sync code */ buffer = GST_BUFFER_CAST (obj); bclass = GST_BASE_SINK_GET_CLASS (basesink); /* just get the times to see if we need syncing */ if (bclass->get_times) bclass->get_times (basesink, buffer, &start, &stop); if (start == -1) { gst_base_sink_get_times (basesink, buffer, &start, &stop); *do_sync = FALSE; } else { *do_sync = TRUE; } GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT ", do_sync %d", GST_TIME_ARGS (start), GST_TIME_ARGS (stop), *do_sync); /* collect segment and format for code clarity */ format = segment->format; /* no timestamp clipping if we did not * get a TIME segment format */ if (G_UNLIKELY (format != GST_FORMAT_TIME)) { cstart = start; cstop = stop; /* do running and stream time in TIME format */ format = GST_FORMAT_TIME; goto do_times; } /* clip */ if (G_UNLIKELY (!gst_segment_clip (segment, GST_FORMAT_TIME, (gint64) start, (gint64) stop, &cstart, &cstop))) goto out_of_segment; if (G_UNLIKELY (start != cstart || stop != cstop)) { GST_DEBUG_OBJECT (basesink, "clipped to: start %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (cstart), GST_TIME_ARGS (cstop)); } /* set last stop position */ gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstop);do_times: /* this can produce wrong values if we accumulated non-TIME segments. If this happens, * upstream is behaving very badly */ sstart = gst_segment_to_stream_time (segment, format, cstart); sstop = gst_segment_to_stream_time (segment, format, cstop); rstart = gst_segment_to_running_time (segment, format, cstart); rstop = gst_segment_to_running_time (segment, format, cstop);done: /* save times */ *rsstart = sstart; *rsstop = sstop; *rrstart = rstart; *rrstop = rstop; /* buffers and EOS always need syncing and preroll */ return TRUE; /* special cases */out_of_segment: { /* should not happen since we clip them in the chain function already, * we return FALSE so that we don't try to sync on it. */ GST_ELEMENT_WARNING (basesink, STREAM, FAILED, (NULL), ("unexpected buffer out of segment found.")); GST_LOG_OBJECT (basesink, "buffer skipped, not in segment"); return FALSE; }}/* with STREAM_LOCK, PREROLL_LOCK * * Waits for the clock to reach @time. If @time is not valid, no * synchronisation is done and BADTIME is returned. * If synchronisation is disabled in the element or there is no * clock, no synchronisation is done and BADTIME is returned. * * Else a blocking wait is performed on the clock. We save the ClockID * so we can unlock the entry at any time. While we are blocking, we * release the PREROLL_LOCK so that other threads can interrupt the entry. * * @time is expressed in running time. */static GstClockReturngst_base_sink_wait_clock (GstBaseSink * basesink, GstClockTime time, GstClockTimeDiff * jitter){ GstClockID id; GstClockReturn ret; GstClock *clock; GstClockTimeDiff ts_offset; if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) goto invalid_time; GST_OBJECT_LOCK (basesink); if (G_UNLIKELY (!basesink->sync)) goto no_sync; if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL)) goto no_clock; /* add base time and latency */ time += GST_ELEMENT_CAST (basesink)->base_time; time += basesink->priv->latency; /* apply offset, be carefull for underflows */ ts_offset = basesink->priv->ts_offset; if (ts_offset < 0) { ts_offset = -ts_offset; if (ts_offset < time) time -= ts_offset; else time = 0; } else time += ts_offset; id = gst_clock_new_single_shot_id (clock, time); GST_OBJECT_UNLOCK (basesink); basesink->clock_id = id; /* release the preroll lock while waiting */ GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); ret = gst_clock_id_wait (id, jitter); GST_PAD_PREROLL_LOCK (basesink->sinkpad); gst_clock_id_unref (id); basesink->clock_id = NULL; return ret; /* no syncing needed */invalid_time: { GST_DEBUG_OBJECT (basesink, "time not valid, no sync needed"); return GST_CLOCK_BADTIME; }no_sync: { GST_DEBUG_OBJECT (basesink, "sync disabled"); GST_OBJECT_UNLOCK (basesink); return GST_CLOCK_BADTIME; }no_clock: { GST_DEBUG_OBJECT (basesink, "no clock, can't sync"); GST_OBJECT_UNLOCK (basesink); return GST_CLOCK_BADTIME; }}/** * gst_base_sink_wait_preroll: * @sink: the sink * * If the #GstBaseSinkClass::render method performs its own synchronisation against * the clock it must unblock when going from PLAYING to the PAUSED state and call * this method before continuing to render the remaining data. * * This function will block until a state change to PLAYING happens (in which * case this function returns #GST_FLOW_OK) or the processing must be stopped due * to a state change to READY or a FLUSH event (in which case this function * returns #GST_FLOW_WRONG_STATE). * * Since: 0.10.11 * * Returns: #GST_FLOW_OK if the preroll completed and processing can * continue. Any other return value should be returned from the render vmethod. */GstFlowReturngst_base_sink_wait_preroll (GstBaseSink * sink){ sink->have_preroll = TRUE; GST_DEBUG_OBJECT (sink, "waiting in preroll for flush or PLAYING"); /* block until the state changes, or we get a flush, or something */ GST_PAD_PREROLL_WAIT (sink->sinkpad); sink->have_preroll = FALSE; if (G_UNLIKELY (sink->flushing)) goto stopping; GST_DEBUG_OBJECT (sink, "continue after preroll"); return GST_FLOW_OK; /* ERRORS */stopping: { GST_DEBUG_OBJECT (sink, "preroll interrupted"); return GST_FLOW_WRONG_STATE; }}/** * gst_base_sink_wait_eos: * @sink: the sink * @time: the running_time to be reached * @jitter: the jitter to be filled with time diff (can be NULL) * * This function will block until @time is reached. It is usually called by * subclasses that use their own internal synchronisation but want to let the * EOS be handled by the base class. * * This function should only be called with the PREROLL_LOCK held, like when * receiving an EOS event in the ::event vmethod. * * Since 0.10.15 * * Returns: #GstFlowReturn */GstFlowReturngst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time, GstClockTimeDiff * jitter){ GstClockReturn status; GstFlowReturn ret; do { GST_DEBUG_OBJECT (sink, "checking preroll"); /* first wait for the playing state before we can continue */ if (G_UNLIKELY (sink->need_preroll)) { ret = gst_base_sink_wait_preroll (sink); if (ret != GST_FLOW_OK) goto flushing; } /* preroll done, we can sync since we are in PLAYING now. */ GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %" GST_TIME_FORMAT, GST_TIME_ARGS (time)); /* wait for the clock, this can be interrupted because we got shut down or * we PAUSED. */ status = gst_base_sink_wait_clock (sink, time, jitter); GST_DEBUG_OBJECT (sink, "clock returned %d", status); /* invalid time, no clock or sync disabled, just continue then */ if (status == GST_CLOCK_BADTIME) break; /* waiting could have been interrupted and we can be flushing now */ if (G_UNLIKELY (sink->flushing)) goto flushing; /* retry if we got unscheduled, which means we did not reach the timeout * yet. if some other error occures, we continue. */ } while (status == GST_CLOCK_UNSCHEDULED); GST_DEBUG_OBJECT (sink, "end of stream"); return GST_FLOW_OK; /* ERRORS */flushing: { GST_DEBUG_OBJECT (sink, "we are flushing"); return GST_FLOW_WRONG_STATE; }}/* with STREAM_LOCK, PREROLL_LOCK * * Make sure we are in PLAYING and synchronize an object to the clock. * * If we need preroll, we are not in PLAYING. We try to commit the state * if needed and then block if we still are not PLAYING. * * We start waiting on the clock in PLAYING. If we got interrupted, we * immediatly try to re-preroll. * * Some objects do not need synchronisation (most events) and so this function * immediatly returns GST_FLOW_OK. * * for objects that arrive later than max-lateness to be synchronized to the * clock have the @late boolean set to TRUE. * * This function keeps a running average of the jitter (the diff between the * clock time and the requested sync time). The jitter is negative for * objects that arrive in time and positive for late buffers. * * does not take ownership of obj. */static GstFlowReturngst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad, GstMiniObject * obj, gboolean * late){ GstClockTimeDiff jitter; gboolean syncable; GstClockReturn status = GST_CLOCK_OK; GstClockTime rstart, rstop, sstart, sstop; gboolean do_sync; GstBaseSinkPrivate *priv; priv = basesink->priv; sstart = sstop = rstart = rstop = -1; do_sync = TRUE; priv->current_rstart = -1; /* get timing information for this object against the render segment */ syncable = gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart, &rstop, &do_sync, &basesink->segment); /* a syncable object needs to participate in preroll and * clocking. All buffers and EOS are syncable. */ if (G_UNLIKELY (!syncable)) goto not_syncable; /* store timing info for current object */ priv->current_rstart = rstart; priv->current_rstop = (rstop != -1 ? rstop : rstart); /* save sync time for eos when the previous object needed sync */ priv->eos_rtime = (do_sync ? priv->current_rstop : -1);again: /* first do preroll, this makes sure we commit our state * to PAUSED and can continue to PLAYING. We cannot perform * any clock sync in PAUSED because there is no clock. */ while (G_UNLIKELY (basesink->need_preroll)) { GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj); if (G_LIKELY (basesink->playing_async)) { /* commit state */ if (G_UNLIKELY (!gst_base_sink_commit_state (basesink))) goto stopping; } /* need to recheck here because the commit state could have * made us not need the preroll anymore */ if (G_LIKELY (basesink->need_preroll)) { /* block until the state changes, or we get a flush, or something */ if (gst_base_sink_wait_preroll (basesink) != GST_FLOW_OK) goto flushing; } } /* After rendering we store the position of the last buffer so that we can use * it to report the position. We need to take the lock here. */ GST_OBJECT_LOCK (basesink); priv->current_sstart = sstart; priv->current_sstop = (sstop != -1 ? sstop : sstart); GST_OBJECT_UNLOCK (basesink); if (!do_sync) goto done; /* preroll done, we can sync since we are in PLAYING now. */ GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %" GST_TIME_FORMAT, GST_TIME_ARGS (rstart)); /* this function will return immediatly if start == -1, no clock * or sync is disabled with GST_CLOCK_BADTIME. */ status = gst_base_sink_wait_clock (basesink, rstart, &jitter); GST_DEBUG_OBJECT (basesink, "clock returned %d", status); /* invalid time, no clock or sync disabled, just render */ if (status == GST_CLOCK_BADTIME) goto done; /* waiting could have been interrupted and we can be flushing now */ if (G_UNLIKELY (basesink->flushing)) goto flushing; /* check for unlocked by a state change, we are not flushing so * we can try to preroll on the current buffer. */ if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) { GST_DEBUG_OBJECT (basesink, "unscheduled, waiting some more"); goto again; } /* successful syncing done, record observation */ priv->current_jitter = jitter; /* check if the object should be dropped */ *late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop, status, jitter);done: return GST_FLOW_OK; /* ERRORS */not_syncable: { GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj); return GST_FLOW_OK; }flushing: { GST_DEBUG_OBJECT (basesink, "we are flushing"); return GST_FLOW_WRONG_STATE; }stopping: { GST_DEBUG_OBJECT (basesink, "stopping while commiting state"); return GST_FLOW_WRONG_STATE; }}static gbooleangst_base_sink_send_qos (GstBaseSink * basesink, gdouble proportion, GstClockTime time, GstClockTimeDiff diff){ GstEvent *event; gboolean res; /* generate Quality-of-Service event */ GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink, "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %" GST_TIME_FORMAT, proportion, diff, GST_TIME_ARGS (time));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -