diff --git a/examples/plugins/Makefile b/examples/plugins/Makefile new file mode 100644 index 0000000..1a2fe8c --- /dev/null +++ b/examples/plugins/Makefile @@ -0,0 +1,10 @@ +PLUGINS ?= $(patsubst %/,%,$(sort $(dir $(wildcard */)))) + +.PHONY: $(PLUGINS) +$(PLUGINS): + cd $@ && \ + go build -o ../libgstgo$@.so -buildmode c-shared . + rm libgstgo$@.h + +clean: + rm -f *.so *.h \ No newline at end of file diff --git a/examples/plugins/filesrc/filesrc.go b/examples/plugins/filesrc/filesrc.go index aa62852..53cf25e 100644 --- a/examples/plugins/filesrc/filesrc.go +++ b/examples/plugins/filesrc/filesrc.go @@ -90,7 +90,8 @@ func (f *fileSrc) setLocation(path string) error { } // The ObjectSubclass implementations below are for registering the various aspects of our -// element and its capabilities with the type system. +// element and its capabilities with the type system. These are the minimum methods that +// should be implemented by an element. // Every element needs to provide its own constructor that returns an initialized // gst.GoElement implementation. Here we simply create a new fileSrc with zeroed settings @@ -132,9 +133,12 @@ func (f *fileSrc) ClassInit(klass *gst.ElementClass) { klass.InstallProperties(properties) } -// Object implementations are used during the initialization of element. The -// methods are called once the obejct is constructed and its properties are read -// and written to. +// Object implementations are used during the initialization of an element. The +// methods are called once the object is constructed and its properties are read +// and written to. These and the rest of the methods described below are documented +// in interfaces in the bindings, however only individual methods needs from those +// interfaces need to be implemented. When left unimplemented, the behavior of the parent +// class is inherited. // SetProperty is called when a `value` is set to the property at index `id` in the // properties slice that we installed during ClassInit. It should attempt to register diff --git a/gst/base/gst_base_src_impl.go b/gst/base/gst_base_src_impl.go index c76956b..ea56f84 100644 --- a/gst/base/gst_base_src_impl.go +++ b/gst/base/gst_base_src_impl.go @@ -56,7 +56,7 @@ import ( var ( // ExtendsBaseSrc is an Extendable for extending a GstBaseSrc - ExtendsBaseSrc gst.Extendable = &extendsBaseSrc{} + ExtendsBaseSrc gst.Extendable = &extendsBaseSrc{parent: gst.ExtendsElement} ) // GstBaseSrcImpl is the documented interface for an element extending a GstBaseSrc. It does not have to @@ -109,7 +109,7 @@ type GstBaseSrcImpl interface { Fill(self *GstBaseSrc, offset uint64, size uint, buffer *gst.Buffer) gst.FlowReturn } -type extendsBaseSrc struct{} +type extendsBaseSrc struct{ parent gst.Extendable } func (e *extendsBaseSrc) Type() glib.Type { return glib.Type(C.gst_base_src_get_type()) } func (e *extendsBaseSrc) ClassSize() int64 { return int64(C.sizeof_GstBaseSrcClass) } @@ -118,6 +118,8 @@ func (e *extendsBaseSrc) InstanceSize() int64 { return int64(C.sizeof_GstBaseSrc // InitClass iterates the methods provided by the element and overrides any provided // in the virtual methods. func (e *extendsBaseSrc) InitClass(klass unsafe.Pointer, elem gst.GoElement) { + e.parent.InitClass(klass, elem) + class := C.toGstBaseSrcClass(klass) if _, ok := elem.(interface { diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index d803ffc..ae74fa7 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -258,6 +258,7 @@ func goClassInit(klass C.gpointer, klassData C.gpointer) { data.ext.InitClass(unsafe.Pointer(klass), data.elem) C.g_type_class_add_private(klass, C.gsize(unsafe.Sizeof(uintptr(0)))) + data.elem.ClassInit(wrapElementClass(klass)) } @@ -310,13 +311,17 @@ func goURIHdlrSetURI(hdlr *C.GstURIHandler, uri *C.gchar, gerr **C.GError) C.gbo //export goObjectSetProperty func goObjectSetProperty(obj *C.GObject, propID C.guint, val *C.GValue, param *C.GParamSpec) { - iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)) + iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)).(interface { + SetProperty(obj *Object, id uint, value *glib.Value) + }) iface.SetProperty(wrapObject(toGObject(unsafe.Pointer(obj))), uint(propID-1), glib.ValueFromNative(unsafe.Pointer(val))) } //export goObjectGetProperty func goObjectGetProperty(obj *C.GObject, propID C.guint, value *C.GValue, param *C.GParamSpec) { - iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)) + iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)).(interface { + GetProperty(obj *Object, id uint) *glib.Value + }) val := iface.GetProperty(wrapObject(toGObject(unsafe.Pointer(obj))), uint(propID-1)) if val == nil { return @@ -326,7 +331,9 @@ func goObjectGetProperty(obj *C.GObject, propID C.guint, value *C.GValue, param //export goObjectConstructed func goObjectConstructed(obj *C.GObject) { - iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)) + iface := FromObjectUnsafePrivate(unsafe.Pointer(obj)).(interface { + Constructed(*Object) + }) iface.Constructed(wrapObject(toGObject(unsafe.Pointer(obj)))) } diff --git a/gst/g_object.go b/gst/g_object.go new file mode 100644 index 0000000..91a0780 --- /dev/null +++ b/gst/g_object.go @@ -0,0 +1,78 @@ +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); + +void objectFinalize (GObject * object) +{ + GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object))); + goObjectFinalize(object, G_OBJECT_GET_CLASS(object)); + parent->finalize(object); +} + +void objectConstructed (GObject * object) +{ + GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object))); + goObjectConstructed(object); + parent->constructed(object); +} + +void setGObjectClassSetProperty (void * klass) { ((GObjectClass *)klass)->set_property = goObjectSetProperty; } +void setGObjectClassGetProperty (void * klass) { ((GObjectClass *)klass)->get_property = goObjectGetProperty; } +void setGObjectClassConstructed (void * klass) { ((GObjectClass *)klass)->constructed = objectConstructed; } +void setGObjectClassFinalize (void * klass) { ((GObjectClass *)klass)->finalize = objectFinalize; } + +*/ +import "C" +import ( + "unsafe" + + "github.com/gotk3/gotk3/glib" +) + +// GoObject is an interface that abstracts on the GObject. In most 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 + // in the order it was registered. + SetProperty(obj *Object, id uint, value *glib.Value) + // GetProperty should retrieve the value of the property with the given id. ID is the index+1 of the parameter + // in the order it was registered. + GetProperty(obj *Object, id uint) *glib.Value + // Constructed is called when the Object has finished setting up. + Constructed(*Object) +} + +// ExtendsObject signifies a GoElement that extends a GObject. +var ExtendsObject Extendable = &extendObject{} + +type extendObject struct{} + +func (e *extendObject) Type() glib.Type { return glib.Type(C.g_object_get_type()) } +func (e *extendObject) ClassSize() int64 { return int64(C.sizeof_GObjectClass) } +func (e *extendObject) InstanceSize() int64 { return int64(C.sizeof_GObject) } + +func (e *extendObject) InitClass(klass unsafe.Pointer, elem GoElement) { + C.setGObjectClassFinalize(klass) + + if _, ok := elem.(interface { + SetProperty(obj *Object, id uint, value *glib.Value) + }); ok { + C.setGObjectClassSetProperty(klass) + } + if _, ok := elem.(interface { + GetProperty(obj *Object, id uint) *glib.Value + }); ok { + C.setGObjectClassGetProperty(klass) + } + if _, ok := elem.(interface { + Constructed(*Object) + }); ok { + C.setGObjectClassConstructed(klass) + } +} diff --git a/gst/g_parameter_spec.go b/gst/g_parameter_spec.go index 631cfa1..967fb60 100644 --- a/gst/g_parameter_spec.go +++ b/gst/g_parameter_spec.go @@ -11,9 +11,7 @@ import ( ) // ParameterSpec is a go representation of a C GParamSpec -type ParameterSpec struct { - paramSpec *C.GParamSpec -} +type ParameterSpec struct{ paramSpec *C.GParamSpec } // NewStringParameter returns a new ParameterSpec that will hold a string value. func NewStringParameter(name, nick, blurb string, defaultValue *string, flags ParameterFlags) *ParameterSpec { diff --git a/gst/gst_buffer.go b/gst/gst_buffer.go index 94874f4..579480e 100644 --- a/gst/gst_buffer.go +++ b/gst/gst_buffer.go @@ -598,6 +598,7 @@ func (b *Buffer) Unmap() { return } C.gst_buffer_unmap(b.Instance(), (*C.GstMapInfo)(b.mapInfo.Instance())) + C.free(unsafe.Pointer(b.mapInfo.Instance())) } // MapRange maps the info of length merged memory blocks starting at idx in buffer. diff --git a/gst/gst_debug.go b/gst/gst_debug.go index 19ed613..71591ea 100644 --- a/gst/gst_debug.go +++ b/gst/gst_debug.go @@ -19,6 +19,7 @@ import "C" import ( "path" "runtime" + "unsafe" ) // DebugColorFlags are terminal style flags you can use when creating your debugging @@ -98,14 +99,20 @@ func NewDebugCategory(name string, color DebugColorFlags, description string) *D func (d *DebugCategory) logDepth(level DebugLevel, message string, depth int, obj *C.GObject) { function, file, line, _ := runtime.Caller(depth) + cFile := C.CString(path.Base(file)) + cFunc := C.CString(runtime.FuncForPC(function).Name()) + cMsg := C.CString(message) + defer C.free(unsafe.Pointer(cFile)) + defer C.free(unsafe.Pointer(cFunc)) + defer C.free(unsafe.Pointer(cMsg)) C.cgoDebugLog( d.ptr, C.GstDebugLevel(level), - C.CString(path.Base(file)), - (C.CString(runtime.FuncForPC(function).Name())), + (*C.gchar)(cFile), + (*C.gchar)(cFunc), C.gint(line), obj, - C.CString(message), + (*C.gchar)(cMsg), ) } diff --git a/gst/gst_element.go b/gst/gst_element.go index c3d00d4..26867d6 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -384,3 +384,16 @@ func (e *Element) URIHandler() URIHandler { } return &gstURIHandler{ptr: e.Instance()} } + +// ExtendsElement signifies a GoElement that extends a GstElement. +var ExtendsElement Extendable = &extendElement{parent: ExtendsObject} + +type extendElement struct{ parent Extendable } + +func (e *extendElement) Type() glib.Type { return glib.Type(C.gst_element_get_type()) } +func (e *extendElement) ClassSize() int64 { return int64(C.sizeof_GstElementClass) } +func (e *extendElement) InstanceSize() int64 { return int64(C.sizeof_GstElement) } + +func (e *extendElement) InitClass(klass unsafe.Pointer, elem GoElement) { + e.parent.InitClass(klass, elem) +} diff --git a/gst/gst_memory.go b/gst/gst_memory.go index ecb2b6b..958eae0 100644 --- a/gst/gst_memory.go +++ b/gst/gst_memory.go @@ -109,6 +109,7 @@ func (m *Memory) Unmap() { return } C.gst_memory_unmap(m.Instance(), (*C.GstMapInfo)(m.mapInfo.Instance())) + C.free(unsafe.Pointer(m.mapInfo.Instance())) } // Bytes will return a byte slice of the data inside this memory if it is readable. diff --git a/gst/interfaces.go b/gst/interfaces.go index 9ee2857..3f78547 100644 --- a/gst/interfaces.go +++ b/gst/interfaces.go @@ -6,40 +6,8 @@ package gst extern void goClassInit (gpointer g_class, gpointer class_data); extern void goInstanceInit (GTypeInstance * instance, gpointer g_class); -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) -{ - GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object))); - goObjectFinalize(object, G_OBJECT_GET_CLASS(object)); - parent->finalize(object); -} - -void objectConstructed (GObject * object) -{ - GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object))); - goObjectConstructed(object); - parent->constructed(object); -} - -void cgoClassInit (gpointer g_class, gpointer class_data) -{ - ((GObjectClass *)g_class)->set_property = goObjectSetProperty; - ((GObjectClass *)g_class)->get_property = goObjectGetProperty; - ((GObjectClass *)g_class)->constructed = objectConstructed; - ((GObjectClass *)g_class)->finalize = objectFinalize; - - goClassInit(g_class, class_data); -} - -void cgoInstanceInit (GTypeInstance * instance, gpointer g_class) -{ - goInstanceInit(instance, g_class); -} - +void cgoClassInit (gpointer g_class, gpointer class_data) { goClassInit(g_class, class_data); } +void cgoInstanceInit (GTypeInstance * instance, gpointer g_class) { goInstanceInit(instance, g_class); } */ import "C" @@ -51,45 +19,28 @@ import ( 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. +// +// Typically, at the very least, an element will want to implement methods from the Element +// Extendable (and by extension the GoObject). +type GoElement interface{ GoObjectSubclass } + // Extendable is an interface implemented by extendable classes. It provides // the methods necessary to setup the vmethods on the object it represents. type Extendable interface { + // Type should return the type of the extended object Type() glib.Type + // ClasSize should return the size of the extended class ClassSize() int64 + // InstanceSize should return the size of the object itself InstanceSize() int64 + // InitClass should take a pointer to a new subclass and a GoElement and override any + // methods implemented by the GoElement in the subclass. InitClass(unsafe.Pointer, GoElement) } -type extendElement struct{} - -func (e *extendElement) Type() glib.Type { return glib.Type(C.gst_element_get_type()) } -func (e *extendElement) ClassSize() int64 { return int64(C.sizeof_GstElementClass) } -func (e *extendElement) InstanceSize() int64 { return int64(C.sizeof_GstElement) } -func (e *extendElement) InitClass(klass unsafe.Pointer, elem GoElement) {} - -// ExtendsElement signifies a GoElement that extends a GstElement. -var ExtendsElement Extendable = &extendElement{} - -// GoElement is an interface to be implemented by GStreamer elements built using the -// go bindings. The various methods are called throughout the lifecycle of the plugin. -type GoElement interface { - GoObjectSubclass - GoObject -} - -// privateFromObj returns the actual value of the address we stored in the object's private data. -func privateFromObj(obj unsafe.Pointer) unsafe.Pointer { - private := C.g_type_instance_get_private((*C.GTypeInstance)(obj), C.objectGType((*C.GObject)(obj))) - privAddr := (*unsafe.Pointer)(unsafe.Pointer(private)) - return *privAddr -} - -// FromObjectUnsafePrivate will return the GoElement addressed in the private data of the given GObject. -func FromObjectUnsafePrivate(obj unsafe.Pointer) GoElement { - ptr := gopointer.Restore(privateFromObj(obj)) - return ptr.(GoElement) -} - // GoObjectSubclass is an interface that abstracts on the GObjectClass. It should be implemented // by plugins using the go bindings. type GoObjectSubclass interface { @@ -99,21 +50,14 @@ type GoObjectSubclass interface { // element should add any interfaces it plans to implement. TypeInit(*TypeInstance) // ClassInit is called on the element after registering it with GStreamer. This is when the element - // should install any properties and pad templates it has. + // should install its properties and pad templates. ClassInit(*ElementClass) } -// GoObject is an interface that abstracts on the GObject. It should be implemented by plugins using -// the gobindings. -type GoObject interface { - // SetProperty should set the value of the property with the given id. ID is the index+1 of the parameter - // in the order it was registered. - SetProperty(obj *Object, id uint, value *glib.Value) - // GetProperty should retrieve the value of the property with the given id. ID is the index+1 of the parameter - // in the order it was registered. - GetProperty(obj *Object, id uint) *glib.Value - // Constructed is called when the Object has finished setting up. - Constructed(*Object) +// FromObjectUnsafePrivate will return the GoElement addressed in the private data of the given GObject. +func FromObjectUnsafePrivate(obj unsafe.Pointer) GoElement { + ptr := gopointer.Restore(privateFromObj(obj)) + return ptr.(GoElement) } type classData struct { @@ -156,3 +100,10 @@ func gtypeForGoElement(name string, elem GoElement, extendable Extendable) C.GTy registeredTypes[reflect.TypeOf(elem).String()] = gtype return gtype } + +// privateFromObj returns the actual value of the address we stored in the object's private data. +func privateFromObj(obj unsafe.Pointer) unsafe.Pointer { + private := C.g_type_instance_get_private((*C.GTypeInstance)(obj), C.objectGType((*C.GObject)(obj))) + privAddr := (*unsafe.Pointer)(unsafe.Pointer(private)) + return *privAddr +}