diff --git a/.gitignore b/.gitignore index 5934e37..dbe8669 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ dist/ _bin/ .devcontainer/ _rust/ -*.DS_Store* \ No newline at end of file +*.DS_Store* +*.supp \ No newline at end of file diff --git a/examples/plugins/filesrc/filesrc.go b/examples/plugins/filesrc/filesrc.go index 53cf25e..cf2d095 100644 --- a/examples/plugins/filesrc/filesrc.go +++ b/examples/plugins/filesrc/filesrc.go @@ -8,7 +8,7 @@ // functionality to implement these plugins. The example in this code produces an element // that can read from a file on the local system. // -// In order to build the plugin for use by GStreamer, you may do the following: +// In order to build the plugin for use by GStreamer, you can do the following: // // $ go build -o libgstgofilesrc.so -buildmode c-shared . // @@ -21,14 +21,15 @@ import ( "io" "os" "strings" - "syscall" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" "github.com/tinyzimmer/go-gst/gst" "github.com/tinyzimmer/go-gst/gst/base" ) -// CAT is the log category for the gofilesrc +// CAT is the log category for the gofilesrc. It is safe to define GStreamer objects as globals +// without calling gst.Init, since in the context of a loaded plugin all initialization has +// already been taken care of by the loading application. var CAT = gst.NewDebugCategory( "gofilesrc", gst.DebugColorNone, @@ -77,6 +78,11 @@ type fileSrc struct { state *state } +func (f *fileSrc) PostMessage(self *gst.Element, msg *gst.Message) bool { + fmt.Println("Received message on the pipeline", msg) + return self.ParentPostMessage(msg) +} + // Private methods only used internally by the plugin // setLocation is a simple method to check the validity of a provided file path and set the @@ -97,7 +103,7 @@ func (f *fileSrc) setLocation(path string) error { // gst.GoElement implementation. Here we simply create a new fileSrc with zeroed settings // and state objects. func (f *fileSrc) New() gst.GoElement { - CAT.Log(gst.LevelDebug, "Initializing new fileSrc object") + CAT.Log(gst.LevelLog, "Initializing new fileSrc object") return &fileSrc{ settings: &settings{}, state: &state{}, @@ -107,29 +113,27 @@ func (f *fileSrc) New() gst.GoElement { // The TypeInit method should register any additional interfaces provided by the element. // In this example we signal to the type system that we also implement the GstURIHandler interface. func (f *fileSrc) TypeInit(instance *gst.TypeInstance) { - CAT.Log(gst.LevelDebug, "Adding URIHandler interface to type") + CAT.Log(gst.LevelLog, "Adding URIHandler interface to type") instance.AddInterface(gst.InterfaceURIHandler) } // The ClassInit method should specify the metadata for this element and add any pad templates // and properties. func (f *fileSrc) ClassInit(klass *gst.ElementClass) { - CAT.Log(gst.LevelDebug, "Initializing gofilesrc class") + CAT.Log(gst.LevelLog, "Initializing gofilesrc class") klass.SetMetadata( "File Source", "Source/File", "Read stream from a file", "Avi Zimmerman ", ) - caps := gst.NewAnyCaps() - srcPadTemplate := gst.NewPadTemplate( + CAT.Log(gst.LevelLog, "Adding src pad template and properties to class") + klass.AddPadTemplate(gst.NewPadTemplate( "src", gst.PadDirectionSource, gst.PadPresenceAlways, - caps, - ) - CAT.Log(gst.LevelDebug, "Adding src pad template and properties to class") - klass.AddPadTemplate(srcPadTemplate) + gst.NewAnyCaps(), + )) klass.InstallProperties(properties) } @@ -189,7 +193,7 @@ func (f *fileSrc) GetProperty(self *gst.Object, id uint) *glib.Value { // during the initialization process can be performed here. In this example, we set the format on our // underlying GstBaseSrc to bytes. func (f *fileSrc) Constructed(self *gst.Object) { - self.Log(CAT, gst.LevelDebug, "Setting format of GstBaseSrc to bytes") + self.Log(CAT, gst.LevelLog, "Setting format of GstBaseSrc to bytes") base.ToGstBaseSrc(self).SetFormat(gst.FormatBytes) } @@ -211,7 +215,7 @@ func (f *fileSrc) GetSize(self *base.GstBaseSrc) (bool, int64) { // and any error encountered in the process is posted to the pipeline. func (f *fileSrc) Start(self *base.GstBaseSrc) bool { if f.state.started { - self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "FileSrc is already started", "") + self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "GoFileSrc is already started", "") return false } @@ -240,7 +244,7 @@ func (f *fileSrc) Start(self *base.GstBaseSrc) bool { self.Log(CAT, gst.LevelDebug, fmt.Sprintf("file stat - name: %s size: %d mode: %v modtime: %v", stat.Name(), stat.Size(), stat.Mode(), stat.ModTime())) self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Opening file %s for reading", f.settings.location)) - f.state.file, err = os.OpenFile(f.settings.location, syscall.O_RDONLY, 0444) + f.state.file, err = os.Open(f.settings.location) if err != nil { self.ErrorMessage(gst.DomainResource, gst.ResourceErrorOpenRead, fmt.Sprintf("Could not open file %s for reading", f.settings.location), err.Error()) @@ -285,7 +289,7 @@ func (f *fileSrc) Fill(self *base.GstBaseSrc, offset uint64, size uint, buffer * return gst.FlowError } - self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Request to fill buffer from offset %v with size %v", offset, size)) + self.Log(CAT, gst.LevelLog, fmt.Sprintf("Request to fill buffer from offset %v with size %v", offset, size)) if f.state.position != offset { self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Seeking to new position at offset %v from previous position at offset %v", offset, f.state.position)) @@ -297,18 +301,6 @@ func (f *fileSrc) Fill(self *base.GstBaseSrc, offset uint64, size uint, buffer * f.state.position = offset } - self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Reading %v bytes from file at offset %v", size, f.state.position)) - out := make([]byte, int(size)) - if _, err := f.state.file.Read(out); err != nil && err != io.EOF { - self.ErrorMessage(gst.DomainResource, gst.ResourceErrorRead, - fmt.Sprintf("Failed to read %d bytes from file at %d", size, offset), err.Error()) - return gst.FlowError - } - - f.state.position = f.state.position + uint64(size) - self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Incremented current position to %v", f.state.position)) - - self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Writing data from file to buffer")) bufmap := buffer.Map(gst.MapWrite) if bufmap == nil { self.ErrorMessage(gst.DomainLibrary, gst.LibraryErrorFailed, "Failed to map buffer", "") @@ -316,9 +308,17 @@ func (f *fileSrc) Fill(self *base.GstBaseSrc, offset uint64, size uint, buffer * } defer buffer.Unmap() - bufmap.WriteData(out) + self.Log(CAT, gst.LevelLog, fmt.Sprintf("Reading %v bytes from offset %v in file into buffer at %v", size, f.state.position, bufmap.Data())) + if _, err := io.CopyN(bufmap.Writer(), f.state.file, int64(size)); err != nil { + self.ErrorMessage(gst.DomainResource, gst.ResourceErrorRead, + fmt.Sprintf("Failed to read %d bytes from file at %d into buffer", size, offset), err.Error()) + return gst.FlowError + } buffer.SetSize(int64(size)) + f.state.position = f.state.position + uint64(size) + self.Log(CAT, gst.LevelLog, fmt.Sprintf("Incremented current position to %v", f.state.position)) + return gst.FlowOK } diff --git a/go.mod b/go.mod index c9d9453..ca91355 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/tinyzimmer/go-gst go 1.15 require ( - github.com/gotk3/gotk3 v0.4.0 github.com/mattn/go-pointer v0.0.1 + github.com/tinyzimmer/go-glib v0.0.2 ) diff --git a/go.sum b/go.sum index dff2a49..4d56c96 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ -github.com/gotk3/gotk3 v0.4.0 h1:TIuhyQitGeRTxOQIV3AJlYtEWWJpC74JHwAIsxlH8MU= -github.com/gotk3/gotk3 v0.4.0/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo= -github.com/gotk3/gotk3 v0.5.2 h1:jbSFvUNMfo3ImM6BWBAkNUxY5piqP3eTc1YFbYy9ecU= github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= +github.com/tinyzimmer/go-glib v0.0.1 h1:pfsr7EbP23/cZPGlEpsmwtQXjdzYj0S+WhUgOALdsQI= +github.com/tinyzimmer/go-glib v0.0.1/go.mod h1:D5rd0CvYn1p7TBhwlwnBXHSr4d8lwEY9JImPQ66S+Bs= +github.com/tinyzimmer/go-glib v0.0.2 h1:hdvjwrhcS6WrMMeqfxsf3e7/lOhV8gbVyJ9/sN3LfyY= +github.com/tinyzimmer/go-glib v0.0.2/go.mod h1:D5rd0CvYn1p7TBhwlwnBXHSr4d8lwEY9JImPQ66S+Bs= diff --git a/gst/app/gst_app_src.go b/gst/app/gst_app_src.go index 4c47634..1e812f8 100644 --- a/gst/app/gst_app_src.go +++ b/gst/app/gst_app_src.go @@ -87,11 +87,13 @@ func (a *Source) GetCurrentLevelBytes() uint64 { return uint64(C.gst_app_src_get_current_level_bytes(a.Instance())) } +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 { dur := C.gst_app_src_get_duration(a.Instance()) - if gst.ClockTime(dur) == gst.ClockTimeNone { - return time.Duration(-1) + if dur == gstClockTimeNone { + return gst.ClockTimeNone } return time.Duration(uint64(dur)) * time.Nanosecond } diff --git a/gst/base/gst_base_src_impl.go b/gst/base/gst_base_src_impl.go index ea56f84..95a09cd 100644 --- a/gst/base/gst_base_src_impl.go +++ b/gst/base/gst_base_src_impl.go @@ -50,7 +50,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" "github.com/tinyzimmer/go-gst/gst" ) diff --git a/gst/c_util.go b/gst/c_util.go index b700c32..923f2f1 100644 --- a/gst/c_util.go +++ b/gst/c_util.go @@ -8,7 +8,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) func toGObject(data unsafe.Pointer) *glib.Object { @@ -99,7 +99,7 @@ func streamSliceToGlist(streams []*Stream) *C.GList { for _, stream := range streams { wrapped = wrapped.Append(uintptr(stream.Unsafe())) } - return (*C.GList)(unsafe.Pointer(wrapped.Native())) + return &glist } func glistToStreamSlice(glist *C.GList) []*Stream { diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index ae74fa7..a790009 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -10,10 +10,11 @@ import "C" import ( "reflect" + "time" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) //export goElementCallAsync @@ -223,7 +224,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, ClockTime(clockTime))) + return gboolean(cb(clock, time.Duration(clockTime))) } //export goPluginInit diff --git a/gst/constants.go b/gst/constants.go index cc25b59..878f8aa 100644 --- a/gst/constants.go +++ b/gst/constants.go @@ -3,7 +3,10 @@ package gst // #include "gst.go.h" import "C" -import "unsafe" +import ( + "time" + "unsafe" +) // Version represents information about the current GST version. type Version int @@ -47,20 +50,20 @@ func (g GFraction) Num() int { return g.num } // Denom returns the fraction's denominator. func (g GFraction) Denom() int { return g.denom } -// ClockTime is a go representation of a GstClockTime. Most of the time these are casted -// to time.Duration objects. It represents a time value in nanoseconds. -type ClockTime uint64 - // ClockTimeDiff is a datatype to hold a time difference, measured in nanoseconds. type ClockTimeDiff int64 -const ( - // ClockFormat is the string used when formatting clock strings - ClockFormat string = "u:%02u:%02u.%09u" +// ClockTimeNone means infinite timeout or an empty value +var ClockTimeNone time.Duration = time.Duration(-1) + +// 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. - ClockTimeNone ClockTime = 0xffffffffffffffff - // BufferOffsetNone is a constant for no-offset return results. - BufferOffsetNone ClockTime = 0xffffffffffffffff + gstClockTimeNone C.GstClockTime = 0xffffffffffffffff + // // BufferOffsetNone is a constant for no-offset return results. + // gstBufferOffsetNone C.GstClockTime = 0xffffffffffffffff ) // ClockEntryType wraps GstClockEntryType @@ -562,6 +565,32 @@ const ( SeekTypeEnd SeekType = C.GST_SEEK_TYPE_END ) +// StateChange is the different state changes an element goes through. StateNull ⇒ StatePlaying is called +// an upwards state change and StatePlaying ⇒ StateNull a downwards state change. +// +// See https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html?gi-language=c#GstStateChange +// for more information on the responsibiltiies of elements during each transition. +type StateChange int + +// StateChange castings +const ( + StateChangeNullToReady StateChange = C.GST_STATE_CHANGE_NULL_TO_READY + StateChangeReadyToPaused StateChange = C.GST_STATE_CHANGE_READY_TO_PAUSED + StateChangePausedToPlaying StateChange = C.GST_STATE_CHANGE_PAUSED_TO_PLAYING + StateChangePlayingToPaused StateChange = C.GST_STATE_CHANGE_PLAYING_TO_PAUSED + StateChangePausedToReady StateChange = C.GST_STATE_CHANGE_PAUSED_TO_READY + StateChangeReadyToNull StateChange = C.GST_STATE_CHANGE_READY_TO_NULL + StateChangeNullToNull StateChange = C.GST_STATE_CHANGE_NULL_TO_NULL + StateChangeReadyToReady StateChange = C.GST_STATE_CHANGE_READY_TO_READY + StateChangePausedToPaused StateChange = C.GST_STATE_CHANGE_PAUSED_TO_PAUSED + StateChangePlayingToPlaying StateChange = C.GST_STATE_CHANGE_PLAYING_TO_PLAYING +) + +// String returns the string representation of a StateChange +func (s StateChange) String() string { + return C.GoString(C.gst_state_change_get_name(C.GstStateChange(s))) +} + // StateChangeReturn is a representation of GstStateChangeReturn. type StateChangeReturn int @@ -573,6 +602,10 @@ const ( StateChangeNoPreroll StateChangeReturn = C.GST_STATE_CHANGE_NO_PREROLL ) +func (s StateChangeReturn) String() string { + return C.GoString(C.gst_element_state_change_return_get_name(C.GstStateChangeReturn(s))) +} + // ElementFlags casts C GstElementFlags to a go type type ElementFlags int diff --git a/gst/g_object.go b/gst/g_object.go index 91a0780..17abdae 100644 --- a/gst/g_object.go +++ b/gst/g_object.go @@ -3,10 +3,10 @@ package gst /* #include "gst.go.h" -extern void goObjectSetProperty (GObject * object, guint property_id, const GValue * value, GParamSpec *pspec); -extern void goObjectGetProperty (GObject * object, guint property_id, GValue * value, GParamSpec * pspec); -extern void goObjectConstructed (GObject * object); -extern void goObjectFinalize (GObject * object, gpointer klass); +extern void goObjectSetProperty (GObject * object, guint property_id, const GValue * value, GParamSpec *pspec); +extern void goObjectGetProperty (GObject * object, guint property_id, GValue * value, GParamSpec * pspec); +extern void goObjectConstructed (GObject * object); +extern void goObjectFinalize (GObject * object, gpointer klass); void objectFinalize (GObject * object) { @@ -32,10 +32,10 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) -// GoObject is an interface that abstracts on the GObject. In most cases at least SetProperty and GetProperty +// GoObject is an interface that abstracts on the GObject. In almost all cases at least SetProperty and GetProperty // should be implemented by elements built from the go bindings. type GoObject interface { // SetProperty should set the value of the property with the given id. ID is the index+1 of the parameter @@ -48,7 +48,8 @@ type GoObject interface { Constructed(*Object) } -// ExtendsObject signifies a GoElement that extends a GObject. +// ExtendsObject signifies a GoElement that extends a GObject. It is the base Extendable +// that all other implementations derive from. var ExtendsObject Extendable = &extendObject{} type extendObject struct{} diff --git a/gst/g_object_class.go b/gst/g_object_class.go index cbe261c..b497c9d 100644 --- a/gst/g_object_class.go +++ b/gst/g_object_class.go @@ -22,7 +22,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // ObjectClass is a loose binding around the glib GObjectClass. diff --git a/gst/g_parameter_spec.go b/gst/g_parameter_spec.go index 967fb60..2c67ea4 100644 --- a/gst/g_parameter_spec.go +++ b/gst/g_parameter_spec.go @@ -7,7 +7,7 @@ import ( "strings" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // ParameterSpec is a go representation of a C GParamSpec diff --git a/gst/gst_allocator.go b/gst/gst_allocator.go index 8fad661..9fe701d 100644 --- a/gst/gst_allocator.go +++ b/gst/gst_allocator.go @@ -6,7 +6,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // AllocationParams wraps the GstAllocationParams. diff --git a/gst/gst_bin.go b/gst/gst_bin.go index de7d1c5..8a15615 100644 --- a/gst/gst_bin.go +++ b/gst/gst_bin.go @@ -8,7 +8,7 @@ import ( "fmt" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Bin is a go wrapper arounds a GstBin. diff --git a/gst/gst_buffer.go b/gst/gst_buffer.go index 579480e..608aeca 100644 --- a/gst/gst_buffer.go +++ b/gst/gst_buffer.go @@ -26,8 +26,8 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // GetMaxBufferMemory returns the maximum amount of memory a buffer can hold. @@ -161,42 +161,42 @@ func (b *Buffer) Bytes() []byte { // presented to the user. func (b *Buffer) PresentationTimestamp() time.Duration { pts := b.Instance().pts - if ClockTime(pts) == ClockTimeNone { - return time.Duration(-1) + if pts == gstClockTimeNone { + return ClockTimeNone } - return guint64ToDuration(pts) + return time.Duration(pts) } // SetPresentationTimestamp sets the presentation timestamp on the buffer. func (b *Buffer) SetPresentationTimestamp(dur time.Duration) { ins := b.Instance() - ins.pts = C.GstClockTime(durationToClockTime(dur)) + ins.pts = C.GstClockTime(dur.Nanoseconds()) } // 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 { dts := b.Instance().dts - if ClockTime(dts) == ClockTimeNone { - return time.Duration(-1) + if dts == gstClockTimeNone { + return ClockTimeNone } - return guint64ToDuration(dts) + return time.Duration(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 { dur := b.Instance().duration - if ClockTime(dur) == ClockTimeNone { - return time.Duration(-1) + if dur == gstClockTimeNone { + return ClockTimeNone } - return guint64ToDuration(dur) + return time.Duration(dur) } // SetDuration sets the duration on the buffer. func (b *Buffer) SetDuration(dur time.Duration) { ins := b.Instance() - ins.duration = C.GstClockTime(durationToClockTime(dur)) + ins.duration = C.GstClockTime(dur.Nanoseconds()) } // Offset returns a media specific offset for the buffer data. For video frames, this is the frame @@ -292,8 +292,8 @@ func (b *Buffer) AddReferenceTimestampMeta(ref *Caps, timestamp, duration time.D return &ReferenceTimestampMeta{ Parent: wrapMeta(&meta.parent), Reference: wrapCaps(meta.reference), - Timestamp: clockTimeToDuration(ClockTime(meta.timestamp)), - Duration: clockTimeToDuration(ClockTime(meta.duration)), + Timestamp: time.Duration(meta.timestamp), + Duration: time.Duration(meta.duration), } } @@ -472,8 +472,8 @@ func (b *Buffer) GetReferenceTimestampMeta(caps *Caps) *ReferenceTimestampMeta { return &ReferenceTimestampMeta{ Parent: wrapMeta(&meta.parent), Reference: wrapCaps(meta.reference), - Timestamp: clockTimeToDuration(ClockTime(meta.timestamp)), - Duration: clockTimeToDuration(ClockTime(meta.duration)), + Timestamp: time.Duration(meta.timestamp), + Duration: time.Duration(meta.duration), } } diff --git a/gst/gst_buffer_pool.go b/gst/gst_buffer_pool.go index e06e18b..19df1f6 100644 --- a/gst/gst_buffer_pool.go +++ b/gst/gst_buffer_pool.go @@ -7,7 +7,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // BufferPool is a go wrapper around a GstBufferPool. diff --git a/gst/gst_bus.go b/gst/gst_bus.go index 41d01cf..0ac0f71 100644 --- a/gst/gst_bus.go +++ b/gst/gst_bus.go @@ -28,8 +28,8 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // Bus is a Go wrapper around a GstBus. It provides convenience methods for @@ -283,7 +283,7 @@ func (b *Bus) Peek() *Message { // For 0 timeouts use gst_bus_pop_filtered instead of this function; for other short timeouts use TimedPopFiltered; // everything else is better handled by setting up an asynchronous bus watch and doing things from there. func (b *Bus) Poll(msgTypes MessageType, timeout time.Duration) *Message { - cTime := C.GstClockTime(durationToClockTime(timeout)) + cTime := C.GstClockTime(timeout.Nanoseconds()) mType := C.GstMessageType(msgTypes) msg := C.gst_bus_poll(b.Instance(), mType, cTime) if msg == nil { @@ -364,10 +364,10 @@ func (b *Bus) SetSyncHandler(f BusSyncHandler) { // 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.Nanoseconds() < 0 { - cTime = C.GstClockTime(ClockTimeNone) + if dur == ClockTimeNone { + cTime = C.GstClockTime(gstClockTimeNone) } else { - cTime = C.GstClockTime(durationToClockTime(dur)) + cTime = C.GstClockTime(dur.Nanoseconds()) } msg := C.gst_bus_timed_pop(b.Instance(), cTime) if msg == nil { @@ -383,10 +383,10 @@ func (b *Bus) TimedPop(dur time.Duration) *Message { // was posted on the bus. func (b *Bus) TimedPopFiltered(dur time.Duration, msgTypes MessageType) *Message { var cTime C.GstClockTime - if dur.Nanoseconds() < 0 { - cTime = C.GstClockTime(ClockTimeNone) + if dur == ClockTimeNone { + cTime = C.GstClockTime(gstClockTimeNone) } else { - cTime = C.GstClockTime(durationToClockTime(dur)) + cTime = C.GstClockTime(dur.Nanoseconds()) } msg := C.gst_bus_timed_pop_filtered(b.Instance(), cTime, C.GstMessageType(msgTypes)) if msg == nil { diff --git a/gst/gst_caps.go b/gst/gst_caps.go index 5c4246a..26b66a2 100644 --- a/gst/gst_caps.go +++ b/gst/gst_caps.go @@ -17,8 +17,8 @@ import ( "fmt" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // Caps is a go wrapper around GstCaps. @@ -406,7 +406,7 @@ func (c *Caps) SetValue(field string, val interface{}) { C.gst_caps_set_value( c.Instance(), (*C.gchar)(unsafe.Pointer(C.CString(field))), - (*C.GValue)(gVal.Native()), + (*C.GValue)(unsafe.Pointer(gVal.GValue)), ) } diff --git a/gst/gst_clock.go b/gst/gst_clock.go index ae17494..3f9ca75 100644 --- a/gst/gst_clock.go +++ b/gst/gst_clock.go @@ -23,12 +23,12 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // ClockCallback is the prototype of a clock callback function. -type ClockCallback func(clock *Clock, clockTime ClockTime) bool +type ClockCallback func(clock *Clock, clockTime time.Duration) bool // ClockID is a go wrapper around a GstClockID. type ClockID struct { @@ -45,8 +45,8 @@ func (c *ClockID) GetClock() *Clock { } // GetTime returns the time for this ClockID -func (c *ClockID) GetTime() ClockTime { - return ClockTime(C.gst_clock_id_get_time(c.Instance())) +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. @@ -88,8 +88,8 @@ func (c *ClockID) Wait() (ret ClockReturn, jitter ClockTimeDiff) { // // id := clock.NewSingleShotID(gst.ClockTime(1000000000)) // 1 second // -// id.WaitAsync(func(clock *gst.Clock, clockTime gst.ClockTime) bool { -// fmt.Println("Single shot triggered at", clockTime) +// id.WaitAsync(func(clock *gst.Clock, clockTime time.Duration) bool { +// fmt.Println("Single shot triggered at", clockTime.Nanoseconds()) // pipeline.SetState(gst.StateNull) // return true // }) @@ -130,7 +130,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 ClockTime) (bool, float64) { +func (c *Clock) AddObservation(slaveTime, masterTime time.Duration) (bool, float64) { var out C.gdouble ok := gobool(C.gst_clock_add_observation( c.Instance(), @@ -145,7 +145,7 @@ func (c *Clock) AddObservation(slaveTime, masterTime ClockTime) (bool, float64) // 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 ClockTime) (ok bool, rSquared float64, internalTime, externalTime, rateNum, rateDenom ClockTime) { +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( @@ -154,7 +154,7 @@ func (c *Clock) AddObservationUnapplied(slaveTime, masterTime ClockTime) (ok boo C.GstClockTime(masterTime), &grSquared, &ginternal, &gexternal, &grateNum, &grateDenom, )) - return ok, float64(grSquared), ClockTime(ginternal), ClockTime(gexternal), ClockTime(grateNum), ClockTime(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 @@ -162,8 +162,8 @@ func (c *Clock) AddObservationUnapplied(slaveTime, masterTime ClockTime) (ok boo // held and is mainly used by clock subclasses. // // This function is the reverse of UnadjustUnlocked. -func (c *Clock) AdjustUnlocked(internal ClockTime) ClockTime { - return ClockTime(C.gst_clock_adjust_unlocked(c.Instance(), C.GstClockTime(internal))) +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. @@ -171,8 +171,8 @@ func (c *Clock) AdjustUnlocked(internal ClockTime) ClockTime { // 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 ClockTime) ClockTime { - return ClockTime(C.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), @@ -183,48 +183,30 @@ 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 ClockTime) { +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 ClockTime(ginternal), ClockTime(gexternal), ClockTime(grateNum), ClockTime(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 ClockTimeNone if invalid. +// 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() ClockTime { +func (c *Clock) GetTime() time.Duration { res := C.gst_clock_get_time(c.Instance()) - if ClockTime(res) == ClockTimeNone { + if res == C.GST_CLOCK_TIME_NONE { return ClockTimeNone } - return ClockTime(res) + 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() ClockTime { +func (c *Clock) GetInternalTime() time.Duration { res := C.gst_clock_get_internal_time(c.Instance()) - if ClockTime(res) == ClockTimeNone { + if res == C.GST_CLOCK_TIME_NONE { return ClockTimeNone } - return ClockTime(res) -} - -// GetDuration returns the time.Duration equivalent of this clock time. -func (c *Clock) GetDuration() time.Duration { - tm := c.GetTime() - if tm == ClockTimeNone { - return time.Duration(-1) - } - return clockTimeToDuration(tm) -} - -// GetInternalDuration returns the time.Duration equivalent of this clock's internal time. -func (c *Clock) GetInternalDuration() time.Duration { - tm := c.GetInternalTime() - if tm == ClockTimeNone { - return time.Duration(-1) - } - return clockTimeToDuration(tm) + return time.Duration(res) } // GetMaster returns the master clock that this clock is slaved to or nil when the clock @@ -239,17 +221,21 @@ 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() ClockTime { return ClockTime(C.gst_clock_get_resolution(c.Instance())) } +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() ClockTime { return ClockTime(C.gst_clock_get_timeout(c.Instance())) } +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 ClockTime) *ClockID { +func (c *Clock) NewPeriodicID(startTime, interval time.Duration) *ClockID { id := C.gst_clock_new_periodic_id( c.Instance(), C.GstClockTime(startTime), @@ -260,7 +246,7 @@ func (c *Clock) NewPeriodicID(startTime, interval ClockTime) *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 ClockTime) *ClockID { +func (c *Clock) NewSingleShotID(at time.Duration) *ClockID { id := C.gst_clock_new_single_shot_id( c.Instance(), C.GstClockTime(at), @@ -270,7 +256,7 @@ func (c *Clock) NewSingleShotID(at ClockTime) *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 ClockTime) bool { +func (c *Clock) PeriodicIDReinit(clockID *ClockID, startTime, interval time.Duration) bool { return gobool(C.gst_clock_periodic_id_reinit( c.Instance(), clockID.Instance(), @@ -281,7 +267,7 @@ func (c *Clock) PeriodicIDReinit(clockID *ClockID, startTime, interval ClockTime // 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 ClockTime) { +func (c *Clock) SetCalibration(internal, external, rateNum, rateDenom time.Duration) { C.gst_clock_set_calibration( c.Instance(), C.GstClockTime(internal), @@ -305,8 +291,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 ClockTime) ClockTime { - return ClockTime(C.gst_clock_set_resolution(c.Instance(), C.GstClockTime(resolution))) +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. @@ -316,12 +302,12 @@ func (c *Clock) SetResolution(resolution ClockTime) ClockTime { 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 ClockTime) { +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 ClockTime) bool { +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))) } @@ -329,14 +315,14 @@ func (c *Clock) SingleShotIDReinit(clockID *ClockID, at ClockTime) 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 ClockTime) ClockTime { - return ClockTime(C.gst_clock_unadjust_unlocked(c.Instance(), C.GstClockTime(external))) +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 ClockTime) ClockTime { - return ClockTime(C.gst_clock_unadjust_with_calibration( +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), @@ -352,12 +338,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 ClockTime) bool { +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.GetDuration().String() } +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.GetInternalDuration().String() } +func (c *Clock) InternalString() string { return c.GetInternalTime().String() } diff --git a/gst/gst_device.go b/gst/gst_device.go index f01ed5d..61f5b61 100644 --- a/gst/gst_device.go +++ b/gst/gst_device.go @@ -8,7 +8,7 @@ import ( "strings" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Device is a Go representation of a GstDevice. diff --git a/gst/gst_element.go b/gst/gst_element.go index 26867d6..c59b00a 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -16,6 +16,46 @@ void cgoElementCallAsync (GstElement * element, gpointer user_data) goElementCallAsync(element, user_data); } +gboolean elementParentPostMessage (GstElement * element, GstMessage * message) { + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(element)); + GstElementClass * parent = toGstElementClass(g_type_class_peek_parent(this_class)); + return parent->post_message(element, message); +} + +extern GstStateChangeReturn goGstElementClassChangeState (GstElement * element, GstStateChange change); +extern GstStateChangeReturn goGstElementClassGetState (GstElement * element, GstState * state, GstState * pending, GstClockTime timeout); +extern void goGstElementClassNoMorePads (GstElement * element); +extern void goGstElementClassPadAdded (GstElement * element, GstPad * pad); +extern void goGstElementClassPadRemoved (GstElement * element, GstPad * pad); +extern gboolean goGstElementClassPostMessage (GstElement * element, GstMessage * msg); +extern GstClock * goGstElementClassProvideClock (GstElement * element); +extern gboolean goGstElementClassQuery (GstElement * element, GstQuery * query); +extern void goGstElementClassReleasePad (GstElement * element, GstPad * pad); +extern GstPad * goGstElementClassRequestNewPad (GstElement * element, GstPadTemplate * templ, const gchar * name, const GstCaps * caps); +extern gboolean goGstElementClassSendEvent (GstElement * element, GstEvent * event); +extern void goGstElementClassSetBus (GstElement * element, GstBus * bus); +extern gboolean goGstElementClassSetClock (GstElement * element, GstClock * clock); +extern void goGstElementClassSetContext (GstElement * element, GstContext * ctx); +extern GstStateChangeReturn goGstElementClassSetState (GstElement * element, GstState state); +extern void goGstElementClassStateChanged (GstElement * element, GstState old, GstState new, GstState pending); + +void setGstElementClassChangeState (GstElementClass * klass) { klass->change_state = goGstElementClassChangeState; } +void setGstElementClassGetState (GstElementClass * klass) { klass->get_state = goGstElementClassGetState; } +void setGstElementClassNoMorePads (GstElementClass * klass) { klass->no_more_pads = goGstElementClassNoMorePads; } +void setGstElementClassPadAdded (GstElementClass * klass) { klass->pad_added = goGstElementClassPadAdded; } +void setGstElementClassPadRemoved (GstElementClass * klass) { klass->pad_removed = goGstElementClassPadRemoved; } +void setGstElementClassPostMessage (GstElementClass * klass) { klass->post_message = goGstElementClassPostMessage; } +void setGstElementClassProvideClock (GstElementClass * klass) { klass->provide_clock = goGstElementClassProvideClock; } +void setGstElementClassQuery (GstElementClass * klass) { klass->query = goGstElementClassQuery; } +void setGstElementClassReleasePad (GstElementClass * klass) { klass->release_pad = goGstElementClassReleasePad; } +void setGstElementClassRequestNewPad (GstElementClass * klass) { klass->request_new_pad = goGstElementClassRequestNewPad; } +void setGstElementClassSendEvent (GstElementClass * klass) { klass->send_event = goGstElementClassSendEvent; } +void setGstElementClassSetBus (GstElementClass * klass) { klass->set_bus = goGstElementClassSetBus; } +void setGstElementClassSetClock (GstElementClass * klass) { klass->set_clock = goGstElementClassSetClock; } +void setGstElementClassSetContext (GstElementClass * klass) { klass->set_context = goGstElementClassSetContext; } +void setGstElementClassSetState (GstElementClass * klass) { klass->set_state = goGstElementClassSetState; } +void setGstElementClassStateChanged (GstElementClass * klass) { klass->state_changed = goGstElementClassStateChanged; } + */ import "C" @@ -23,9 +63,10 @@ import ( "fmt" "path" "runtime" + "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" gopointer "github.com/mattn/go-pointer" ) @@ -119,6 +160,11 @@ func (e *Element) CallAsync(f func()) { ) } +// ChangeState performs the given transition on this element. +func (e *Element) ChangeState(transition StateChange) StateChangeReturn { + return StateChangeReturn(C.gst_element_change_state(e.Instance(), C.GstStateChange(transition))) +} + // Connect connects to the given signal on this element, and applies f as the callback. The callback must // match the signature of the expected callback from the documentation. However, instead of specifying C types // for arguments specify the go-gst equivalent (e.g. *gst.Element for almost all GstElement derivitives). @@ -291,6 +337,12 @@ func (e *Element) LinkFiltered(elem *Element, caps *Caps) error { return nil } +// ParentPostMessage can be used when extending an element. During a PostMessage, use this method +// to have the message posted on the bus after processing. +func (e *Element) ParentPostMessage(msg *Message) bool { + return gobool(C.elementParentPostMessage(e.Instance(), msg.Instance())) +} + // Query performs a query on the given element. // // For elements that don't implement a query handler, this function forwards the query to a random srcpad or @@ -388,6 +440,44 @@ func (e *Element) URIHandler() URIHandler { // ExtendsElement signifies a GoElement that extends a GstElement. var ExtendsElement Extendable = &extendElement{parent: ExtendsObject} +// ElementImpl is an interface containing go quivalents of the virtual methods that can be +// overridden by a plugin extending an Element. +type ElementImpl interface { + // ChangeState is called by SetState to perform an incremental state change. + ChangeState(*Element, StateChange) StateChangeReturn + // GetState should return the states of the element + GetState(self *Element, timeout time.Duration) (ret StateChangeReturn, current, pending State) + // NoMorePads is called when there are no more pads on the element. + NoMorePads(*Element) + // PadAdded is called to add a pad to the element. + PadAdded(*Element, *Pad) + // PadRemoved is called to remove a pad from the element. + PadRemoved(*Element, *Pad) + // PostMessage is called when a message is posted to the element. Call Element.ParentPostMessage + // to have it posted on the bus after processing. + PostMessage(*Element, *Message) bool + // ProvideClock is called to retrieve the clock provided by the element. + ProvideClock(*Element) *Clock + // Query is called to perform a query on the element. + Query(*Element, *Query) bool + // ReleasePad is called when a request pad is to be released. + ReleasePad(*Element, *Pad) + // RequestNewPad is called when a new pad is requested from the element. + RequestNewPad(self *Element, templ *PadTemplate, name string, caps *Caps) *Pad + // SendEvent is called to send an event to the element. + SendEvent(*Element, *Event) bool + // SetBus is called to set the Bus on the element. + SetBus(*Element, *Bus) + // SetClock is called to set the clock on the element. + SetClock(*Element, *Clock) bool + // SetContext is called to set the Context on the element. + SetContext(*Element, *Context) + // SetState is called to set a new state on the element. + SetState(*Element, State) StateChangeReturn + // StateChanged is called immediately after a new state was set. + StateChanged(self *Element, old, new, pending State) +} + type extendElement struct{ parent Extendable } func (e *extendElement) Type() glib.Type { return glib.Type(C.gst_element_get_type()) } @@ -396,4 +486,102 @@ func (e *extendElement) InstanceSize() int64 { return int64(C.sizeof_GstElement) func (e *extendElement) InitClass(klass unsafe.Pointer, elem GoElement) { e.parent.InitClass(klass, elem) + + elemClass := C.toGstElementClass(klass) + + if _, ok := elem.(interface { + ChangeState(*Element, StateChange) StateChangeReturn + }); ok { + C.setGstElementClassChangeState(elemClass) + } + + if _, ok := elem.(interface { + GetState(self *Element, timeout time.Duration) (ret StateChangeReturn, current, pending State) + }); ok { + C.setGstElementClassGetState(elemClass) + } + + if _, ok := elem.(interface { + NoMorePads(*Element) + }); ok { + C.setGstElementClassNoMorePads(elemClass) + } + + if _, ok := elem.(interface { + PadAdded(*Element, *Pad) + }); ok { + C.setGstElementClassPadAdded(elemClass) + } + + if _, ok := elem.(interface { + PadRemoved(*Element, *Pad) + }); ok { + C.setGstElementClassPadRemoved(elemClass) + } + + if _, ok := elem.(interface { + PostMessage(*Element, *Message) bool + }); ok { + C.setGstElementClassPostMessage(elemClass) + } + + if _, ok := elem.(interface { + ProvideClock(*Element) *Clock + }); ok { + C.setGstElementClassProvideClock(elemClass) + } + + if _, ok := elem.(interface { + Query(*Element, *Query) bool + }); ok { + C.setGstElementClassQuery(elemClass) + } + + if _, ok := elem.(interface { + ReleasePad(*Element, *Pad) + }); ok { + C.setGstElementClassReleasePad(elemClass) + } + + if _, ok := elem.(interface { + RequestNewPad(self *Element, templ *PadTemplate, name string, caps *Caps) *Pad + }); ok { + C.setGstElementClassRequestNewPad(elemClass) + } + + if _, ok := elem.(interface { + SendEvent(*Element, *Event) bool + }); ok { + C.setGstElementClassSendEvent(elemClass) + } + + if _, ok := elem.(interface { + SetBus(*Element, *Bus) + }); ok { + C.setGstElementClassSetBus(elemClass) + } + + if _, ok := elem.(interface { + SetClock(*Element, *Clock) bool + }); ok { + C.setGstElementClassSetClock(elemClass) + } + + if _, ok := elem.(interface { + SetContext(*Element, *Context) + }); ok { + C.setGstElementClassSetContext(elemClass) + } + + if _, ok := elem.(interface { + SetState(*Element, State) StateChangeReturn + }); ok { + C.setGstElementClassSetState(elemClass) + } + + if _, ok := elem.(interface { + StateChanged(self *Element, old, new, pending State) + }); ok { + C.setGstElementClassStateChanged(elemClass) + } } diff --git a/gst/gst_element_exports.go b/gst/gst_element_exports.go new file mode 100644 index 0000000..e96d122 --- /dev/null +++ b/gst/gst_element_exports.go @@ -0,0 +1,160 @@ +package gst + +/* +#include "gst.go.h" +*/ +import "C" + +import ( + "time" + "unsafe" +) + +//export goGstElementClassChangeState +func goGstElementClassChangeState(elem *C.GstElement, change C.GstStateChange) C.GstStateChangeReturn { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + ChangeState(*Element, StateChange) StateChangeReturn + }) + return C.GstStateChangeReturn(iface.ChangeState(wrapCbElem(elem), StateChange(change))) +} + +//export goGstElementClassGetState +func goGstElementClassGetState(elem *C.GstElement, state, pending *C.GstState, timeout C.GstClockTime) C.GstStateChangeReturn { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + GetState(*Element, time.Duration) (ret StateChangeReturn, current, pending State) + }) + ret, cur, pend := iface.GetState(wrapCbElem(elem), time.Duration(timeout)*time.Nanosecond) + if ret == StateChangeFailure { + return C.GstStateChangeReturn(ret) + } + *state = C.GstState(cur) + *pending = C.GstState(pend) + return C.GstStateChangeReturn(ret) +} + +//export goGstElementClassNoMorePads +func goGstElementClassNoMorePads(elem *C.GstElement) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + NoMorePads(*Element) + }) + iface.NoMorePads(wrapCbElem(elem)) +} + +//export goGstElementClassPadAdded +func goGstElementClassPadAdded(elem *C.GstElement, pad *C.GstPad) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + PadAdded(*Element, *Pad) + }) + iface.PadAdded(wrapCbElem(elem), wrapPad(toGObject(unsafe.Pointer(pad)))) +} + +//export goGstElementClassPadRemoved +func goGstElementClassPadRemoved(elem *C.GstElement, pad *C.GstPad) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + PadRemoved(*Element, *Pad) + }) + iface.PadRemoved(wrapCbElem(elem), wrapPad(toGObject(unsafe.Pointer(pad)))) +} + +//export goGstElementClassPostMessage +func goGstElementClassPostMessage(elem *C.GstElement, msg *C.GstMessage) C.gboolean { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + PostMessage(*Element, *Message) bool + }) + return gboolean(iface.PostMessage(wrapCbElem(elem), wrapMessage(msg))) +} + +//export goGstElementClassProvideClock +func goGstElementClassProvideClock(elem *C.GstElement) *C.GstClock { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + ProvideClock(*Element) *Clock + }) + clock := iface.ProvideClock(wrapCbElem(elem)) + if clock == nil { + return nil + } + return clock.Instance() +} + +//export goGstElementClassQuery +func goGstElementClassQuery(elem *C.GstElement, query *C.GstQuery) C.gboolean { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + Query(*Element, *Query) bool + }) + return gboolean(iface.Query(wrapCbElem(elem), wrapQuery(query))) +} + +//export goGstElementClassReleasePad +func goGstElementClassReleasePad(elem *C.GstElement, pad *C.GstPad) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + ReleasePad(*Element, *Pad) + }) + iface.ReleasePad(wrapCbElem(elem), wrapPad(toGObject(unsafe.Pointer(pad)))) +} + +//export goGstElementClassRequestNewPad +func goGstElementClassRequestNewPad(elem *C.GstElement, templ *C.GstPadTemplate, name *C.gchar, caps *C.GstCaps) *C.GstPad { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + RequestNewPad(self *Element, templ *PadTemplate, name string, caps *Caps) *Pad + }) + pad := iface.RequestNewPad( + wrapCbElem(elem), + wrapPadTemplate(toGObject(unsafe.Pointer(templ))), + C.GoString(name), + wrapCaps(caps), + ) + if pad == nil { + return nil + } + return pad.Instance() +} + +//export goGstElementClassSendEvent +func goGstElementClassSendEvent(elem *C.GstElement, event *C.GstEvent) C.gboolean { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + SendEvent(*Element, *Event) bool + }) + return gboolean(iface.SendEvent(wrapCbElem(elem), wrapEvent(event))) +} + +//export goGstElementClassSetBus +func goGstElementClassSetBus(elem *C.GstElement, bus *C.GstBus) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + SetBus(*Element, *Bus) + }) + iface.SetBus(wrapCbElem(elem), wrapBus(toGObject(unsafe.Pointer(bus)))) +} + +//export goGstElementClassSetClock +func goGstElementClassSetClock(elem *C.GstElement, clock *C.GstClock) C.gboolean { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + SetClock(*Element, *Clock) bool + }) + return gboolean(iface.SetClock(wrapCbElem(elem), wrapClock(toGObject(unsafe.Pointer(clock))))) +} + +//export goGstElementClassSetContext +func goGstElementClassSetContext(elem *C.GstElement, ctx *C.GstContext) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + SetContext(*Element, *Context) + }) + iface.SetContext(wrapCbElem(elem), wrapContext(ctx)) +} + +//export goGstElementClassSetState +func goGstElementClassSetState(elem *C.GstElement, state C.GstState) C.GstStateChangeReturn { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + SetState(*Element, State) StateChangeReturn + }) + return C.GstStateChangeReturn(iface.SetState(wrapCbElem(elem), State(state))) +} + +//export goGstElementClassStateChanged +func goGstElementClassStateChanged(elem *C.GstElement, old, new, pending C.GstState) { + iface := FromObjectUnsafePrivate(unsafe.Pointer(elem)).(interface { + StateChanged(self *Element, old, new, pending State) + }) + iface.StateChanged(wrapCbElem(elem), State(old), State(new), State(pending)) +} + +func wrapCbElem(elem *C.GstElement) *Element { return wrapElement(toGObject(unsafe.Pointer(elem))) } diff --git a/gst/gst_element_factory.go b/gst/gst_element_factory.go index 3916787..b5c0eea 100644 --- a/gst/gst_element_factory.go +++ b/gst/gst_element_factory.go @@ -7,7 +7,7 @@ import ( "fmt" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // FromGstElementUnsafe wraps the pointer to the given C GstElement with the go type. diff --git a/gst/gst_event.go b/gst/gst_event.go index fda1212..851248f 100644 --- a/gst/gst_event.go +++ b/gst/gst_event.go @@ -7,7 +7,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Event is a go wrapper around a GstEvent. @@ -108,7 +108,7 @@ func (e *Event) ParseFlushStop() (resetTime bool) { func (e *Event) ParseGap() (timestamp, duration time.Duration) { var ts, dur C.GstClockTime C.gst_event_parse_gap(e.Instance(), &ts, &dur) - return clockTimeToDuration(ClockTime(ts)), clockTimeToDuration(ClockTime(dur)) + return time.Duration(ts), time.Duration(dur) } // ParseGapFlags retrieves the gap flags that may have been set on a gap event with SetGapFlags. @@ -135,7 +135,7 @@ func (e *Event) ParseGroupID() (ok bool, gid uint) { func (e *Event) ParseLatency() time.Duration { var out C.GstClockTime C.gst_event_parse_latency(e.Instance(), &out) - return clockTimeToDuration(ClockTime(out)) + return time.Duration(out) } // ParseProtection parses an event containing protection system specific information and stores the results in @@ -168,7 +168,7 @@ func (e *Event) ParseQOS() (qTtype QOSType, proportion float64, diff ClockTimeDi e.Instance(), >ype, &gprop, &gdiff, >s, ) - return QOSType(gtype), float64(gprop), ClockTimeDiff(gdiff), clockTimeToDuration(ClockTime(gts)) + return QOSType(gtype), float64(gprop), ClockTimeDiff(gdiff), time.Duration(gts) } // ParseSeek parses a seek event. @@ -187,7 +187,7 @@ func (e *Event) ParseSeek() (rate float64, format Format, flags SeekFlags, start func (e *Event) ParseSeekTrickModeInterval() (interval time.Duration) { var out C.GstClockTime C.gst_event_parse_seek_trickmode_interval(e.Instance(), &out) - return clockTimeToDuration(ClockTime(out)) + return time.Duration(out) } // ParseSegment parses a segment event and stores the result in the given segment location. segment remains valid @@ -324,7 +324,7 @@ func (e *Event) SetRunningTimeOffset(offset int64) { // SetSeekTrickModeInterval sets a trickmode interval on a (writable) seek event. Elements that support TRICKMODE_KEY_UNITS // seeks SHOULD use this as the minimal interval between each frame they may output. func (e *Event) SetSeekTrickModeInterval(interval time.Duration) { - C.gst_event_set_seek_trickmode_interval(e.Instance(), C.GstClockTime(durationToClockTime(interval))) + C.gst_event_set_seek_trickmode_interval(e.Instance(), C.GstClockTime(interval.Nanoseconds())) } // SetSeqnum sets the sequence number of a event. diff --git a/gst/gst_event_constructors.go b/gst/gst_event_constructors.go index ac80cd3..6d51ff8 100644 --- a/gst/gst_event_constructors.go +++ b/gst/gst_event_constructors.go @@ -67,8 +67,8 @@ func NewFlushStopEvent(resetTime bool) *Event { // especially for sparse streams such as subtitle streams. func NewGapEvent(timestamp, duration time.Duration) *Event { return wrapEvent(C.gst_event_new_gap( - C.GstClockTime(durationToClockTime(timestamp)), - C.GstClockTime(durationToClockTime(duration)), + C.GstClockTime(timestamp.Nanoseconds()), + C.GstClockTime(duration.Nanoseconds()), )) } @@ -105,7 +105,7 @@ func NewGapEvent(timestamp, duration time.Duration) *Event { // The latency is mostly used in live sinks and is always expressed in the time format. func NewLatencyEvent(latency time.Duration) *Event { return wrapEvent(C.gst_event_new_latency( - C.GstClockTime(durationToClockTime(latency)), + C.GstClockTime(latency.Nanoseconds()), )) } @@ -168,7 +168,7 @@ func NewQOSEvent(qType QOSType, proportion float64, diff ClockTimeDiff, timestam C.GstQOSType(qType), C.gdouble(proportion), C.GstClockTimeDiff(diff), - C.GstClockTime(durationToClockTime(timestamp)), + C.GstClockTime(timestamp.Nanoseconds()), )) } diff --git a/gst/gst_init.go b/gst/gst_init.go index 5681ea6..9959981 100644 --- a/gst/gst_init.go +++ b/gst/gst_init.go @@ -4,6 +4,10 @@ package gst import "C" import "unsafe" +// CAT is the global DebugCategory used for logging from the bindings. It is okay to use +// it from applications, but plugins should use their own. +var CAT *DebugCategory + // Init is a wrapper around gst_init() and must be called before any // other gstreamer calls and is used to initialize everything necessary. // In addition to setting up gstreamer for usage, a pointer to a slice of @@ -11,6 +15,10 @@ import "unsafe" // args will be modified to remove any flags that were handled. // Alternatively, nil may be passed in to not perform any command line // parsing. +// +// The bindings will also set up their own internal DebugCategory for logging +// than can be invoked from applications or plugins as well. However, for +// plugins it is generally better to initialize your own DebugCategory. func Init(args *[]string) { if args != nil { argc := C.int(len(*args)) @@ -29,4 +37,5 @@ func Init(args *[]string) { } else { C.gst_init(nil, nil) } + CAT = NewDebugCategory("GST_GO", DebugColorNone, "GStreamer Go Bindings") } diff --git a/gst/gst_mapinfo.go b/gst/gst_mapinfo.go index a2cf471..d9eaf45 100644 --- a/gst/gst_mapinfo.go +++ b/gst/gst_mapinfo.go @@ -2,11 +2,17 @@ package gst /* #include "gst.go.h" + +void memcpy_offset (void * dest, guint offset, const void * src, size_t n) { memcpy(dest + offset, src, n); } + */ import "C" import ( + "bytes" "encoding/binary" + "errors" + "io" "unsafe" ) @@ -20,11 +26,45 @@ func (m *MapInfo) Instance() *C.GstMapInfo { return m.ptr } +// Reader returns a Reader for the contents of this map's memory. +func (m *MapInfo) Reader() io.Reader { + return bytes.NewReader(m.Bytes()) +} + +type mapInfoWriter struct { + mapInfo *MapInfo + wsize int +} + +func (m *mapInfoWriter) Write(p []byte) (int, error) { + if m.wsize+len(p) > int(m.mapInfo.Size()) { + return 0, errors.New("Attempt to write more data than allocated to MapInfo") + } + m.mapInfo.WriteAt(m.wsize, p) + m.wsize += len(p) + return len(p), nil +} + +// Writer returns a writer that will copy the contents passed to Write directly to the +// map's memory sequentially. An error will be returned if an attempt is made to write +// more data than the map can hold. +func (m *MapInfo) Writer() io.Writer { + return &mapInfoWriter{ + mapInfo: m, + wsize: 0, + } +} + // WriteData writes the given sequence directly to the map's memory. func (m *MapInfo) WriteData(data []byte) { C.memcpy(unsafe.Pointer(m.ptr.data), unsafe.Pointer(&data[0]), C.gsize(len(data))) } +// WriteAt writes the given data sequence directly to the map's memory at the given offset. +func (m *MapInfo) WriteAt(offset int, data []byte) { + C.memcpy_offset(unsafe.Pointer(m.ptr.data), C.guint(offset), unsafe.Pointer(&data[0]), C.gsize(len(data))) +} + // Memory returns the underlying memory object. func (m *MapInfo) Memory() *Memory { return wrapMemory(m.ptr.memory) diff --git a/gst/gst_memory.go b/gst/gst_memory.go index 958eae0..32494fe 100644 --- a/gst/gst_memory.go +++ b/gst/gst_memory.go @@ -8,7 +8,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Memory is a go representation of GstMemory. This object is implemented in a read-only fashion diff --git a/gst/gst_message.go b/gst/gst_message.go index f16ac8d..b2d53ce 100644 --- a/gst/gst_message.go +++ b/gst/gst_message.go @@ -8,7 +8,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Message is a Go wrapper around a GstMessage. It provides convenience methods for @@ -170,7 +170,7 @@ func (m *Message) ParseStreamStatus() (StreamStatusType, *Element) { func (m *Message) ParseAsyncDone() time.Duration { var clockTime C.GstClockTime C.gst_message_parse_async_done((*C.GstMessage)(m.Instance()), &clockTime) - return clockTimeToDuration(ClockTime(clockTime)) + return time.Duration(clockTime) } // BufferingStats represents the buffering stats as retrieved from a GST_MESSAGE_TYPE_BUFFERING. @@ -339,7 +339,7 @@ type QoSValues struct { // The stream time of the buffer that generated the message StreamTime time.Duration // The timestamps of the buffer that generated the message - Timestamp ClockTime + Timestamp time.Duration // The duration of the buffer that generated the message Duration time.Duration } @@ -356,10 +356,10 @@ func (m *Message) ParseQoS() *QoSValues { ) return &QoSValues{ Live: gobool(live), - RunningTime: guint64ToDuration(runningTime), - StreamTime: guint64ToDuration(streamTime), - Timestamp: ClockTime(timestamp), - Duration: guint64ToDuration(duration), + RunningTime: time.Duration(runningTime), + StreamTime: time.Duration(streamTime), + Timestamp: time.Duration(timestamp), + Duration: time.Duration(duration), } } @@ -385,7 +385,7 @@ func (m *Message) ParseProgress() (progressType ProgressType, code, text string) func (m *Message) ParseResetTime() time.Duration { var clockTime C.GstClockTime C.gst_message_parse_reset_time((*C.GstMessage)(m.Instance()), &clockTime) - return clockTimeToDuration(ClockTime(clockTime)) + return time.Duration(clockTime) } // ParseDeviceAdded parses a device-added message. The device-added message is diff --git a/gst/gst_message_constructors.go b/gst/gst_message_constructors.go index 6a7161a..6077c61 100644 --- a/gst/gst_message_constructors.go +++ b/gst/gst_message_constructors.go @@ -6,7 +6,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) func getMessageSourceObj(src interface{}) *C.GstObject { @@ -41,9 +41,9 @@ func NewAsyncDoneMessage(src interface{}, runningTime time.Duration) *Message { } var cTime C.GstClockTime if runningTime.Nanoseconds() < 0 { - cTime = C.GstClockTime(ClockTimeNone) + cTime = C.GstClockTime(gstClockTimeNone) } else { - cTime = C.GstClockTime(durationToClockTime(runningTime)) + cTime = C.GstClockTime(runningTime.Nanoseconds()) } return wrapMessage(C.gst_message_new_async_done( srcObj, @@ -312,7 +312,7 @@ func NewPropertyNotifyMessage(src interface{}, propName string, val interface{}) return wrapMessage(C.gst_message_new_property_notify( srcObj, cName, - (*C.GValue)(gVal.Native()), + (*C.GValue)(unsafe.Pointer(gVal.GValue)), )) } @@ -332,10 +332,10 @@ func NewQoSMessage(src interface{}, live bool, runningTime, streamTime, timestam return wrapMessage(C.gst_message_new_qos( srcObj, gboolean(live), - C.guint64(durationToClockTime(runningTime)), - C.guint64(durationToClockTime(streamTime)), - C.guint64(durationToClockTime(timestamp)), - C.guint64(durationToClockTime(duration)), + C.guint64((runningTime.Nanoseconds())), + C.guint64((streamTime.Nanoseconds())), + C.guint64((timestamp.Nanoseconds())), + C.guint64((duration.Nanoseconds())), )) } @@ -410,7 +410,7 @@ func NewResetTimeMessage(src interface{}, runningTime time.Duration) *Message { if srcObj == nil { return nil } - return wrapMessage(C.gst_message_new_reset_time(srcObj, C.GstClockTime(durationToClockTime(runningTime)))) + return wrapMessage(C.gst_message_new_reset_time(srcObj, C.GstClockTime(runningTime.Nanoseconds()))) } // NewSegmentDoneMessage creates a new segment done message. This message is posted by elements that finish playback of a segment as a result of a @@ -479,7 +479,7 @@ func NewStepDoneMessage(src interface{}, format Format, amount uint64, rate floa C.gdouble(rate), gboolean(flush), gboolean(intermediate), - C.guint64(durationToClockTime(duration)), + C.guint64(duration.Nanoseconds()), gboolean(eos), )) } diff --git a/gst/gst_meta.go b/gst/gst_meta.go index de084aa..9135747 100644 --- a/gst/gst_meta.go +++ b/gst/gst_meta.go @@ -27,7 +27,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Meta is a go representation of GstMeta. diff --git a/gst/gst_mini_object.go b/gst/gst_mini_object.go index 5706c33..eefaf9f 100644 --- a/gst/gst_mini_object.go +++ b/gst/gst_mini_object.go @@ -6,7 +6,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // MiniObject is an opaque struct meant to form the base of gstreamer diff --git a/gst/gst_object.go b/gst/gst_object.go index 9ed657c..3906824 100644 --- a/gst/gst_object.go +++ b/gst/gst_object.go @@ -6,7 +6,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Object is a go representation of a GstObject. diff --git a/gst/gst_pad.go b/gst/gst_pad.go index 6a2e5c6..11e8e2a 100644 --- a/gst/gst_pad.go +++ b/gst/gst_pad.go @@ -35,7 +35,7 @@ import ( "errors" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" gopointer "github.com/mattn/go-pointer" ) diff --git a/gst/gst_pad_template.go b/gst/gst_pad_template.go index 1114dab..77c0c86 100644 --- a/gst/gst_pad_template.go +++ b/gst/gst_pad_template.go @@ -5,7 +5,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // PadTemplate is a go representation of a GstPadTemplate diff --git a/gst/gst_pipeline.go b/gst/gst_pipeline.go index f238657..86929d9 100644 --- a/gst/gst_pipeline.go +++ b/gst/gst_pipeline.go @@ -9,7 +9,7 @@ import ( "strings" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Pipeline is a go implementation of a GstPipeline. diff --git a/gst/gst_plugin.go b/gst/gst_plugin.go index c38087b..ea461cb 100644 --- a/gst/gst_plugin.go +++ b/gst/gst_plugin.go @@ -16,9 +16,20 @@ gboolean cgoGlobalPluginInit(GstPlugin * plugin) return goGlobalPluginInit(plugin); } -GstPluginDesc * exportPluginMeta (gint major, gint minor, gchar * name, gchar * description, GstPluginInitFunc init, gchar * version, gchar * license, gchar * source, gchar * package, gchar * origin, gchar * release_datetime) +GstPluginDesc * getPluginMeta (gint major, + gint minor, + gchar * name, + gchar * description, + GstPluginInitFunc init, + gchar * version, + gchar * license, + gchar * source, + gchar * package, + gchar * origin, + gchar * release_datetime) { - GstPluginDesc * desc = malloc ( sizeof(GstPluginDesc) ); + + GstPluginDesc * desc = malloc ( sizeof (GstPluginDesc) ); desc->major_version = major; desc->minor_version = minor; @@ -42,7 +53,7 @@ import ( "errors" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" gopointer "github.com/mattn/go-pointer" ) @@ -80,10 +91,10 @@ var globalPluginInit PluginInitFunc // Export will export the PluginMetadata to an unsafe pointer to a GstPluginDesc. func (p *PluginMetadata) Export() unsafe.Pointer { globalPluginInit = p.Init - desc := C.exportPluginMeta( + desc := C.getPluginMeta( C.gint(p.MajorVersion), C.gint(p.MinorVersion), - (*C.gchar)(C.CString(p.Name)), + (*C.gchar)(unsafe.Pointer(&[]byte(p.Name)[0])), (*C.gchar)(C.CString(p.Description)), (C.GstPluginInitFunc(C.cgoGlobalPluginInit)), (*C.gchar)(C.CString(p.Version)), diff --git a/gst/gst_plugin_feature.go b/gst/gst_plugin_feature.go index 954da71..60b63db 100644 --- a/gst/gst_plugin_feature.go +++ b/gst/gst_plugin_feature.go @@ -6,7 +6,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // PluginFeature wraps the C GstPluginFeature. diff --git a/gst/gst_query.go b/gst/gst_query.go index 5bde9cc..e12123c 100644 --- a/gst/gst_query.go +++ b/gst/gst_query.go @@ -6,7 +6,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Query is a go wrapper around a GstQuery. @@ -332,7 +332,7 @@ func (q *Query) ParseLatency() (live bool, minLatency, maxLatency time.Duration) var min, max C.GstClockTime var gl C.gboolean C.gst_query_parse_latency(q.Instance(), &gl, &min, &max) - return gobool(gl), clockTimeToDuration(ClockTime(min)), clockTimeToDuration(ClockTime(max)) + return gobool(gl), time.Duration(min), time.Duration(max) } // ParseNumFormats parses the number of formats in the formats query. @@ -517,7 +517,7 @@ func (q *Query) SetFormats(formats ...Format) { // SetLatency answers a latency query by setting the requested values in the given format. func (q *Query) SetLatency(live bool, minLatency, maxLatency time.Duration) { - C.gst_query_set_latency(q.Instance(), gboolean(live), C.GstClockTime(durationToClockTime(minLatency)), C.GstClockTime(durationToClockTime(maxLatency))) + C.gst_query_set_latency(q.Instance(), gboolean(live), C.guint64(minLatency.Nanoseconds()), C.guint64(maxLatency.Nanoseconds())) } // SetAllocationParamAt sets allocation params in query. diff --git a/gst/gst_registry.go b/gst/gst_registry.go index e002281..e6a88bf 100644 --- a/gst/gst_registry.go +++ b/gst/gst_registry.go @@ -7,7 +7,7 @@ import ( "fmt" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Registry is a go representation of a GstRegistry. diff --git a/gst/gst_stream.go b/gst/gst_stream.go index 9153f09..ee0a301 100644 --- a/gst/gst_stream.go +++ b/gst/gst_stream.go @@ -6,7 +6,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // Stream is a Go representation of a GstStream. diff --git a/gst/gst_stream_collection.go b/gst/gst_stream_collection.go index 3758f7b..38df3df 100644 --- a/gst/gst_stream_collection.go +++ b/gst/gst_stream_collection.go @@ -7,7 +7,7 @@ import ( "fmt" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // StreamCollection is a Go representation of a GstStreamCollection. diff --git a/gst/gst_structure.go b/gst/gst_structure.go index db10d61..a9b5d22 100644 --- a/gst/gst_structure.go +++ b/gst/gst_structure.go @@ -20,8 +20,8 @@ import ( "sync" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // Structure is a go implementation of a C GstStructure. @@ -52,7 +52,7 @@ func NewStructureFromString(stStr string) *Structure { // StructureFromGValue extracts the GstStructure from a glib.Value, or nil // if one does not exist. func StructureFromGValue(gval *glib.Value) *Structure { - st := C.gst_value_get_structure((*C.GValue)(gval.Native())) + st := C.gst_value_get_structure((*C.GValue)(unsafe.Pointer(gval.GValue))) if st == nil { return nil } diff --git a/gst/gst_system_clock.go b/gst/gst_system_clock.go index 5d65d0c..5589cbd 100644 --- a/gst/gst_system_clock.go +++ b/gst/gst_system_clock.go @@ -5,7 +5,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // SystemClock wraps GstSystemClock diff --git a/gst/gst_tag_list.go b/gst/gst_tag_list.go index 0f77fb7..660a340 100644 --- a/gst/gst_tag_list.go +++ b/gst/gst_tag_list.go @@ -17,8 +17,8 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" gopointer "github.com/mattn/go-pointer" + "github.com/tinyzimmer/go-glib/glib" ) // TagList is a go wrapper around a GstTagList. For now, until the rest of the methods are @@ -86,7 +86,7 @@ func (t *TagList) AddValue(mergeMode TagMergeMode, tag Tag, value interface{}) { t.Instance(), C.GstTagMergeMode(mergeMode), (*C.gchar)(ctag), - (*C.GValue)(gVal.Native()), + (*C.GValue)(unsafe.Pointer(gVal.GValue)), ) } diff --git a/gst/gst_tag_setter.go b/gst/gst_tag_setter.go index 346283f..53deca1 100644 --- a/gst/gst_tag_setter.go +++ b/gst/gst_tag_setter.go @@ -5,7 +5,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // InterfaceTagSetter represents the GstTagsetter interface GType. Use this when querying bins @@ -57,7 +57,7 @@ func (t *gstTagSetter) AddTagValue(mergeMode TagMergeMode, tagKey Tag, tagValue t.Instance(), C.GstTagMergeMode(mergeMode), (*C.gchar)(unsafe.Pointer(ckey)), - (*C.GValue)(gVal.Native()), + (*C.GValue)(unsafe.Pointer(gVal.GValue)), ) } diff --git a/gst/gst_toc_setter.go b/gst/gst_toc_setter.go index 1ec432d..6b368cd 100644 --- a/gst/gst_toc_setter.go +++ b/gst/gst_toc_setter.go @@ -2,7 +2,7 @@ package gst // #include "gst.go.h" import "C" -import "github.com/gotk3/gotk3/glib" +import "github.com/tinyzimmer/go-glib/glib" // InterfaceTOCSetter represents the GstTocSetter interface GType. Use this when querying bins // for elements that implement a TOCSetter. diff --git a/gst/gst_uri_handler.go b/gst/gst_uri_handler.go index 6543e26..0d30983 100644 --- a/gst/gst_uri_handler.go +++ b/gst/gst_uri_handler.go @@ -8,7 +8,7 @@ import ( "errors" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) // InterfaceURIHandler represents the GstURIHandler interface GType. Use this when querying bins diff --git a/gst/gst_wrappers.go b/gst/gst_wrappers.go index 6c0aab4..0a01f5c 100644 --- a/gst/gst_wrappers.go +++ b/gst/gst_wrappers.go @@ -2,17 +2,85 @@ package gst /* #include "gst.go.h" + +GValue * toGValue (guintptr p) { return (GValue*)(p); } */ import "C" import ( - "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" ) -func init() { +func init() { registerMarshalers() } + +// Object wrappers + +func wrapAllocator(obj *glib.Object) *Allocator { return &Allocator{wrapObject(obj)} } +func wrapBin(obj *glib.Object) *Bin { return &Bin{wrapElement(obj)} } +func wrapBuffer(buf *C.GstBuffer) *Buffer { return &Buffer{ptr: buf} } +func wrapBufferList(bufList *C.GstBufferList) *BufferList { return &BufferList{ptr: bufList} } +func wrapBufferPool(obj *glib.Object) *BufferPool { return &BufferPool{wrapObject(obj)} } +func wrapBus(obj *glib.Object) *Bus { return &Bus{Object: wrapObject(obj)} } +func wrapCaps(caps *C.GstCaps) *Caps { return &Caps{native: caps} } +func wrapClock(obj *glib.Object) *Clock { return &Clock{wrapObject(obj)} } +func wrapContext(ctx *C.GstContext) *Context { return &Context{ptr: ctx} } +func wrapDevice(obj *glib.Object) *Device { return &Device{wrapObject(obj)} } +func wrapElement(obj *glib.Object) *Element { return &Element{wrapObject(obj)} } +func wrapEvent(ev *C.GstEvent) *Event { return &Event{ptr: ev} } +func wrapGhostPad(obj *glib.Object) *GhostPad { return &GhostPad{wrapProxyPad(obj)} } +func wrapMainContext(ctx *C.GMainContext) *MainContext { return &MainContext{ptr: ctx} } +func wrapMainLoop(loop *C.GMainLoop) *MainLoop { return &MainLoop{ptr: loop} } +func wrapMapInfo(mapInfo *C.GstMapInfo) *MapInfo { return &MapInfo{ptr: mapInfo} } +func wrapMemory(mem *C.GstMemory) *Memory { return &Memory{ptr: mem} } +func wrapMessage(msg *C.GstMessage) *Message { return &Message{msg: msg} } +func wrapMeta(meta *C.GstMeta) *Meta { return &Meta{ptr: meta} } +func wrapMetaInfo(info *C.GstMetaInfo) *MetaInfo { return &MetaInfo{ptr: info} } +func wrapPad(obj *glib.Object) *Pad { return &Pad{wrapObject(obj)} } +func wrapPadTemplate(obj *glib.Object) *PadTemplate { return &PadTemplate{wrapObject(obj)} } +func wrapPipeline(obj *glib.Object) *Pipeline { return &Pipeline{Bin: wrapBin(obj)} } +func wrapPluginFeature(obj *glib.Object) *PluginFeature { return &PluginFeature{wrapObject(obj)} } +func wrapPlugin(obj *glib.Object) *Plugin { return &Plugin{wrapObject(obj)} } +func wrapProxyPad(obj *glib.Object) *ProxyPad { return &ProxyPad{wrapPad(obj)} } +func wrapQuery(query *C.GstQuery) *Query { return &Query{ptr: query} } +func wrapRegistry(obj *glib.Object) *Registry { return &Registry{wrapObject(obj)} } +func wrapSample(sample *C.GstSample) *Sample { return &Sample{sample: sample} } +func wrapSegment(segment *C.GstSegment) *Segment { return &Segment{ptr: segment} } +func wrapStream(obj *glib.Object) *Stream { return &Stream{wrapObject(obj)} } +func wrapTagList(tagList *C.GstTagList) *TagList { return &TagList{ptr: tagList} } +func wrapTOC(toc *C.GstToc) *TOC { return &TOC{ptr: toc} } +func wrapTOCEntry(toc *C.GstTocEntry) *TOCEntry { return &TOCEntry{ptr: toc} } + +func wrapCapsFeatures(features *C.GstCapsFeatures) *CapsFeatures { + return &CapsFeatures{native: features} +} + +func wrapObject(obj *glib.Object) *Object { + return &Object{InitiallyUnowned: &glib.InitiallyUnowned{Object: obj}} +} + +func wrapElementFactory(obj *glib.Object) *ElementFactory { + return &ElementFactory{wrapPluginFeature(obj)} +} + +func wrapStreamCollection(obj *glib.Object) *StreamCollection { + return &StreamCollection{wrapObject(obj)} +} + +func wrapAllocationParams(obj *C.GstAllocationParams) *AllocationParams { + return &AllocationParams{ptr: obj} +} + +func wrapElementClass(klass C.gpointer) *ElementClass { + return &ElementClass{&ObjectClass{ptr: C.toGObjectClass(unsafe.Pointer(klass))}} +} + +// Marshallers + +func uintptrToGVal(p uintptr) *C.GValue { return (*C.GValue)(C.toGValue(C.guintptr(p))) } + +func registerMarshalers() { tm := []glib.TypeMarshaler{ { T: glib.Type(C.gst_buffering_mode_get_type()), @@ -147,92 +215,6 @@ func init() { glib.RegisterGValueMarshalers(tm) } -func uintptrToGVal(p uintptr) *C.GValue { - return (*C.GValue)(unsafe.Pointer(p)) // vet thinks this is unsafe and there is no way around it for now. - // but the given ptr is an address to a C object so go's concerns are misplaced. -} - -// Object wrappers - -func wrapAllocator(obj *glib.Object) *Allocator { return &Allocator{wrapObject(obj)} } -func wrapBin(obj *glib.Object) *Bin { return &Bin{wrapElement(obj)} } -func wrapBuffer(buf *C.GstBuffer) *Buffer { return &Buffer{ptr: buf} } -func wrapBufferList(bufList *C.GstBufferList) *BufferList { return &BufferList{ptr: bufList} } -func wrapBufferPool(obj *glib.Object) *BufferPool { return &BufferPool{wrapObject(obj)} } -func wrapBus(obj *glib.Object) *Bus { return &Bus{Object: wrapObject(obj)} } -func wrapCaps(caps *C.GstCaps) *Caps { return &Caps{native: caps} } -func wrapClock(obj *glib.Object) *Clock { return &Clock{wrapObject(obj)} } -func wrapContext(ctx *C.GstContext) *Context { return &Context{ptr: ctx} } -func wrapDevice(obj *glib.Object) *Device { return &Device{wrapObject(obj)} } -func wrapElement(obj *glib.Object) *Element { return &Element{wrapObject(obj)} } -func wrapEvent(ev *C.GstEvent) *Event { return &Event{ptr: ev} } -func wrapGhostPad(obj *glib.Object) *GhostPad { return &GhostPad{wrapProxyPad(obj)} } -func wrapMainContext(ctx *C.GMainContext) *MainContext { return &MainContext{ptr: ctx} } -func wrapMainLoop(loop *C.GMainLoop) *MainLoop { return &MainLoop{ptr: loop} } -func wrapMapInfo(mapInfo *C.GstMapInfo) *MapInfo { return &MapInfo{ptr: mapInfo} } -func wrapMemory(mem *C.GstMemory) *Memory { return &Memory{ptr: mem} } -func wrapMessage(msg *C.GstMessage) *Message { return &Message{msg: msg} } -func wrapMeta(meta *C.GstMeta) *Meta { return &Meta{ptr: meta} } -func wrapMetaInfo(info *C.GstMetaInfo) *MetaInfo { return &MetaInfo{ptr: info} } -func wrapPad(obj *glib.Object) *Pad { return &Pad{wrapObject(obj)} } -func wrapPadTemplate(obj *glib.Object) *PadTemplate { return &PadTemplate{wrapObject(obj)} } -func wrapPipeline(obj *glib.Object) *Pipeline { return &Pipeline{Bin: wrapBin(obj)} } -func wrapPluginFeature(obj *glib.Object) *PluginFeature { return &PluginFeature{wrapObject(obj)} } -func wrapPlugin(obj *glib.Object) *Plugin { return &Plugin{wrapObject(obj)} } -func wrapProxyPad(obj *glib.Object) *ProxyPad { return &ProxyPad{wrapPad(obj)} } -func wrapQuery(query *C.GstQuery) *Query { return &Query{ptr: query} } -func wrapRegistry(obj *glib.Object) *Registry { return &Registry{wrapObject(obj)} } -func wrapSample(sample *C.GstSample) *Sample { return &Sample{sample: sample} } -func wrapSegment(segment *C.GstSegment) *Segment { return &Segment{ptr: segment} } -func wrapStream(obj *glib.Object) *Stream { return &Stream{wrapObject(obj)} } -func wrapTagList(tagList *C.GstTagList) *TagList { return &TagList{ptr: tagList} } -func wrapTOC(toc *C.GstToc) *TOC { return &TOC{ptr: toc} } -func wrapTOCEntry(toc *C.GstTocEntry) *TOCEntry { return &TOCEntry{ptr: toc} } - -func wrapCapsFeatures(features *C.GstCapsFeatures) *CapsFeatures { - return &CapsFeatures{native: features} -} - -func wrapObject(obj *glib.Object) *Object { - return &Object{InitiallyUnowned: &glib.InitiallyUnowned{Object: obj}} -} - -func wrapElementFactory(obj *glib.Object) *ElementFactory { - return &ElementFactory{wrapPluginFeature(obj)} -} - -func wrapStreamCollection(obj *glib.Object) *StreamCollection { - return &StreamCollection{wrapObject(obj)} -} - -func wrapAllocationParams(obj *C.GstAllocationParams) *AllocationParams { - return &AllocationParams{ptr: obj} -} - -func wrapElementClass(klass C.gpointer) *ElementClass { - return &ElementClass{&ObjectClass{ptr: C.toGObjectClass(unsafe.Pointer(klass))}} -} - -// Clock wrappers - -func clockTimeToDuration(n ClockTime) time.Duration { - if n == ClockTimeNone { - return time.Duration(-1) - } - return time.Duration(uint64(n)) * time.Nanosecond -} - -func guint64ToDuration(n C.guint64) time.Duration { return clockTimeToDuration(ClockTime(n)) } - -func durationToClockTime(dur time.Duration) ClockTime { - if dur.Nanoseconds() < 0 { - return ClockTimeNone - } - return ClockTime(dur.Nanoseconds()) -} - -// Marshallers - func marshalBufferingMode(p uintptr) (interface{}, error) { c := C.g_value_get_enum(uintptrToGVal(p)) return BufferingMode(c), nil diff --git a/gst/interfaces.go b/gst/interfaces.go index 3f78547..5ab5315 100644 --- a/gst/interfaces.go +++ b/gst/interfaces.go @@ -15,13 +15,13 @@ import ( "reflect" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" gopointer "github.com/mattn/go-pointer" ) // GoElement is an interface to be implemented by GStreamer elements built using the // go bindings. Select methods from other interfaces can be overridden and declared via -// the Extendable properties. +// the Extendable property provided at plugin registration. // // Typically, at the very least, an element will want to implement methods from the Element // Extendable (and by extension the GoObject). @@ -68,7 +68,6 @@ type classData struct { func gtypeForGoElement(name string, elem GoElement, extendable Extendable) C.GType { registerMutex.Lock() defer registerMutex.Unlock() - // fmt.Printf("Checking registration of %v\n", reflect.TypeOf(elem).String()) if registered, ok := registeredTypes[reflect.TypeOf(elem).String()]; ok { return registered } @@ -96,7 +95,6 @@ func gtypeForGoElement(name string, elem GoElement, extendable Extendable) C.GTy C.GTypeFlags(0), ) elem.TypeInit(&TypeInstance{gtype: gtype, gotype: elem}) - // fmt.Printf("Registering %v to type %v\n", reflect.TypeOf(elem).String(), gtype) registeredTypes[reflect.TypeOf(elem).String()] = gtype return gtype } diff --git a/gst/pbutils/discoverer.go b/gst/pbutils/discoverer.go index 1a8e649..e4ddbe1 100644 --- a/gst/pbutils/discoverer.go +++ b/gst/pbutils/discoverer.go @@ -8,7 +8,7 @@ import ( "time" "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" "github.com/tinyzimmer/go-gst/gst" ) diff --git a/gst/pkg_config.go b/gst/pkg_config.go index 29e76e4..8c292c7 100644 --- a/gst/pkg_config.go +++ b/gst/pkg_config.go @@ -2,7 +2,7 @@ package gst /* #cgo pkg-config: gstreamer-1.0 -#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall +#cgo CFLAGS: -Wno-deprecated-declarations -Wno-format-security -g -Wall #cgo LDFLAGS: -lm */ import "C" diff --git a/gst/video/gst_color_balance.go b/gst/video/gst_color_balance.go index 979c5af..bd3dc86 100644 --- a/gst/video/gst_color_balance.go +++ b/gst/video/gst_color_balance.go @@ -13,7 +13,7 @@ import "C" import ( "unsafe" - "github.com/gotk3/gotk3/glib" + "github.com/tinyzimmer/go-glib/glib" "github.com/tinyzimmer/go-gst/gst" ) diff --git a/gst/video/gst_video_info.go b/gst/video/gst_video_info.go index 4ada818..d96196f 100644 --- a/gst/video/gst_video_info.go +++ b/gst/video/gst_video_info.go @@ -35,7 +35,6 @@ gint infoWidth (GstVideoInfo * info) */ import "C" import ( - "runtime" "unsafe" "github.com/tinyzimmer/go-gst/gst" @@ -164,10 +163,13 @@ type Info struct { func wrapInfo(vinfo *C.GstVideoInfo) *Info { info := &Info{vinfo} - runtime.SetFinalizer(info, func(i *Info) { C.gst_video_info_free(vinfo) }) return info } +func (i *Info) Free() { + C.gst_video_info_free(i.instance()) +} + // instance returns the underlying GstVideoInfo instance. func (i *Info) instance() *C.GstVideoInfo { return i.ptr }