From ca1a83e57d3dafa5618730df7d44ca4e9f3aea08 Mon Sep 17 00:00:00 2001 From: RSWilli Date: Fri, 11 Aug 2023 14:39:32 +0200 Subject: [PATCH] change clockTime type, add more bindings --- examples/appsink/main.go | 3 +- examples/pad-probes/main.go | 3 +- examples/tagsetter/main.go | 3 +- examples/toc/main.go | 2 +- gst/app/gst_app_src.go | 13 ++-- gst/cgo_exports.go | 3 +- gst/constants.go | 9 +-- gst/control_binding.go | 30 ++++++++++ gst/control_source.go | 27 +++++++++ gst/gst.go.c | 85 ++++++++++++++------------- gst/gst.go.h | 87 ++++++++++++++------------- gst/gst_buffer.go | 43 +++++--------- gst/gst_bus.go | 28 +++------ gst/gst_clock.go | 101 +++++++++++++++++++------------- gst/gst_element.go | 36 ++++++++++++ gst/gst_element_factory.go | 43 +++++++++++++- gst/gst_message_constructors.go | 11 +--- gst/gst_object.go | 4 ++ gst/gst_pipeline.go | 9 +++ gst/gstnet/gst_ntp_clock.go | 64 ++++++++++++++++++++ gst/pkg_config.go | 2 +- 21 files changed, 400 insertions(+), 206 deletions(-) create mode 100644 gst/control_binding.go create mode 100644 gst/control_source.go create mode 100644 gst/gstnet/gst_ntp_clock.go diff --git a/examples/appsink/main.go b/examples/appsink/main.go index 555832d..a9be100 100644 --- a/examples/appsink/main.go +++ b/examples/appsink/main.go @@ -6,7 +6,6 @@ import ( "math" "os" "os/signal" - "time" "github.com/go-gst/go-gst/examples" "github.com/go-gst/go-gst/gst" @@ -116,7 +115,7 @@ func mainLoop(pipeline *gst.Pipeline) error { // Loop over messsages from the pipeline for { - msg := bus.TimedPop(time.Duration(-1)) + msg := bus.TimedPop(gst.ClockTimeNone) if msg == nil { break } diff --git a/examples/pad-probes/main.go b/examples/pad-probes/main.go index 4a40201..9c02556 100644 --- a/examples/pad-probes/main.go +++ b/examples/pad-probes/main.go @@ -13,7 +13,6 @@ import ( "errors" "fmt" "math" - "time" "github.com/go-gst/go-glib/glib" "github.com/go-gst/go-gst/examples" @@ -87,7 +86,7 @@ func padProbes(mainLoop *glib.MainLoop) error { // Block on messages coming in from the bus instead of using the main loop for { - msg := pipeline.GetPipelineBus().TimedPop(time.Duration(-1)) + msg := pipeline.GetPipelineBus().TimedPop(gst.ClockTimeNone) if msg == nil { break } diff --git a/examples/tagsetter/main.go b/examples/tagsetter/main.go index 650cb87..c103d60 100644 --- a/examples/tagsetter/main.go +++ b/examples/tagsetter/main.go @@ -23,7 +23,6 @@ package main import ( "fmt" - "time" "github.com/go-gst/go-gst/examples" "github.com/go-gst/go-gst/gst" @@ -65,7 +64,7 @@ func tagsetter() error { var cont bool var pipelineErr error for { - msg := pipeline.GetPipelineBus().TimedPop(time.Duration(-1)) + msg := pipeline.GetPipelineBus().TimedPop(gst.ClockTimeNone) if msg == nil { break } diff --git a/examples/toc/main.go b/examples/toc/main.go index 9790edf..b4b5fa4 100644 --- a/examples/toc/main.go +++ b/examples/toc/main.go @@ -87,7 +87,7 @@ func tagsetter(mainLoop *glib.MainLoop) error { // timed_pop on the bus with the desired timeout for when to stop waiting for new messages. // (-1 = Wait forever) for { - msg := pipeline.GetPipelineBus().TimedPop(time.Duration(-1)) + msg := pipeline.GetPipelineBus().TimedPop(gst.ClockTimeNone) switch msg.Type() { // When we use this method of popping from the bus (instead of a Watch), we own a diff --git a/gst/app/gst_app_src.go b/gst/app/gst_app_src.go index 6a8a11d..ef11dff 100644 --- a/gst/app/gst_app_src.go +++ b/gst/app/gst_app_src.go @@ -18,7 +18,6 @@ gboolean cgoSeekDataCb (GstAppSrc *src, guint64 offset, gpointer user_data) { */ import "C" import ( - "time" "unsafe" "github.com/go-gst/go-gst/gst" @@ -91,12 +90,10 @@ func (a *Source) GetCurrentLevelBytes() uint64 { var gstClockTimeNone C.GstClockTime = 0xffffffffffffffff // GetDuration gets the duration of the stream in nanoseconds. A negative value means that the duration is not known. -func (a *Source) GetDuration() time.Duration { +func (a *Source) GetDuration() gst.ClockTime { dur := C.gst_app_src_get_duration(a.Instance()) - if dur == gstClockTimeNone { - return gst.ClockTimeNone - } - return time.Duration(uint64(dur)) * time.Nanosecond + + return gst.ClockTime(dur) } // GetEmitSignals checks if appsrc will emit the "new-preroll" and "new-buffer" signals. @@ -189,8 +186,8 @@ func (a *Source) SetCaps(caps *gst.Caps) { // SetDuration sets the duration of the source stream. You should call // this if the value is known. -func (a *Source) SetDuration(dur time.Duration) { - C.gst_app_src_set_duration((*C.GstAppSrc)(a.Instance()), C.GstClockTime(dur.Nanoseconds())) +func (a *Source) SetDuration(dur gst.ClockTime) { + C.gst_app_src_set_duration((*C.GstAppSrc)(a.Instance()), C.GstClockTime(dur)) } // SetEmitSignals makes appsrc emit the "new-preroll" and "new-buffer" signals. This option is by default disabled because signal emission diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index 3a9243c..c9c7cbf 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -10,7 +10,6 @@ package gst import "C" import ( - "time" "unsafe" "github.com/go-gst/go-glib/glib" @@ -224,7 +223,7 @@ func goClockCb(gclock *C.GstClock, clockTime C.GstClockTime, clockID C.GstClockI } clock := wrapClock(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(gclock))}) - return gboolean(cb(clock, time.Duration(clockTime))) + return gboolean(cb(clock, ClockTime(clockTime))) } //export goPluginInit diff --git a/gst/constants.go b/gst/constants.go index a8a1d97..bc74d5d 100644 --- a/gst/constants.go +++ b/gst/constants.go @@ -38,18 +38,11 @@ const ( type ClockTimeDiff int64 // ClockTimeNone means infinite timeout or an empty value -var ClockTimeNone time.Duration = time.Duration(-1) +var ClockTimeNone ClockTime = C.GST_CLOCK_TIME_NONE // BufferOffsetNone is a var for no-offset return results. var BufferOffsetNone time.Duration = time.Duration(-1) -var ( - // ClockTimeNone means infinite timeout (unsigned representation of -1) or an otherwise unknown value. - gstClockTimeNone C.GstClockTime = 0xffffffffffffffff - // // BufferOffsetNone is a constant for no-offset return results. - // gstBufferOffsetNone C.GstClockTime = 0xffffffffffffffff -) - // ClockEntryType wraps GstClockEntryType type ClockEntryType int diff --git a/gst/control_binding.go b/gst/control_binding.go new file mode 100644 index 0000000..baaba2d --- /dev/null +++ b/gst/control_binding.go @@ -0,0 +1,30 @@ +package gst + +// #include "gst.go.h" +import "C" +import ( + "unsafe" + + "github.com/go-gst/go-glib/glib" +) + +type ControlBinding struct{ *Object } + +func (cb *ControlBinding) Instance() *C.GstControlBinding { + return C.toGstControlBinding(cb.Unsafe()) +} + +type DirectControlBinding struct{ ControlBinding } + +func NewDirectControlBinding(obj *Object, prop string, csource *InterpolationControlSource) *DirectControlBinding { + cprop := C.CString(prop) + defer C.free(unsafe.Pointer(cprop)) + + cbinding := C.gst_direct_control_binding_new(obj.Instance(), cprop, csource.Instance()) + + return &DirectControlBinding{ + ControlBinding: ControlBinding{ + Object: wrapObject(glib.TransferNone(unsafe.Pointer(cbinding))), + }, + } +} diff --git a/gst/control_source.go b/gst/control_source.go new file mode 100644 index 0000000..dac40c8 --- /dev/null +++ b/gst/control_source.go @@ -0,0 +1,27 @@ +package gst + +// #include "gst.go.h" +import "C" +import ( + "unsafe" + + "github.com/go-gst/go-glib/glib" +) + +type InterpolationControlSource struct{ *Object } + +func (cs *InterpolationControlSource) Instance() *C.GstControlSource { + return C.toGstControlSource(cs.Unsafe()) +} + +func NewInterpolationControlSource() *InterpolationControlSource { + cCs := C.gst_interpolation_control_source_new() + + return &InterpolationControlSource{ + Object: wrapObject(glib.TransferNone(unsafe.Pointer(cCs))), + } +} + +func (cs *InterpolationControlSource) SetTimedValue(time ClockTime, value float64) { + C.gst_timed_value_control_source_set(C.toGstTimedValueControlSource(cs.Unsafe()), C.ulong(time), C.double(value)) +} diff --git a/gst/gst.go.c b/gst/gst.go.c index adc0079..5ea4e27 100644 --- a/gst/gst.go.c +++ b/gst/gst.go.c @@ -1,47 +1,50 @@ #include "gst.go.h" -GType objectGType (GObject *obj) { return G_OBJECT_TYPE(obj); }; +GType objectGType (GObject *obj) { return G_OBJECT_TYPE(obj); }; -GstAllocator * toGstAllocator (void *p) { return (GST_ALLOCATOR_CAST(p)); } -GstBin * toGstBin (void *p) { return (GST_BIN(p)); } -GstBinClass * toGstBinClass (void *p) { return (GST_BIN_CLASS(p)); } -GstBufferList * toGstBufferList (void *p) { return (GST_BUFFER_LIST(p)); } -GstBufferPool * toGstBufferPool (void *p) { return (GST_BUFFER_POOL(p)); } -GstBuffer * toGstBuffer (void *p) { return (GST_BUFFER(p)); } -GstBus * toGstBus (void *p) { return (GST_BUS(p)); } -GstCapsFeatures * toGstCapsFeatures (void *p) { return (GST_CAPS_FEATURES(p)); } -GstCaps * toGstCaps (void *p) { return (GST_CAPS(p)); } -GstChildProxy * toGstChildProxy (void *p) { return (GST_CHILD_PROXY(p)); } -GstClock * toGstClock (void *p) { return (GST_CLOCK(p)); } -GstContext * toGstContext (void *p) { return (GST_CONTEXT_CAST(p)); } -GstDevice * toGstDevice (void *p) { return (GST_DEVICE_CAST(p)); } -GstElementFactory * toGstElementFactory (void *p) { return (GST_ELEMENT_FACTORY(p)); } -GstElementClass * toGstElementClass (void *p) { return (GST_ELEMENT_CLASS(p)); } -GstElement * toGstElement (void *p) { return (GST_ELEMENT(p)); } -GstEvent * toGstEvent (void *p) { return (GST_EVENT(p)); } -GstGhostPad * toGstGhostPad (void *p) { return (GST_GHOST_PAD(p)); } -GstMemory * toGstMemory (void *p) { return (GST_MEMORY_CAST(p)); } -GstMessage * toGstMessage (void *p) { return (GST_MESSAGE(p)); } -GstMeta * toGstMeta (void *p) { return (GST_META_CAST(p)); } -GstMiniObject * toGstMiniObject (void *p) { return (GST_MINI_OBJECT(p)); } -GstObject * toGstObject (void *p) { return (GST_OBJECT(p)); } -GstPad * toGstPad (void *p) { return (GST_PAD(p)); } -GstPadTemplate * toGstPadTemplate (void *p) { return (GST_PAD_TEMPLATE(p)); } -GstPipeline * toGstPipeline (void *p) { return (GST_PIPELINE(p)); } -GstPluginFeature * toGstPluginFeature (void *p) { return (GST_PLUGIN_FEATURE(p)); } -GstPlugin * toGstPlugin (void *p) { return (GST_PLUGIN(p)); } -GstProxyPad * toGstProxyPad (void *p) { return (GST_PROXY_PAD(p)); } -GstQuery * toGstQuery (void *p) { return (GST_QUERY(p)); } -GstRegistry * toGstRegistry (void *p) { return (GST_REGISTRY(p)); } -GstSample * toGstSample (void *p) { return (GST_SAMPLE(p)); } -GstStreamCollection * toGstStreamCollection (void *p) { return (GST_STREAM_COLLECTION_CAST(p)); } -GstStream * toGstStream (void *p) { return (GST_STREAM_CAST(p)); } -GstStructure * toGstStructure (void *p) { return (GST_STRUCTURE(p)); } -GstTagList * toGstTagList (void *p) { return (GST_TAG_LIST(p)); } -GstTask * toGstTask (void *p) { return (GST_TASK_CAST(p)); } -GstTaskPool * toGstTaskPool (void *p) { return (GST_TASK_POOL_CAST(p)); } -GstURIHandler * toGstURIHandler (void *p) { return (GST_URI_HANDLER(p)); } -GstUri * toGstURI (void *p) { return (GST_URI(p)); } +GstAllocator * toGstAllocator (void *p) { return (GST_ALLOCATOR_CAST(p)); } +GstBin * toGstBin (void *p) { return (GST_BIN(p)); } +GstBinClass * toGstBinClass (void *p) { return (GST_BIN_CLASS(p)); } +GstBufferList * toGstBufferList (void *p) { return (GST_BUFFER_LIST(p)); } +GstBufferPool * toGstBufferPool (void *p) { return (GST_BUFFER_POOL(p)); } +GstBuffer * toGstBuffer (void *p) { return (GST_BUFFER(p)); } +GstBus * toGstBus (void *p) { return (GST_BUS(p)); } +GstCapsFeatures * toGstCapsFeatures (void *p) { return (GST_CAPS_FEATURES(p)); } +GstCaps * toGstCaps (void *p) { return (GST_CAPS(p)); } +GstChildProxy * toGstChildProxy (void *p) { return (GST_CHILD_PROXY(p)); } +GstClock * toGstClock (void *p) { return (GST_CLOCK(p)); } +GstContext * toGstContext (void *p) { return (GST_CONTEXT_CAST(p)); } +GstDevice * toGstDevice (void *p) { return (GST_DEVICE_CAST(p)); } +GstElementFactory * toGstElementFactory (void *p) { return (GST_ELEMENT_FACTORY(p)); } +GstElementClass * toGstElementClass (void *p) { return (GST_ELEMENT_CLASS(p)); } +GstElement * toGstElement (void *p) { return (GST_ELEMENT(p)); } +GstEvent * toGstEvent (void *p) { return (GST_EVENT(p)); } +GstGhostPad * toGstGhostPad (void *p) { return (GST_GHOST_PAD(p)); } +GstMemory * toGstMemory (void *p) { return (GST_MEMORY_CAST(p)); } +GstMessage * toGstMessage (void *p) { return (GST_MESSAGE(p)); } +GstMeta * toGstMeta (void *p) { return (GST_META_CAST(p)); } +GstMiniObject * toGstMiniObject (void *p) { return (GST_MINI_OBJECT(p)); } +GstObject * toGstObject (void *p) { return (GST_OBJECT(p)); } +GstPad * toGstPad (void *p) { return (GST_PAD(p)); } +GstPadTemplate * toGstPadTemplate (void *p) { return (GST_PAD_TEMPLATE(p)); } +GstPipeline * toGstPipeline (void *p) { return (GST_PIPELINE(p)); } +GstPluginFeature * toGstPluginFeature (void *p) { return (GST_PLUGIN_FEATURE(p)); } +GstPlugin * toGstPlugin (void *p) { return (GST_PLUGIN(p)); } +GstProxyPad * toGstProxyPad (void *p) { return (GST_PROXY_PAD(p)); } +GstQuery * toGstQuery (void *p) { return (GST_QUERY(p)); } +GstRegistry * toGstRegistry (void *p) { return (GST_REGISTRY(p)); } +GstSample * toGstSample (void *p) { return (GST_SAMPLE(p)); } +GstStreamCollection * toGstStreamCollection (void *p) { return (GST_STREAM_COLLECTION_CAST(p)); } +GstStream * toGstStream (void *p) { return (GST_STREAM_CAST(p)); } +GstStructure * toGstStructure (void *p) { return (GST_STRUCTURE(p)); } +GstTagList * toGstTagList (void *p) { return (GST_TAG_LIST(p)); } +GstTask * toGstTask (void *p) { return (GST_TASK_CAST(p)); } +GstTaskPool * toGstTaskPool (void *p) { return (GST_TASK_POOL_CAST(p)); } +GstURIHandler * toGstURIHandler (void *p) { return (GST_URI_HANDLER(p)); } +GstUri * toGstURI (void *p) { return (GST_URI(p)); } +GstControlBinding * toGstControlBinding (void *p) { return (GST_CONTROL_BINDING(p)); } +GstControlSource * toGstControlSource (void *p) { return (GST_CONTROL_SOURCE(p)); } +GstTimedValueControlSource * toGstTimedValueControlSource (void *p) { return (GST_TIMED_VALUE_CONTROL_SOURCE(p)); } /* Buffer Utilities */ diff --git a/gst/gst.go.h b/gst/gst.go.h index 62e4692..292ae3d 100644 --- a/gst/gst.go.h +++ b/gst/gst.go.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include typedef struct _PadDestroyNotifyInfo { gpointer pad_ptr; @@ -14,48 +16,51 @@ typedef struct _PadDestroyNotifyInfo { Type Castings */ -extern GType objectGType (GObject *obj); +extern GType objectGType (GObject *obj); -extern GstAllocator * toGstAllocator (void *p); -extern GstBin * toGstBin (void *p); -extern GstBinClass * toGstBinClass (void *p); -extern GstBufferList * toGstBufferList (void *p); -extern GstBufferPool * toGstBufferPool (void *p); -extern GstBuffer * toGstBuffer (void *p); -extern GstBus * toGstBus (void *p); -extern GstCapsFeatures * toGstCapsFeatures (void *p); -extern GstCaps * toGstCaps (void *p); -extern GstChildProxy * toGstChildProxy (void *p); -extern GstClock * toGstClock (void *p); -extern GstContext * toGstContext (void *p); -extern GstDevice * toGstDevice (void *p); -extern GstElementFactory * toGstElementFactory (void *p); -extern GstElementClass * toGstElementClass (void *p); -extern GstElement * toGstElement (void *p); -extern GstEvent * toGstEvent (void *p); -extern GstGhostPad * toGstGhostPad (void *p); -extern GstMemory * toGstMemory (void *p); -extern GstMessage * toGstMessage (void *p); -extern GstMeta * toGstMeta (void *p); -extern GstMiniObject * toGstMiniObject (void *p); -extern GstObject * toGstObject (void *p); -extern GstPad * toGstPad (void *p); -extern GstPadTemplate * toGstPadTemplate (void *p); -extern GstPipeline * toGstPipeline (void *p); -extern GstPluginFeature * toGstPluginFeature (void *p); -extern GstPlugin * toGstPlugin (void *p); -extern GstProxyPad * toGstProxyPad (void *p); -extern GstQuery * toGstQuery (void *p); -extern GstRegistry * toGstRegistry (void *p); -extern GstSample * toGstSample (void *p); -extern GstStreamCollection * toGstStreamCollection (void *p); -extern GstStream * toGstStream (void *p); -extern GstStructure * toGstStructure (void *p); -extern GstTagList * toGstTagList (void *p); -extern GstTask * toGstTask (void *p); -extern GstTaskPool * toGstTaskPool (void *p); -extern GstURIHandler * toGstURIHandler (void *p); -extern GstUri * toGstURI (void *p); +extern GstAllocator * toGstAllocator (void *p); +extern GstBin * toGstBin (void *p); +extern GstBinClass * toGstBinClass (void *p); +extern GstBufferList * toGstBufferList (void *p); +extern GstBufferPool * toGstBufferPool (void *p); +extern GstBuffer * toGstBuffer (void *p); +extern GstBus * toGstBus (void *p); +extern GstCapsFeatures * toGstCapsFeatures (void *p); +extern GstCaps * toGstCaps (void *p); +extern GstChildProxy * toGstChildProxy (void *p); +extern GstClock * toGstClock (void *p); +extern GstContext * toGstContext (void *p); +extern GstDevice * toGstDevice (void *p); +extern GstElementFactory * toGstElementFactory (void *p); +extern GstElementClass * toGstElementClass (void *p); +extern GstElement * toGstElement (void *p); +extern GstEvent * toGstEvent (void *p); +extern GstGhostPad * toGstGhostPad (void *p); +extern GstMemory * toGstMemory (void *p); +extern GstMessage * toGstMessage (void *p); +extern GstMeta * toGstMeta (void *p); +extern GstMiniObject * toGstMiniObject (void *p); +extern GstObject * toGstObject (void *p); +extern GstPad * toGstPad (void *p); +extern GstPadTemplate * toGstPadTemplate (void *p); +extern GstPipeline * toGstPipeline (void *p); +extern GstPluginFeature * toGstPluginFeature (void *p); +extern GstPlugin * toGstPlugin (void *p); +extern GstProxyPad * toGstProxyPad (void *p); +extern GstQuery * toGstQuery (void *p); +extern GstRegistry * toGstRegistry (void *p); +extern GstSample * toGstSample (void *p); +extern GstStreamCollection * toGstStreamCollection (void *p); +extern GstStream * toGstStream (void *p); +extern GstStructure * toGstStructure (void *p); +extern GstTagList * toGstTagList (void *p); +extern GstTask * toGstTask (void *p); +extern GstTaskPool * toGstTaskPool (void *p); +extern GstURIHandler * toGstURIHandler (void *p); +extern GstUri * toGstURI (void *p); +extern GstControlBinding * toGstControlBinding (void *p); +extern GstControlSource * toGstControlSource (void *p); +extern GstTimedValueControlSource * toGstTimedValueControlSource (void *p); /* Buffer Utilities */ diff --git a/gst/gst_buffer.go b/gst/gst_buffer.go index 4b2c2f6..3c3a899 100644 --- a/gst/gst_buffer.go +++ b/gst/gst_buffer.go @@ -176,12 +176,10 @@ func (b *Buffer) Bytes() []byte { // PresentationTimestamp returns the presentation timestamp of the buffer, or a negative duration // if not known or relevant. This value contains the timestamp when the media should be // presented to the user. -func (b *Buffer) PresentationTimestamp() time.Duration { +func (b *Buffer) PresentationTimestamp() ClockTime { pts := b.Instance().pts - if pts == gstClockTimeNone { - return ClockTimeNone - } - return time.Duration(pts) + + return ClockTime(pts) } // SetPresentationTimestamp sets the presentation timestamp on the buffer. @@ -192,22 +190,18 @@ func (b *Buffer) SetPresentationTimestamp(dur time.Duration) { // DecodingTimestamp returns the decoding timestamp of the buffer, or a negative duration if not known // or relevant. This value contains the timestamp when the media should be processed. -func (b *Buffer) DecodingTimestamp() time.Duration { +func (b *Buffer) DecodingTimestamp() ClockTime { dts := b.Instance().dts - if dts == gstClockTimeNone { - return ClockTimeNone - } - return time.Duration(dts) + + return ClockTime(dts) } // Duration returns the length of the data inside this buffer, or a negative duration if not known // or relevant. -func (b *Buffer) Duration() time.Duration { +func (b *Buffer) Duration() ClockTime { dur := b.Instance().duration - if dur == gstClockTimeNone { - return ClockTimeNone - } - return time.Duration(dur) + + return ClockTime(dur) } // SetDuration sets the duration on the buffer. @@ -287,7 +281,7 @@ func (b *Buffer) AddProtectionMeta(info *Structure) *ProtectionMeta { type ReferenceTimestampMeta struct { Parent *Meta Reference *Caps - Timestamp, Duration time.Duration + Timestamp, Duration ClockTime } // AddReferenceTimestampMeta adds a ReferenceTimestampMeta to this buffer that holds a @@ -295,21 +289,16 @@ type ReferenceTimestampMeta struct { // // See the documentation of GstReferenceTimestampMeta for details. // https://gstreamer.freedesktop.org/documentation/gstreamer/gstbuffer.html?gi-language=c#GstReferenceTimestampMeta -func (b *Buffer) AddReferenceTimestampMeta(ref *Caps, timestamp, duration time.Duration) *ReferenceTimestampMeta { - durClockTime := C.GstClockTime(ClockTimeNone) - if duration > time.Duration(0) { - durClockTime = C.GstClockTime(duration.Nanoseconds()) - } - tsClockTime := C.GstClockTime(timestamp.Nanoseconds()) - meta := C.gst_buffer_add_reference_timestamp_meta(b.Instance(), ref.Instance(), tsClockTime, durClockTime) +func (b *Buffer) AddReferenceTimestampMeta(ref *Caps, timestamp, duration ClockTime) *ReferenceTimestampMeta { + meta := C.gst_buffer_add_reference_timestamp_meta(b.Instance(), ref.Instance(), C.GstClockTime(timestamp), C.GstClockTime(duration)) if meta == nil { return nil } return &ReferenceTimestampMeta{ Parent: wrapMeta(&meta.parent), Reference: wrapCaps(meta.reference), - Timestamp: time.Duration(meta.timestamp), - Duration: time.Duration(meta.duration), + Timestamp: ClockTime(meta.timestamp), + Duration: ClockTime(meta.duration), } } @@ -502,8 +491,8 @@ func (b *Buffer) GetReferenceTimestampMeta(caps *Caps) *ReferenceTimestampMeta { } refMeta := &ReferenceTimestampMeta{ Parent: wrapMeta(&meta.parent), - Timestamp: time.Duration(meta.timestamp), - Duration: time.Duration(meta.duration), + Timestamp: ClockTime(meta.timestamp), + Duration: ClockTime(meta.duration), } if meta.reference != nil { refMeta.Reference = wrapCaps(meta.reference) diff --git a/gst/gst_bus.go b/gst/gst_bus.go index 06aa9c8..13ff2bc 100644 --- a/gst/gst_bus.go +++ b/gst/gst_bus.go @@ -97,8 +97,8 @@ func (b *Bus) AddSignalWatch() { C.gst_bus_add_signal_watch(b.Instance()) } // // It is much safer and easier to use the AddWatch or other polling functions. Only use this method if you // are unable to also run a MainLoop, or for convenience sake. -func (b *Bus) PopMessage(timeout int) *Message { - return b.TimedPop(time.Duration(timeout) * time.Second) +func (b *Bus) PopMessage(timeout ClockTime) *Message { + return b.TimedPop(timeout) } // BlockPopMessage blocks until a message is available on the bus and then returns it. @@ -315,15 +315,9 @@ func (b *Bus) SetSyncHandler(f BusSyncHandler) { // TimedPop gets a message from the bus, waiting up to the specified timeout. Unref returned messages after usage. // -// If timeout is 0, this function behaves like Pop. If timeout is < 0, this function will block forever until a message was posted on the bus. -func (b *Bus) TimedPop(dur time.Duration) *Message { - var cTime C.GstClockTime - if dur == ClockTimeNone { - cTime = C.GstClockTime(gstClockTimeNone) - } else { - cTime = C.GstClockTime(dur.Nanoseconds()) - } - msg := C.gst_bus_timed_pop(b.Instance(), cTime) +// If timeout is 0, this function behaves like Pop. If timeout is ClockTimeNone, this function will block forever until a message was posted on the bus. +func (b *Bus) TimedPop(dur ClockTime) *Message { + msg := C.gst_bus_timed_pop(b.Instance(), C.GstClockTime(dur)) if msg == nil { return nil } @@ -333,16 +327,10 @@ func (b *Bus) TimedPop(dur time.Duration) *Message { // TimedPopFiltered gets a message from the bus whose type matches the message type mask types, waiting up to the specified timeout // (and discarding any messages that do not match the mask provided). // -// If timeout is 0, this function behaves like PopFiltered. If timeout is < 0, this function will block forever until a matching message +// If timeout is 0, this function behaves like PopFiltered. If timeout is ClockTimeNone, this function will block forever until a matching message // was posted on the bus. -func (b *Bus) TimedPopFiltered(dur time.Duration, msgTypes MessageType) *Message { - var cTime C.GstClockTime - if dur == ClockTimeNone { - cTime = C.GstClockTime(gstClockTimeNone) - } else { - cTime = C.GstClockTime(dur.Nanoseconds()) - } - msg := C.gst_bus_timed_pop_filtered(b.Instance(), cTime, C.GstMessageType(msgTypes)) +func (b *Bus) TimedPopFiltered(dur ClockTime, msgTypes MessageType) *Message { + msg := C.gst_bus_timed_pop_filtered(b.Instance(), C.GstClockTime(dur), C.GstMessageType(msgTypes)) if msg == nil { return nil } diff --git a/gst/gst_clock.go b/gst/gst_clock.go index fc17f3d..9646e04 100644 --- a/gst/gst_clock.go +++ b/gst/gst_clock.go @@ -20,6 +20,7 @@ void clockDestroyNotify (gpointer user_data) import "C" import ( + "fmt" "runtime" "time" "unsafe" @@ -28,8 +29,28 @@ import ( gopointer "github.com/mattn/go-pointer" ) +type ClockTime C.GstClockTime + +func (ct ClockTime) String() string { + if ct == ClockTimeNone { + return "ClockTimeNone" + } + + return fmt.Sprint(uint64(ct)) +} + +func (ct ClockTime) AsDuration() *time.Duration { + if ct == ClockTimeNone { + return nil + } + + dur := time.Duration(uint64(ct)) + + return &dur +} + // ClockCallback is the prototype of a clock callback function. -type ClockCallback func(clock *Clock, clockTime time.Duration) bool +type ClockCallback func(clock *Clock, clockTime ClockTime) bool // ClockID is a go wrapper around a GstClockID. type ClockID struct { @@ -46,8 +67,8 @@ func (c *ClockID) GetClock() *Clock { } // GetTime returns the time for this ClockID -func (c *ClockID) GetTime() time.Duration { - return time.Duration(C.gst_clock_id_get_time(c.Instance())) +func (c *ClockID) GetTime() ClockTime { + return ClockTime(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. @@ -89,7 +110,7 @@ func (c *ClockID) Wait() (ret ClockReturn, jitter ClockTimeDiff) { // // id := clock.NewSingleShotID(gst.ClockTime(1000000000)) // 1 second // -// id.WaitAsync(func(clock *gst.Clock, clockTime time.Duration) bool { +// id.WaitAsync(func(clock *gst.Clock, clockTime ClockTime) bool { // fmt.Println("Single shot triggered at", clockTime.Nanoseconds()) // pipeline.SetState(gst.StateNull) // return true @@ -143,7 +164,7 @@ func (c *Clock) Instance() *C.GstClock { return C.toGstClock(c.Unsafe()) } // // 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) { +func (c *Clock) AddObservation(slaveTime, masterTime ClockTime) (bool, float64) { var out C.gdouble ok := gobool(C.gst_clock_add_observation( c.Instance(), @@ -158,7 +179,7 @@ func (c *Clock) AddObservation(slaveTime, masterTime time.Duration) (bool, float // 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) { +func (c *Clock) AddObservationUnapplied(slaveTime, masterTime ClockTime) (ok bool, rSquared float64, internalTime, externalTime, rateNum, rateDenom ClockTime) { var ginternal, gexternal, grateNum, grateDenom C.GstClockTime var grSquared C.gdouble ok = gobool(C.gst_clock_add_observation_unapplied( @@ -167,7 +188,7 @@ func (c *Clock) AddObservationUnapplied(slaveTime, masterTime time.Duration) (ok C.GstClockTime(masterTime), &grSquared, &ginternal, &gexternal, &grateNum, &grateDenom, )) - return ok, float64(grSquared), time.Duration(ginternal), time.Duration(gexternal), time.Duration(grateNum), time.Duration(grateDenom) + return ok, float64(grSquared), ClockTime(ginternal), ClockTime(gexternal), ClockTime(grateNum), ClockTime(grateDenom) } // AdjustUnlocked converts the given internal clock time to the external time, adjusting for the rate and reference time set with @@ -175,8 +196,8 @@ func (c *Clock) AddObservationUnapplied(slaveTime, masterTime time.Duration) (ok // 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))) +func (c *Clock) AdjustUnlocked(internal ClockTime) ClockTime { + return ClockTime(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. @@ -184,8 +205,8 @@ func (c *Clock) AdjustUnlocked(internal time.Duration) time.Duration { // 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( +func (c *Clock) AdjustWithCalibration(internalTarget, cinternal, cexternal, cnum, cdenom ClockTime) ClockTime { + return ClockTime(C.gst_clock_adjust_with_calibration( c.Instance(), C.GstClockTime(internalTarget), C.GstClockTime(cinternal), @@ -196,30 +217,26 @@ func (c *Clock) AdjustWithCalibration(internalTarget, cinternal, cexternal, cnum } // 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) { +func (c *Clock) GetCalibration() (internal, external, rateNum, rateDenom ClockTime) { 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) + return ClockTime(ginternal), ClockTime(gexternal), ClockTime(grateNum), ClockTime(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 { +func (c *Clock) GetTime() ClockTime { res := C.gst_clock_get_time(c.Instance()) - if res == gstClockTimeNone { - return ClockTimeNone - } - return time.Duration(res) + + return ClockTime(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 { +func (c *Clock) GetInternalTime() ClockTime { res := C.gst_clock_get_internal_time(c.Instance()) - if res == gstClockTimeNone { - return ClockTimeNone - } - return time.Duration(res) + + return ClockTime(res) } // GetMaster returns the master clock that this clock is slaved to or nil when the clock @@ -234,13 +251,13 @@ func (c *Clock) GetMaster() *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())) +func (c *Clock) GetResolution() ClockTime { + return ClockTime(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())) +func (c *Clock) GetTimeout() ClockTime { + return ClockTime(C.gst_clock_get_timeout(c.Instance())) } // IsSynced returns true if the clock is synced. @@ -248,7 +265,7 @@ 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 { +func (c *Clock) NewPeriodicID(startTime, interval ClockTime) *ClockID { id := C.gst_clock_new_periodic_id( c.Instance(), C.GstClockTime(startTime), @@ -261,7 +278,7 @@ func (c *Clock) NewPeriodicID(startTime, interval time.Duration) *ClockID { // 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 { +func (c *Clock) NewSingleShotID(at ClockTime) *ClockID { id := C.gst_clock_new_single_shot_id( c.Instance(), C.GstClockTime(at), @@ -273,7 +290,7 @@ func (c *Clock) NewSingleShotID(at time.Duration) *ClockID { // 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 { +func (c *Clock) PeriodicIDReinit(clockID *ClockID, startTime, interval ClockTime) bool { return gobool(C.gst_clock_periodic_id_reinit( c.Instance(), clockID.Instance(), @@ -284,7 +301,7 @@ func (c *Clock) PeriodicIDReinit(clockID *ClockID, startTime, interval time.Dura // 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) { +func (c *Clock) SetCalibration(internal, external, rateNum, rateDenom ClockTime) { C.gst_clock_set_calibration( c.Instance(), C.GstClockTime(internal), @@ -311,8 +328,8 @@ func (c *Clock) SetMaster(master *Clock) bool { // 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))) +func (c *Clock) SetResolution(resolution ClockTime) ClockTime { + return ClockTime(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. @@ -322,12 +339,12 @@ func (c *Clock) SetResolution(resolution time.Duration) time.Duration { 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) { +func (c *Clock) SetTimeout(timeout ClockTime) { 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 { +func (c *Clock) SingleShotIDReinit(clockID *ClockID, at ClockTime) bool { return gobool(C.gst_clock_single_shot_id_reinit(c.Instance(), clockID.Instance(), C.GstClockTime(at))) } @@ -335,14 +352,14 @@ func (c *Clock) SingleShotIDReinit(clockID *ClockID, at time.Duration) bool { // 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))) +func (c *Clock) UnadjustUnlocked(external ClockTime) ClockTime { + return ClockTime(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( +func (c *Clock) UnadjustWithCalibration(externalTarget, cinternal, cexternal, cnum, cdenom ClockTime) ClockTime { + return ClockTime(C.gst_clock_unadjust_with_calibration( c.Instance(), C.GstClockTime(externalTarget), C.GstClockTime(cinternal), @@ -358,12 +375,12 @@ func (c *Clock) UnadjustWithCalibration(externalTarget, cinternal, cexternal, cn // 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 { +func (c *Clock) WaitForSync(timeout ClockTime) 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() } +func (c *Clock) String() string { return fmt.Sprintf("%d", c.GetTime()) } // InternalString returns the string representation of this clock's internal value. -func (c *Clock) InternalString() string { return c.GetInternalTime().String() } +func (c *Clock) InternalString() string { return fmt.Sprintf("%d", c.GetInternalTime()) } diff --git a/gst/gst_element.go b/gst/gst_element.go index b192ccb..6d1cae8 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -520,3 +520,39 @@ func (e *Element) GetRequestPad(name string) *Pad { func (e *Element) ReleaseRequestPad(pad *Pad) { C.gst_element_release_request_pad(e.Instance(), pad.Instance()) } + +// Set the start time of an element. The start time of the element is the running time of the element +// when it last went to the PAUSED state. In READY or after a flushing seek, it is set to 0. +// +// Toplevel elements like GstPipeline will manage the start_time and base_time on its children. +// Setting the start_time to GST_CLOCK_TIME_NONE on such a toplevel element will disable the distribution of the base_time +// to the children and can be useful if the application manages the base_time itself, for example if you want to synchronize +// capture from multiple pipelines, and you can also ensure that the pipelines have the same clock. +// +// MT safe. +func (e *Element) SetStartTime(startTime ClockTime) { + C.gst_element_set_start_time(e.Instance(), C.GstClockTime(startTime)) +} + +// Returns the start time of the element. The start time is the running time of the clock when this element was last put to PAUSED. +// Usually the start_time is managed by a toplevel element such as GstPipeline. +// MT safe. +func (e *Element) GetStartTime() ClockTime { + ctime := C.gst_element_get_start_time(e.Instance()) + + return ClockTime(ctime) +} + +// Set the base time of an element. The base time is the absolute time of the clock +// when this element was last put to PLAYING. Subtracting the base time from the clock time gives the running time of the element. +func (e *Element) SetBaseTime(startTime ClockTime) { + C.gst_element_set_base_time(e.Instance(), C.GstClockTime(startTime)) +} + +// Returns the base time of the element. The base time is the absolute time of the clock +// when this element was last put to PLAYING. Subtracting the base time from the clock time gives the running time of the element. +func (e *Element) GetBaseTime() ClockTime { + ctime := C.gst_element_get_base_time(e.Instance()) + + return ClockTime(ctime) +} diff --git a/gst/gst_element_factory.go b/gst/gst_element_factory.go index e5fa713..32cb506 100644 --- a/gst/gst_element_factory.go +++ b/gst/gst_element_factory.go @@ -28,11 +28,52 @@ func NewElementWithName(factory string, name string) (*Element, error) { elem = C.gst_element_factory_make((*C.gchar)(elemName), (*C.gchar)(cname)) } if elem == nil { - return nil, fmt.Errorf("Could not create element: %s", factory) + return nil, fmt.Errorf("could not create element: %s", factory) } return wrapElement(glib.TransferNone(unsafe.Pointer(elem))), nil } +// Create a new element of the type defined by the given elementfactory. The supplied list of properties, will be passed at object construction. +// +// using this results in a free() panic :( DO NOT USE +func NewElementWithProperties(factory string, properties map[string]interface{}) (*Element, error) { + props := make([]*C.char, 0) + values := make([]C.GValue, 0) + + for p, v := range properties { + cpropName := C.CString(p) + defer C.free(unsafe.Pointer(cpropName)) + + props = append(props, cpropName) + + cValue, err := glib.GValue(v) + + if err != nil { + return nil, err + } + + values = append(values, *(*C.GValue)(cValue.Unsafe())) + } + + cfactory := C.CString(factory) + defer C.free(unsafe.Pointer(cfactory)) + + n := C.uint(len(properties)) + + var elem *C.GstElement + + if n > 0 { + elem = C.gst_element_factory_make_with_properties(cfactory, n, &props[0], &values[0]) + } else { + elem = C.gst_element_factory_make_with_properties(cfactory, n, nil, nil) + } + + if elem == nil { + return nil, fmt.Errorf("could not create element: %s", factory) + } + return wrapElement(glib.TransferFull(unsafe.Pointer(elem))), nil +} + // NewElementMany is a convenience wrapper around building many GstElements in a // single function call. It returns an error if the creation of any element fails. A // slice in the order the names were given is returned. diff --git a/gst/gst_message_constructors.go b/gst/gst_message_constructors.go index 5d6508c..86cc4e4 100644 --- a/gst/gst_message_constructors.go +++ b/gst/gst_message_constructors.go @@ -34,20 +34,15 @@ func NewApplicationMessage(src interface{}, structure *Structure) *Message { // RunningTime contains the time of the desired running time when this elements goes to PLAYING. // A value less than 0 for runningTime means that the element has no clock interaction and thus doesn't // care about the running time of the pipeline. -func NewAsyncDoneMessage(src interface{}, runningTime time.Duration) *Message { +func NewAsyncDoneMessage(src interface{}, runningTime ClockTime) *Message { srcObj := getMessageSourceObj(src) if srcObj == nil { return nil } - var cTime C.GstClockTime - if runningTime.Nanoseconds() < 0 { - cTime = C.GstClockTime(gstClockTimeNone) - } else { - cTime = C.GstClockTime(runningTime.Nanoseconds()) - } + return FromGstMessageUnsafeFull(unsafe.Pointer(C.gst_message_new_async_done( srcObj, - cTime, + C.GstClockTime(runningTime), ))) } diff --git a/gst/gst_object.go b/gst/gst_object.go index 54098e1..afcb566 100644 --- a/gst/gst_object.go +++ b/gst/gst_object.go @@ -93,3 +93,7 @@ func (o *Object) Ref() *Object { func (o *Object) Unref() { C.gst_object_unref((C.gpointer)(o.Unsafe())) } + +func (o *Object) AddControlBinding(binding *ControlBinding) { + C.gst_object_add_control_binding(o.Instance(), binding.Instance()) +} diff --git a/gst/gst_pipeline.go b/gst/gst_pipeline.go index 2e7c8a3..22ff6b8 100644 --- a/gst/gst_pipeline.go +++ b/gst/gst_pipeline.go @@ -78,6 +78,15 @@ func (p *Pipeline) GetPipelineClock() *Clock { return FromGstClockUnsafeFull(unsafe.Pointer(cClock)) } +// Force pipeline to use the given clock. The pipeline will always use the given clock even if new clock providers are added to this pipeline. +// +// If clock is NULL all clocking will be disabled which will make the pipeline run as fast as possible. +// +// MT safe. +func (p *Pipeline) ForceClock(clock *Clock) { + C.gst_pipeline_use_clock(p.Instance(), clock.Instance()) +} + /* SetAutoFlushBus can be used to disable automatically flushing the message bus when a pipeline goes to StateNull. diff --git a/gst/gstnet/gst_ntp_clock.go b/gst/gstnet/gst_ntp_clock.go new file mode 100644 index 0000000..cc7716f --- /dev/null +++ b/gst/gstnet/gst_ntp_clock.go @@ -0,0 +1,64 @@ +package gstnet + +// #include "gst.go.h" +import "C" +import ( + "context" + "errors" + "time" + "unsafe" + + "github.com/go-gst/go-gst/gst" +) + +// NTP timestamps are realtive to 1. Jan 1900, so we need an offset for 70 Years to be Unix TS compatible +const NTPTimeToUnixEpoch = gst.ClockTime(2208988800 * time.Second) + +// NTPClock wraps GstClock +type NTPClock struct{ *gst.Clock } + +// ObtainNTPClock returns the default NTPClock. The refcount of the clock will be +// increased so you need to unref the clock after usage. +func ObtainNTPClock(ctx context.Context, name, address string, port int) (*NTPClock, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + caddr := C.CString(address) + defer C.free(unsafe.Pointer(caddr)) + + currentSystemTime := time.Now().UnixNano() + int64(NTPTimeToUnixEpoch) + + ntpC := &NTPClock{gst.FromGstClockUnsafeFull(unsafe.Pointer(C.gst_ntp_clock_new(cname, caddr, C.gint(port), C.GstClockTime(currentSystemTime))))} + + for { + select { + case <-ctx.Done(): + return nil, errors.New("timeout reached while trying to sync") + default: + if ntpC.IsSynced() { + return ntpC, nil + } + time.Sleep(100 * time.Millisecond) + } + } +} + +// get the current time of the clock +// +// if you want to access the clocks actual time value, use the underlying NTPClock.Clock.GetTime() instead +func (ntp *NTPClock) GetTime() time.Time { + // nanos since 1. Jan 1900 + ntpNanos := NTPClockTime(ntp.Clock.GetTime()) + + return ntpNanos.ToDate() +} + +type NTPClockTime gst.ClockTime + +func (ct NTPClockTime) ToDate() time.Time { + return time.Unix(0, int64(ct)-int64(NTPTimeToUnixEpoch)) +} + +func NewNTPClockTime(t time.Time) NTPClockTime { + return NTPClockTime(t.UnixNano() + int64(NTPTimeToUnixEpoch)) +} diff --git a/gst/pkg_config.go b/gst/pkg_config.go index 61550f6..4ffb2d6 100644 --- a/gst/pkg_config.go +++ b/gst/pkg_config.go @@ -1,7 +1,7 @@ package gst /* -#cgo pkg-config: gstreamer-1.0 +#cgo pkg-config: gstreamer-1.0 gstreamer-controller-1.0 #cgo CFLAGS: -Wno-deprecated-declarations -Wno-format-security -g -Wall -Wno-unused-variable #cgo LDFLAGS: -lm */