mirror of
https://github.com/go-gst/go-gst.git
synced 2025-09-26 20:11:18 +08:00
370 lines
15 KiB
Go
370 lines
15 KiB
Go
package gst
|
|
|
|
/*
|
|
#include "gst.go.h"
|
|
|
|
extern gboolean goClockCb (GstClock * clock, GstClockTime time, GstClockID id, gpointer user_data);
|
|
extern void goGDestroyNotifyFuncNoRun (gpointer user_data);
|
|
|
|
gboolean cgoClockCb (GstClock * clock, GstClockTime time, GstClockID id, gpointer user_data)
|
|
{
|
|
return goClockCb(clock, time, id, user_data);
|
|
}
|
|
|
|
void clockDestroyNotify (gpointer user_data)
|
|
{
|
|
goGDestroyNotifyFuncNoRun(user_data);
|
|
}
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"runtime"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/go-gst/go-glib/glib"
|
|
gopointer "github.com/mattn/go-pointer"
|
|
)
|
|
|
|
// ClockCallback is the prototype of a clock callback function.
|
|
type ClockCallback func(clock *Clock, clockTime time.Duration) bool
|
|
|
|
// ClockID is a go wrapper around a GstClockID.
|
|
type ClockID struct {
|
|
ptr C.GstClockID // which is actually just a casted pointer
|
|
}
|
|
|
|
// Instance returns the underlying pointer.
|
|
func (c *ClockID) Instance() C.GstClockID { return c.ptr }
|
|
|
|
// GetClock returns the clock for this ClockID.
|
|
func (c *ClockID) GetClock() *Clock {
|
|
clk := C.gst_clock_id_get_clock(c.Instance())
|
|
return FromGstClockUnsafeFull(unsafe.Pointer(clk))
|
|
}
|
|
|
|
// GetTime returns the time for this ClockID
|
|
func (c *ClockID) GetTime() time.Duration {
|
|
return time.Duration(C.gst_clock_id_get_time(c.Instance()))
|
|
}
|
|
|
|
// Unschedule cancels an outstanding request with id. This can either be an outstanding async notification or a pending sync notification.
|
|
// After this call, id cannot be used anymore to receive sync or async notifications, you need to create a new GstClockID.
|
|
func (c *ClockID) Unschedule() {
|
|
C.gst_clock_id_unschedule(c.Instance())
|
|
}
|
|
|
|
// UsesClock returns whether id uses clock as the underlying clock. clock can be nil, in which case the return value indicates whether the
|
|
// underlying clock has been freed. If this is the case, the id is no longer usable and should be freed.
|
|
func (c *ClockID) UsesClock(clock *Clock) bool {
|
|
return gobool(C.gst_clock_id_uses_clock(c.Instance(), clock.Instance()))
|
|
}
|
|
|
|
// Wait performs a blocking wait on id. id should have been created with NewSingleShotID or NewPeriodicID and should not have been unscheduled
|
|
// with a call to Unschedule.
|
|
//
|
|
// If the jitter argument is not 0 and this function returns ClockOK or ClockEarly, it will contain the difference against the clock and the
|
|
// time of id when this method was called. Positive values indicate how late id was relative to the clock (in which case this function will
|
|
// return ClockEarly). Negative values indicate how much time was spent waiting on the clock before this function returned.
|
|
func (c *ClockID) Wait() (ret ClockReturn, jitter ClockTimeDiff) {
|
|
var gjitter C.GstClockTimeDiff
|
|
ret = ClockReturn(C.gst_clock_id_wait(c.Instance(), &gjitter))
|
|
jitter = ClockTimeDiff(gjitter)
|
|
return
|
|
}
|
|
|
|
// WaitAsync registers a callback on the given ClockID id with the given function and user_data. When passing a ClockID with an invalid time to
|
|
// this function, the callback will be called immediately with a time set to ClockTimeNone. The callback will be called when the time of id has been reached.
|
|
//
|
|
// The callback func can be invoked from any thread, either provided by the core or from a streaming thread. The application should be prepared for this.
|
|
//
|
|
// // Example
|
|
//
|
|
// pipeline, _ := gst.NewPipelineFromString("fakesrc ! fakesink")
|
|
// defer pipeline.Unref()
|
|
//
|
|
// clock := pipeline.GetPipelineClock()
|
|
//
|
|
// id := clock.NewSingleShotID(gst.ClockTime(1000000000)) // 1 second
|
|
//
|
|
// id.WaitAsync(func(clock *gst.Clock, clockTime time.Duration) bool {
|
|
// fmt.Println("Single shot triggered at", clockTime.Nanoseconds())
|
|
// pipeline.SetState(gst.StateNull)
|
|
// return true
|
|
// })
|
|
//
|
|
// pipeline.SetState(gst.StatePlaying)
|
|
// gst.Wait(pipeline)
|
|
//
|
|
// // Single shot triggered at 1000000000
|
|
func (c *ClockID) WaitAsync(f ClockCallback) ClockReturn {
|
|
ptr := gopointer.Save(f)
|
|
return ClockReturn(C.gst_clock_id_wait_async(
|
|
c.Instance(),
|
|
C.GstClockCallback(C.cgoClockCb),
|
|
(C.gpointer)(unsafe.Pointer(ptr)),
|
|
C.GDestroyNotify(C.clockDestroyNotify),
|
|
))
|
|
}
|
|
|
|
// Ref increaes the ref count on ClockID.
|
|
func (c *ClockID) Ref() *ClockID {
|
|
C.gst_clock_id_ref(c.Instance())
|
|
return c
|
|
}
|
|
|
|
// Unref unrefs a ClockID.
|
|
func (c *ClockID) Unref() {
|
|
C.gst_clock_id_unref(c.Instance())
|
|
}
|
|
|
|
// Clock is a go wrapper around a GstClock.
|
|
type Clock struct{ *Object }
|
|
|
|
// FromGstClockUnsafeNone takes a pointer to a GstClock and wraps it in a Clock instance.
|
|
// A ref is taken on the clock and a finalizer applied.
|
|
func FromGstClockUnsafeNone(clock unsafe.Pointer) *Clock {
|
|
return wrapClock(glib.TransferNone(clock))
|
|
}
|
|
|
|
// FromGstClockUnsafeFull takes a pointer to a GstClock and wraps it in a Clock instance.
|
|
// A finalizer is set on the returned object.
|
|
func FromGstClockUnsafeFull(clock unsafe.Pointer) *Clock {
|
|
return wrapClock(glib.TransferFull(clock))
|
|
}
|
|
|
|
// Instance returns the underlying GstClock instance.
|
|
func (c *Clock) Instance() *C.GstClock { return C.toGstClock(c.Unsafe()) }
|
|
|
|
// AddObservation adds the time master of the master clock and the time slave of the slave clock to the list of observations.
|
|
// If enough observations are available, a linear regression algorithm is run on the observations and clock is recalibrated.
|
|
//
|
|
// If this functions returns TRUE, the float will contain the correlation coefficient of the interpolation. A value of 1.0 means
|
|
// a perfect regression was performed. This value can be used to control the sampling frequency of the master and slave clocks.
|
|
func (c *Clock) AddObservation(slaveTime, masterTime time.Duration) (bool, float64) {
|
|
var out C.gdouble
|
|
ok := gobool(C.gst_clock_add_observation(
|
|
c.Instance(),
|
|
C.GstClockTime(slaveTime),
|
|
C.GstClockTime(masterTime),
|
|
&out,
|
|
))
|
|
return ok, float64(out)
|
|
}
|
|
|
|
// AddObservationUnapplied adds a clock observation to the internal slaving algorithm the same as AddObservation, and returns the
|
|
// result of the master clock estimation, without updating the internal calibration.
|
|
//
|
|
// The caller can then take the results and call SetCalibration with the values, or some modified version of them.
|
|
func (c *Clock) AddObservationUnapplied(slaveTime, masterTime time.Duration) (ok bool, rSquared float64, internalTime, externalTime, rateNum, rateDenom time.Duration) {
|
|
var ginternal, gexternal, grateNum, grateDenom C.GstClockTime
|
|
var grSquared C.gdouble
|
|
ok = gobool(C.gst_clock_add_observation_unapplied(
|
|
c.Instance(),
|
|
C.GstClockTime(slaveTime),
|
|
C.GstClockTime(masterTime),
|
|
&grSquared, &ginternal, &gexternal, &grateNum, &grateDenom,
|
|
))
|
|
return ok, float64(grSquared), time.Duration(ginternal), time.Duration(gexternal), time.Duration(grateNum), time.Duration(grateDenom)
|
|
}
|
|
|
|
// AdjustUnlocked converts the given internal clock time to the external time, adjusting for the rate and reference time set with
|
|
// SetCalibration and making sure that the returned time is increasing. This function should be called with the clock's OBJECT_LOCK
|
|
// held and is mainly used by clock subclasses.
|
|
//
|
|
// This function is the reverse of UnadjustUnlocked.
|
|
func (c *Clock) AdjustUnlocked(internal time.Duration) time.Duration {
|
|
return time.Duration(C.gst_clock_adjust_unlocked(c.Instance(), C.GstClockTime(internal)))
|
|
}
|
|
|
|
// AdjustWithCalibration converts the given internal_target clock time to the external time, using the passed calibration parameters.
|
|
// This function performs the same calculation as AdjustUnlocked when called using the current calibration parameters, but
|
|
// doesn't ensure a monotonically increasing result as AdjustUnlocked does.
|
|
//
|
|
// See: https://gstreamer.freedesktop.org/documentation/gstreamer/gstclock.html#gst_clock_adjust_with_calibration
|
|
func (c *Clock) AdjustWithCalibration(internalTarget, cinternal, cexternal, cnum, cdenom time.Duration) time.Duration {
|
|
return time.Duration(C.gst_clock_adjust_with_calibration(
|
|
c.Instance(),
|
|
C.GstClockTime(internalTarget),
|
|
C.GstClockTime(cinternal),
|
|
C.GstClockTime(cexternal),
|
|
C.GstClockTime(cnum),
|
|
C.GstClockTime(cdenom),
|
|
))
|
|
}
|
|
|
|
// GetCalibration gets the internal rate and reference time of clock. See gst_clock_set_calibration for more information.
|
|
func (c *Clock) GetCalibration() (internal, external, rateNum, rateDenom time.Duration) {
|
|
var ginternal, gexternal, grateNum, grateDenom C.GstClockTime
|
|
C.gst_clock_get_calibration(c.Instance(), &ginternal, &gexternal, &grateNum, &grateDenom)
|
|
return time.Duration(ginternal), time.Duration(gexternal), time.Duration(grateNum), time.Duration(grateDenom)
|
|
}
|
|
|
|
// GetTime gets the current time of the given clock in nanoseconds or -1 if invalid.
|
|
// The time is always monotonically increasing and adjusted according to the current offset and rate.
|
|
func (c *Clock) GetTime() time.Duration {
|
|
res := C.gst_clock_get_time(c.Instance())
|
|
if res == gstClockTimeNone {
|
|
return ClockTimeNone
|
|
}
|
|
return time.Duration(res)
|
|
}
|
|
|
|
// GetInternalTime gets the current internal time of the given clock in nanoseconds
|
|
// or ClockTimeNone if invalid. The time is returned unadjusted for the offset and the rate.
|
|
func (c *Clock) GetInternalTime() time.Duration {
|
|
res := C.gst_clock_get_internal_time(c.Instance())
|
|
if res == gstClockTimeNone {
|
|
return ClockTimeNone
|
|
}
|
|
return time.Duration(res)
|
|
}
|
|
|
|
// GetMaster returns the master clock that this clock is slaved to or nil when the clock
|
|
// is not slaved to any master clock.
|
|
func (c *Clock) GetMaster() *Clock {
|
|
clock := C.gst_clock_get_master(c.Instance())
|
|
if clock == nil {
|
|
return nil
|
|
}
|
|
return FromGstClockUnsafeFull(unsafe.Pointer(clock))
|
|
}
|
|
|
|
// GetResolution gets the accuracy of the clock. The accuracy of the clock is the granularity
|
|
// of the values returned by GetTime.
|
|
func (c *Clock) GetResolution() time.Duration {
|
|
return time.Duration(C.gst_clock_get_resolution(c.Instance()))
|
|
}
|
|
|
|
// GetTimeout gets the amount of time that master and slave clocks are sampled.
|
|
func (c *Clock) GetTimeout() time.Duration {
|
|
return time.Duration(C.gst_clock_get_timeout(c.Instance()))
|
|
}
|
|
|
|
// IsSynced returns true if the clock is synced.
|
|
func (c *Clock) IsSynced() bool { return gobool(C.gst_clock_is_synced(c.Instance())) }
|
|
|
|
// NewPeriodicID gets an ID from clock to trigger a periodic notification. The periodic notifications
|
|
// will start at time start_time and will then be fired with the given interval. ID should be unreffed after usage.
|
|
func (c *Clock) NewPeriodicID(startTime, interval time.Duration) *ClockID {
|
|
id := C.gst_clock_new_periodic_id(
|
|
c.Instance(),
|
|
C.GstClockTime(startTime),
|
|
C.GstClockTime(interval),
|
|
)
|
|
clkid := &ClockID{id}
|
|
runtime.SetFinalizer(clkid, (*ClockID).Unref)
|
|
return clkid
|
|
}
|
|
|
|
// NewSingleShotID gets a ClockID from the clock to trigger a single shot notification at the requested time.
|
|
// The single shot id should be unreffed after usage.
|
|
func (c *Clock) NewSingleShotID(at time.Duration) *ClockID {
|
|
id := C.gst_clock_new_single_shot_id(
|
|
c.Instance(),
|
|
C.GstClockTime(at),
|
|
)
|
|
clkid := &ClockID{id}
|
|
runtime.SetFinalizer(clkid, (*ClockID).Unref)
|
|
return clkid
|
|
}
|
|
|
|
// PeriodicIDReinit reinitializes the provided periodic id to the provided start time and interval. Does not
|
|
// / modify the reference count.
|
|
func (c *Clock) PeriodicIDReinit(clockID *ClockID, startTime, interval time.Duration) bool {
|
|
return gobool(C.gst_clock_periodic_id_reinit(
|
|
c.Instance(),
|
|
clockID.Instance(),
|
|
C.GstClockTime(startTime),
|
|
C.GstClockTime(interval),
|
|
))
|
|
}
|
|
|
|
// SetCalibration adjusts the rate and time of clock.
|
|
// See: https://gstreamer.freedesktop.org/documentation/gstreamer/gstclock.html#gst_clock_set_calibration.
|
|
func (c *Clock) SetCalibration(internal, external, rateNum, rateDenom time.Duration) {
|
|
C.gst_clock_set_calibration(
|
|
c.Instance(),
|
|
C.GstClockTime(internal),
|
|
C.GstClockTime(external),
|
|
C.GstClockTime(rateNum),
|
|
C.GstClockTime(rateDenom),
|
|
)
|
|
}
|
|
|
|
// SetMaster sets master as the master clock for clock. clock will be automatically calibrated so that
|
|
// GetTime reports the same time as the master clock.
|
|
//
|
|
// A clock provider that slaves its clock to a master can get the current calibration values with GetCalibration.
|
|
//
|
|
// Master can be nil in which case clock will not be slaved anymore. It will however keep reporting its time
|
|
// adjusted with the last configured rate and time offsets.
|
|
func (c *Clock) SetMaster(master *Clock) bool {
|
|
if master == nil {
|
|
return gobool(C.gst_clock_set_master(c.Instance(), nil))
|
|
}
|
|
return gobool(C.gst_clock_set_master(c.Instance(), master.Instance()))
|
|
}
|
|
|
|
// SetResolution sets the accuracy of the clock. Some clocks have the possibility to operate with different accuracy
|
|
// at the expense of more resource usage. There is normally no need to change the default resolution of a clock.
|
|
// The resolution of a clock can only be changed if the clock has the ClockFlagCanSetResolution flag set.
|
|
func (c *Clock) SetResolution(resolution time.Duration) time.Duration {
|
|
return time.Duration(C.gst_clock_set_resolution(c.Instance(), C.GstClockTime(resolution)))
|
|
}
|
|
|
|
// SetSynced sets clock to synced and emits the GstClock::synced signal, and wakes up any thread waiting in WaitForSync.
|
|
//
|
|
// This function must only be called if ClockFlagNeedsStartupSync is set on the clock, and is intended to be called by
|
|
// subclasses only.
|
|
func (c *Clock) SetSynced(synced bool) { C.gst_clock_set_synced(c.Instance(), gboolean(synced)) }
|
|
|
|
// SetTimeout sets the amount of time, in nanoseconds, to sample master and slave clocks
|
|
func (c *Clock) SetTimeout(timeout time.Duration) {
|
|
C.gst_clock_set_timeout(c.Instance(), C.GstClockTime(timeout))
|
|
}
|
|
|
|
// SingleShotIDReinit reinitializes the provided single shot id to the provided time. Does not modify the reference count.
|
|
func (c *Clock) SingleShotIDReinit(clockID *ClockID, at time.Duration) bool {
|
|
return gobool(C.gst_clock_single_shot_id_reinit(c.Instance(), clockID.Instance(), C.GstClockTime(at)))
|
|
}
|
|
|
|
// UnadjustUnlocked converts the given external clock time to the internal time of clock, using the rate and reference time
|
|
// set with SetCalibration. This function should be called with the clock's OBJECT_LOCK held and is mainly used by clock subclasses.
|
|
//
|
|
// This function is the reverse of AdjustUnlocked.
|
|
func (c *Clock) UnadjustUnlocked(external time.Duration) time.Duration {
|
|
return time.Duration(C.gst_clock_unadjust_unlocked(c.Instance(), C.GstClockTime(external)))
|
|
}
|
|
|
|
// UnadjustWithCalibration converts the given external_target clock time to the internal time, using the passed calibration parameters.
|
|
// This function performs the same calculation as UnadjustUnlocked when called using the current calibration parameters.
|
|
func (c *Clock) UnadjustWithCalibration(externalTarget, cinternal, cexternal, cnum, cdenom time.Duration) time.Duration {
|
|
return time.Duration(C.gst_clock_unadjust_with_calibration(
|
|
c.Instance(),
|
|
C.GstClockTime(externalTarget),
|
|
C.GstClockTime(cinternal),
|
|
C.GstClockTime(cexternal),
|
|
C.GstClockTime(cnum),
|
|
C.GstClockTime(cdenom),
|
|
))
|
|
}
|
|
|
|
// WaitForSync waits until clock is synced for reporting the current time. If timeout is ClockTimeNone it will wait forever, otherwise it
|
|
// will time out after timeout nanoseconds.
|
|
//
|
|
// For asynchronous waiting, the GstClock::synced signal can be used.
|
|
//
|
|
// This returns immediately with TRUE if ClockFlagNeedsStartupSync is not set on the clock, or if the clock is already synced.
|
|
func (c *Clock) WaitForSync(timeout time.Duration) bool {
|
|
return gobool(C.gst_clock_wait_for_sync(c.Instance(), C.GstClockTime(timeout)))
|
|
}
|
|
|
|
// String returns the string representation of this clock value.
|
|
func (c *Clock) String() string { return c.GetTime().String() }
|
|
|
|
// InternalString returns the string representation of this clock's internal value.
|
|
func (c *Clock) InternalString() string { return c.GetInternalTime().String() }
|