From 546d620440b12f13d48c45d8f48fd9bd2dd98332 Mon Sep 17 00:00:00 2001 From: Avi Zimmerman Date: Mon, 18 Jan 2021 08:50:34 +0200 Subject: [PATCH] pull in upstream interface improvements and add a few more gstreamer interfaces --- README.md | 3 +- cmd/gst-plugin-gen/main.go | 11 +- examples/playbin/main.go | 2 +- examples/plugins/boilerplate/boilerplate.go | 27 +++ examples/plugins/gobin/gobin.go | 27 +++ examples/plugins/gofilesink/filesink.go | 9 +- examples/plugins/gofilesrc/filesrc.go | 9 +- go.mod | 2 +- go.sum | 4 +- gst/gst.go.h | 2 + gst/gst_bin.go | 141 +++++++++++-- gst/gst_bin_exports.go | 91 ++++++++ gst/gst_bin_impl.go | 106 ++++++++++ gst/gst_child_proxy.go | 222 ++++++++++++++++++++ gst/gst_child_proxy_exports.go | 74 +++++++ gst/gst_device.go | 2 +- gst/gst_element.go | 192 +---------------- gst/gst_element_factory.go | 20 +- gst/gst_element_impl.go | 196 +++++++++++++++++ gst/gst_message_stringer.go | 6 +- gst/gst_object.go | 83 ++++---- gst/gst_tag_setter.go | 11 +- gst/gst_uri_handler.go | 21 +- 23 files changed, 969 insertions(+), 292 deletions(-) create mode 100644 examples/plugins/boilerplate/boilerplate.go create mode 100644 examples/plugins/gobin/gobin.go create mode 100644 gst/gst_bin_exports.go create mode 100644 gst/gst_bin_impl.go create mode 100644 gst/gst_child_proxy.go create mode 100644 gst/gst_child_proxy_exports.go create mode 100644 gst/gst_element_impl.go diff --git a/README.md b/README.md index 0023fa4..a05dacc 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ import ( "fmt" "os" + "github.com/tinyzimmer/go-glib/glib" "github.com/tinyzimmer/go-gst/gst" ) @@ -51,7 +52,7 @@ func main() { // Create a main loop. This is only required when utilizing signals via the bindings. // In this example, the AddWatch on the pipeline bus requires iterating on the main loop. - mainLoop := gst.NewMainLoop(gst.DefaultMainContext(), false) + mainLoop := glib.NewMainLoop(glib.MainContextDefault(), false) defer mainLoop.Unref() // Build a pipeline string from the cli arguments diff --git a/cmd/gst-plugin-gen/main.go b/cmd/gst-plugin-gen/main.go index 891ca1e..4ad8d67 100644 --- a/cmd/gst-plugin-gen/main.go +++ b/cmd/gst-plugin-gen/main.go @@ -47,7 +47,7 @@ type pluginConfig struct { } type elementConfig struct { - Name, Rank, Impl, Subclass string + Name, Rank, Impl, Subclass, Interfaces string } func buildCfgFromDir(dir string) *libraryConfig { @@ -119,6 +119,9 @@ var pluginTmpl = template.Must(template.New("").Funcs(template.FuncMap{ "adjustedName": func(name string) string { return strings.ToLower(strings.Replace(name, "-", "_", -1)) }, + "splitInterfaces": func(ifacesString string) []string { + return strings.Split(ifacesString, ",") + }, "extendsFromBase": func(subclass string) bool { if strings.HasPrefix(subclass, "base.") { return true @@ -163,6 +166,12 @@ var pluginMeta = &gst.PluginMetadata{ &{{ .Config.Element.Impl }}{}, // The base subclass this element extends {{ .Config.Element.Subclass }}, + {{- if .Config.Element.Interfaces }} + // The interfaces this element implements + {{- range $index, $interface := .Config.Element.Interfaces | splitInterfaces }} + {{ $interface }}, + {{- end }} + {{- end }} ) }, } diff --git a/examples/playbin/main.go b/examples/playbin/main.go index 1bdf399..a4ec904 100644 --- a/examples/playbin/main.go +++ b/examples/playbin/main.go @@ -60,7 +60,7 @@ func playbin(mainLoop *glib.MainLoop) error { // Watch state change events case gst.MessageStateChanged: if _, newState := msg.ParseStateChanged(); newState == gst.StatePlaying { - bin := gst.BinFromElement(playbin) + bin := gst.ToGstBin(playbin) // Generate a dot graph of the pipeline to GST_DEBUG_DUMP_DOT_DIR if defined bin.DebugBinToDotFile(gst.DebugGraphShowAll, "PLAYING") } diff --git a/examples/plugins/boilerplate/boilerplate.go b/examples/plugins/boilerplate/boilerplate.go new file mode 100644 index 0000000..cea4639 --- /dev/null +++ b/examples/plugins/boilerplate/boilerplate.go @@ -0,0 +1,27 @@ +//go:generate gst-plugin-gen +// +// +plugin:Name=boilerplate +// +plugin:Description=My plugin written in go +// +plugin:Version=v0.0.1 +// +plugin:License=gst.LicenseLGPL +// +plugin:Source=go-gst +// +plugin:Package=examples +// +plugin:Origin=https://github.com/tinyzimmer/go-gst +// +plugin:ReleaseDate=2021-01-18 +// +// +element:Name=myelement +// +element:Rank=gst.RankNone +// +element:Impl=myelement +// +element:Subclass=gst.ExtendsElement +// +package main + +import "github.com/tinyzimmer/go-glib/glib" + +func main() {} + +type myelement struct{} + +func (g *myelement) New() glib.GoObjectSubclass { return &myelement{} } + +func (g *myelement) ClassInit(klass *glib.ObjectClass) {} diff --git a/examples/plugins/gobin/gobin.go b/examples/plugins/gobin/gobin.go new file mode 100644 index 0000000..e0f4097 --- /dev/null +++ b/examples/plugins/gobin/gobin.go @@ -0,0 +1,27 @@ +//go:generate gst-plugin-gen +// +// +plugin:Name=gobin +// +plugin:Description=A bin element written in go +// +plugin:Version=v0.0.1 +// +plugin:License=gst.LicenseLGPL +// +plugin:Source=go-gst +// +plugin:Package=examples +// +plugin:Origin=https://github.com/tinyzimmer/go-gst +// +plugin:ReleaseDate=2021-01-18 +// +// +element:Name=gobin +// +element:Rank=gst.RankNone +// +element:Impl=gobin +// +element:Subclass=gst.ExtendsBin +// +package main + +import "github.com/tinyzimmer/go-glib/glib" + +func main() {} + +type gobin struct{} + +func (g *gobin) New() glib.GoObjectSubclass { return &gobin{} } + +func (g *gobin) ClassInit(klass *glib.ObjectClass) {} diff --git a/examples/plugins/gofilesink/filesink.go b/examples/plugins/gofilesink/filesink.go index 13d5c7e..5fa7923 100644 --- a/examples/plugins/gofilesink/filesink.go +++ b/examples/plugins/gofilesink/filesink.go @@ -29,6 +29,8 @@ // +element:Rank=gst.RankNone // +element:Impl=fileSink // +element:Subclass=base.ExtendsBaseSink +// +element:Interfaces=gst.InterfaceURIHandler +// package main import ( @@ -119,13 +121,6 @@ func (f *fileSink) New() glib.GoObjectSubclass { } } -// 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 *fileSink) TypeInit(instance *glib.TypeInstance) { - 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 *fileSink) ClassInit(klass *glib.ObjectClass) { diff --git a/examples/plugins/gofilesrc/filesrc.go b/examples/plugins/gofilesrc/filesrc.go index fca0a90..cdd4fa5 100644 --- a/examples/plugins/gofilesrc/filesrc.go +++ b/examples/plugins/gofilesrc/filesrc.go @@ -29,6 +29,8 @@ // +element:Rank=gst.RankNone // +element:Impl=fileSrc // +element:Subclass=base.ExtendsBaseSrc +// +element:Interfaces=gst.InterfaceURIHandler +// package main import ( @@ -123,13 +125,6 @@ func (f *fileSrc) New() glib.GoObjectSubclass { } } -// 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 *glib.TypeInstance) { - 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 *glib.ObjectClass) { diff --git a/go.mod b/go.mod index 8e29564..c1a69a3 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.15 require ( github.com/mattn/go-pointer v0.0.1 - github.com/tinyzimmer/go-glib v0.0.7 + github.com/tinyzimmer/go-glib v0.0.11 ) diff --git a/go.sum b/go.sum index d309070..cabb46f 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ 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.7 h1:09SIbhaL+E+5U/4qbZXiM7f6HEDvcxOBuEiSCkT9FNw= -github.com/tinyzimmer/go-glib v0.0.7/go.mod h1:zy2cs6eXSTtqqYrv9/UgYMDfr4pWKuYPSzwX87cBGX4= +github.com/tinyzimmer/go-glib v0.0.11 h1:+X15JtyglmBhiLu5KXHWxcxhypyc/CEqW+SIFmjZ110= +github.com/tinyzimmer/go-glib v0.0.11/go.mod h1:zy2cs6eXSTtqqYrv9/UgYMDfr4pWKuYPSzwX87cBGX4= diff --git a/gst/gst.go.h b/gst/gst.go.h index 0e6c708..a497454 100644 --- a/gst/gst.go.h +++ b/gst/gst.go.h @@ -19,12 +19,14 @@ inline GObjectClass * toGObjectClass (void *p) { return (G_ inline GstAllocator * toGstAllocator (void *p) { return (GST_ALLOCATOR_CAST(p)); } inline GstBin * toGstBin (void *p) { return (GST_BIN(p)); } +inline GstBinClass * toGstBinClass (void *p) { return (GST_BIN_CLASS(p)); } inline GstBufferList * toGstBufferList (void *p) { return (GST_BUFFER_LIST(p)); } inline GstBufferPool * toGstBufferPool (void *p) { return (GST_BUFFER_POOL(p)); } inline GstBuffer * toGstBuffer (void *p) { return (GST_BUFFER(p)); } inline GstBus * toGstBus (void *p) { return (GST_BUS(p)); } inline GstCapsFeatures * toGstCapsFeatures (void *p) { return (GST_CAPS_FEATURES(p)); } inline GstCaps * toGstCaps (void *p) { return (GST_CAPS(p)); } +inline GstChildProxy * toGstChildProxy (void *p) { return (GST_CHILD_PROXY(p)); } inline GstClock * toGstClock (void *p) { return (GST_CLOCK(p)); } inline GstContext * toGstContext (void *p) { return (GST_CONTEXT_CAST(p)); } inline GstDevice * toGstDevice (void *p) { return (GST_DEVICE_CAST(p)); } diff --git a/gst/gst_bin.go b/gst/gst_bin.go index 8a15615..e523e19 100644 --- a/gst/gst_bin.go +++ b/gst/gst_bin.go @@ -1,6 +1,73 @@ package gst -// #include "gst.go.h" +/* +#include "gst.go.h" + +gboolean +binParentAddElement (GstBin * bin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + return parent->add_element(bin, element); +} + +void +binParentDeepElementAdded (GstBin * bin, GstBin * subbin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + parent->deep_element_added(bin, subbin, element); +} + +void +binParentDeepElementRemoved (GstBin * bin, GstBin * subbin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(element)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + parent->deep_element_removed(bin, subbin, element); +} + +gboolean +binParentDoLatency (GstBin * bin) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + return parent->do_latency(bin); +} + +void +binParentElementAdded (GstBin * bin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + parent->element_added(bin, element); +} + +void +binParentElementRemoved (GstBin * bin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + parent->element_removed(bin, element); +} + +void +binParentHandleMessage (GstBin * bin, GstMessage * message) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + parent->handle_message(bin, message); +} + +gboolean +binParentRemoveElement (GstBin * bin, GstElement * element) +{ + GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(bin)); + GstBinClass * parent = toGstBinClass(g_type_class_peek_parent(this_class)); + return parent->remove_element(bin, element); +} + +*/ import "C" import ( @@ -22,14 +89,18 @@ func NewBin(name string) *Bin { return wrapBin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(bin))}) } -// BinFromElement wraps the given Element in a Bin reference. This only works for elements -// that implement their own Bin, such as playbin. If the provided element does not implement -// a Bin then nil is returned. -func BinFromElement(elem *Element) *Bin { - if C.toGstBin(elem.Unsafe()) == nil { - return nil +// ToGstBin wraps the given glib.Object, gst.Object, or gst.Element in a Bin instance. Only +// works for objects that implement their own Bin. +func ToGstBin(obj interface{}) *Bin { + switch obj := obj.(type) { + case *Object: + return &Bin{&Element{Object: obj}} + case *Element: + return &Bin{obj} + case *glib.Object: + return &Bin{&Element{Object: &Object{InitiallyUnowned: &glib.InitiallyUnowned{Object: obj}}}} } - return &Bin{elem} + return nil } // Instance returns the underlying GstBin instance. @@ -95,10 +166,10 @@ func (b *Bin) GetElementsSorted() ([]*Element, error) { // element is found, it returns the element. You can cast this element to the given interface afterwards. // If you want all elements that implement the interface, use GetAllByInterface. This function recurses // into child bins. -func (b *Bin) GetByInterface(iface glib.Type) (*Element, error) { - elem := C.gst_bin_get_by_interface(b.Instance(), C.GType(iface)) +func (b *Bin) GetByInterface(iface glib.Interface) (*Element, error) { + elem := C.gst_bin_get_by_interface(b.Instance(), C.GType(iface.Type())) if elem == nil { - return nil, fmt.Errorf("Could not find any elements implementing %s", iface.Name()) + return nil, fmt.Errorf("Could not find any elements implementing %s", iface.Type().Name()) } return wrapElement(toGObject(unsafe.Pointer(elem))), nil } @@ -106,8 +177,8 @@ func (b *Bin) GetByInterface(iface glib.Type) (*Element, error) { // GetAllByInterface looks for all elements inside the bin that implements the given interface. You can // safely cast all returned elements to the given interface. The function recurses inside child bins. // The function will return a series of Elements that should be unreffed after use. -func (b *Bin) GetAllByInterface(iface glib.Type) ([]*Element, error) { - iterator := C.gst_bin_iterate_all_by_interface(b.Instance(), C.GType(iface)) +func (b *Bin) GetAllByInterface(iface glib.Interface) ([]*Element, error) { + iterator := C.gst_bin_iterate_all_by_interface(b.Instance(), C.GType(iface.Type())) return iteratorToElementSlice(iterator) } @@ -123,7 +194,7 @@ func (b *Bin) GetAllByInterface(iface glib.Type) ([]*Element, error) { // Add adds an element to the bin. func (b *Bin) Add(elem *Element) error { if ok := C.gst_bin_add((*C.GstBin)(b.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) { - return fmt.Errorf("Failed to add element to pipeline: %s", elem.Name()) + return fmt.Errorf("Failed to add element to pipeline: %s", elem.GetName()) } return nil } @@ -141,7 +212,7 @@ func (b *Bin) AddMany(elems ...*Element) error { // Remove removes an element from the Bin. func (b *Bin) Remove(elem *Element) error { if ok := C.gst_bin_remove((*C.GstBin)(b.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) { - return fmt.Errorf("Failed to add element to pipeline: %s", elem.Name()) + return fmt.Errorf("Failed to add element to pipeline: %s", elem.GetName()) } return nil } @@ -257,3 +328,43 @@ func iteratorToElementSlice(iterator *C.GstIterator) ([]*Element, error) { } } } + +// ParentAddElement chains up to the parent AddElement handler. +func (b *Bin) ParentAddElement(element *Element) bool { + return gobool(C.binParentAddElement(b.Instance(), element.Instance())) +} + +// ParentDeepElementAdded chains up to the parent DeepElementAdded handler. +func (b *Bin) ParentDeepElementAdded(subbin *Bin, element *Element) { + C.binParentDeepElementAdded(b.Instance(), subbin.Instance(), element.Instance()) +} + +// ParentDeepElementRemoved chains up to the parent DeepElementRemoved handler. +func (b *Bin) ParentDeepElementRemoved(subbin *Bin, element *Element) { + C.binParentDeepElementRemoved(b.Instance(), subbin.Instance(), element.Instance()) +} + +// ParentDoLatency chains up to the parent DoLatency handler. +func (b *Bin) ParentDoLatency() bool { + return gobool(C.binParentDoLatency(b.Instance())) +} + +// ParentElementAdded chains up to the parent ElementAdded handler. +func (b *Bin) ParentElementAdded(element *Element) { + C.binParentElementAdded(b.Instance(), element.Instance()) +} + +// ParentElementRemoved chains up to the parent ElementRemoved handler. +func (b *Bin) ParentElementRemoved(element *Element) { + C.binParentElementRemoved(b.Instance(), element.Instance()) +} + +// ParentHandleMessage chains up to the parent HandleMessage handler. +func (b *Bin) ParentHandleMessage(message *Message) { + C.binParentHandleMessage(b.Instance(), message.Instance()) +} + +// ParentRemoveElement chains up to the parent RemoveElement handler. +func (b *Bin) ParentRemoveElement(element *Element) bool { + return gobool(C.binParentRemoveElement(b.Instance(), element.Instance())) +} diff --git a/gst/gst_bin_exports.go b/gst/gst_bin_exports.go new file mode 100644 index 0000000..9b5f4c2 --- /dev/null +++ b/gst/gst_bin_exports.go @@ -0,0 +1,91 @@ +package gst + +/* +#include "gst.go.h" +*/ +import "C" +import ( + "unsafe" + + "github.com/tinyzimmer/go-glib/glib" +) + +func cbWrapBin(bin *C.GstBin) *Bin { + return wrapBin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(bin))}) +} + +func cbWrapElement(elem *C.GstElement) *Element { + return wrapElement(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(elem))}) +} + +//export goGstBinAddElement +func goGstBinAddElement(bin *C.GstBin, element *C.GstElement) C.gboolean { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + AddElement(self *Bin, element *Element) bool + }) + return gboolean(caller.AddElement(cbWrapBin(bin), cbWrapElement(element))) +} + +//export goGstBinDeepElementAdded +func goGstBinDeepElementAdded(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + DeepElementAdded(self *Bin, subbin *Bin, child *Element) + }) + caller.DeepElementAdded(cbWrapBin(bin), cbWrapBin(subbin), cbWrapElement(child)) +} + +//export goGstBinDeepElementRemoved +func goGstBinDeepElementRemoved(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + DeepElementRemoved(self *Bin, subbin *Bin, child *Element) + }) + caller.DeepElementRemoved(cbWrapBin(bin), cbWrapBin(subbin), cbWrapElement(child)) +} + +//export goGstBinDoLatency +func goGstBinDoLatency(bin *C.GstBin) C.gboolean { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + DoLatency(self *Bin) bool + }) + return gboolean(caller.DoLatency(cbWrapBin(bin))) +} + +//export goGstBinElementAdded +func goGstBinElementAdded(bin *C.GstBin, child *C.GstElement) { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + ElementAdded(self *Bin, child *Element) + }) + caller.ElementAdded(cbWrapBin(bin), cbWrapElement(child)) +} + +//export goGstBinElementRemoved +func goGstBinElementRemoved(bin *C.GstBin, child *C.GstElement) { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + ElementRemoved(self *Bin, child *Element) + }) + caller.ElementRemoved(cbWrapBin(bin), cbWrapElement(child)) +} + +//export goGstBinHandleMessage +func goGstBinHandleMessage(bin *C.GstBin, message *C.GstMessage) { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + HandleMessage(self *Bin, msg *Message) + }) + caller.HandleMessage(cbWrapBin(bin), wrapMessage(message)) +} + +//export goGstBinRemoveElement +func goGstBinRemoveElement(bin *C.GstBin, element *C.GstElement) C.gboolean { + elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin)) + caller := elem.(interface { + RemoveElement(self *Bin, element *Element) bool + }) + return gboolean(caller.RemoveElement(cbWrapBin(bin), cbWrapElement(element))) +} diff --git a/gst/gst_bin_impl.go b/gst/gst_bin_impl.go new file mode 100644 index 0000000..d319166 --- /dev/null +++ b/gst/gst_bin_impl.go @@ -0,0 +1,106 @@ +package gst + +/* +#include "gst.go.h" + +extern gboolean goGstBinAddElement (GstBin * bin, GstElement * element); +extern void goGstBinDeepElementAdded (GstBin * bin, GstBin * subbin, GstElement * child); +extern void goGstBinDeepElementRemoved (GstBin * bin, GstBin * subbin, GstElement * child); +extern gboolean goGstBinDoLatency (GstBin * bin); +extern void goGstBinElementAdded (GstBin * bin, GstElement * child); +extern void goGstBinElementRemoved (GstBin * bin, GstElement * child); +extern void goGstBinHandleMessage (GstBin * bin, GstMessage * message); +extern gboolean goGstBinRemoveElement (GstBin * bin, GstElement * element); + +void setGstBinAddElement (GstBinClass * klass) { klass->add_element = goGstBinAddElement; }; +void setGstBinDeepElementAdded (GstBinClass * klass) { klass->deep_element_added = goGstBinDeepElementAdded; }; +void setGstBinDeepElementRemoved (GstBinClass * klass) { klass->deep_element_removed = goGstBinDeepElementRemoved; }; +void setGstBinDoLatency (GstBinClass * klass) { klass->do_latency = goGstBinDoLatency; }; +void setGstBinElementAdded (GstBinClass * klass) { klass->element_added = goGstBinElementAdded; }; +void setGstBinElementRemoved (GstBinClass * klass) { klass->element_removed = goGstBinElementRemoved; }; +void setGstBinHandleMessage (GstBinClass * klass) { klass->handle_message = goGstBinHandleMessage; }; +void setGstBinRemoveElement (GstBinClass * klass) { klass->remove_element = goGstBinRemoveElement; }; + +*/ +import "C" +import ( + "unsafe" + + "github.com/tinyzimmer/go-glib/glib" +) + +// ExtendsBin implements an Extendable object based on a GstBin. +var ExtendsBin glib.Extendable = &extendsBin{parent: ExtendsElement} + +// BinImpl is the reference interface for Go elements extending a Bin. You only need to +// implement the methods that interest you. +type BinImpl interface { + AddElement(self *Bin, element *Element) bool + DeepElementAdded(self *Bin, subbin *Bin, child *Element) + DeepElementRemoved(self *Bin, subbin *Bin, child *Element) + DoLatency(self *Bin) bool + ElementAdded(self *Bin, child *Element) + ElementRemoved(self *Bin, child *Element) + HandleMessage(self *Bin, msg *Message) + RemoveElement(self *Bin, element *Element) bool +} + +type extendsBin struct{ parent glib.Extendable } + +func (e *extendsBin) Type() glib.Type { return glib.Type(C.gst_bin_get_type()) } +func (e *extendsBin) ClassSize() int64 { return int64(C.sizeof_GstBinClass) } +func (e *extendsBin) InstanceSize() int64 { return int64(C.sizeof_GstBin) } + +func (e *extendsBin) InitClass(klass unsafe.Pointer, elem glib.GoObjectSubclass) { + e.parent.InitClass(klass, elem) + + class := C.toGstBinClass(klass) + + if _, ok := elem.(interface { + AddElement(self *Bin, element *Element) bool + }); ok { + C.setGstBinAddElement(class) + } + + if _, ok := elem.(interface { + DeepElementAdded(self *Bin, subbin *Bin, child *Element) + }); ok { + C.setGstBinDeepElementAdded(class) + } + + if _, ok := elem.(interface { + DeepElementRemoved(self *Bin, subbin *Bin, child *Element) + }); ok { + C.setGstBinDeepElementRemoved(class) + } + + if _, ok := elem.(interface { + DoLatency(self *Bin) bool + }); ok { + C.setGstBinDoLatency(class) + } + + if _, ok := elem.(interface { + ElementAdded(self *Bin, child *Element) + }); ok { + C.setGstBinElementAdded(class) + } + + if _, ok := elem.(interface { + ElementRemoved(self *Bin, child *Element) + }); ok { + C.setGstBinElementRemoved(class) + } + + if _, ok := elem.(interface { + HandleMessage(self *Bin, msg *Message) + }); ok { + C.setGstBinHandleMessage(class) + } + + if _, ok := elem.(interface { + RemoveElement(self *Bin, element *Element) bool + }); ok { + C.setGstBinRemoveElement(class) + } +} diff --git a/gst/gst_child_proxy.go b/gst/gst_child_proxy.go new file mode 100644 index 0000000..2938295 --- /dev/null +++ b/gst/gst_child_proxy.go @@ -0,0 +1,222 @@ +package gst + +/* +#include "gst.go.h" + +extern void goGstChildProxyChildAdded (GstChildProxy * parent, GObject * child, const gchar * name); +extern void goGstChildProxyChildRemoved (GstChildProxy * parent, GObject * child, const gchar * name); +extern GObject * goGstChildProxyGetChildByIndex (GstChildProxy * parent, guint idx); +extern GObject * goGstChildProxyGetChildByName (GstChildProxy * parent, const gchar * name); +extern guint goGstChildProxyGetChildrenCount (GstChildProxy * parent); + +void setGstChildProxyChildAdded (gpointer iface) { ((GstChildProxyInterface*)iface)->child_added = goGstChildProxyChildAdded; } +void setGstChildProxyChildRemoved (gpointer iface) { ((GstChildProxyInterface*)iface)->child_removed = goGstChildProxyChildRemoved; } +void setGstChildProxyGetChildByIndex (gpointer iface) { ((GstChildProxyInterface*)iface)->get_child_by_index = goGstChildProxyGetChildByIndex; } +void setGstChildProxyGetChildByName (gpointer iface) { ((GstChildProxyInterface*)iface)->get_child_by_name = goGstChildProxyGetChildByName; } +void setGstChildProxyGetChildrenCount (gpointer iface) { ((GstChildProxyInterface*)iface)->get_children_count = goGstChildProxyGetChildrenCount; } + +*/ +import "C" + +import ( + "unsafe" + + "github.com/tinyzimmer/go-glib/glib" +) + +// InterfaceChildProxy represents the GstChildProxy interface. Use this when querying bins +// for elements that implement GstChildProxy, or when signaling that a GoObjectSubclass +// provides this interface. +var InterfaceChildProxy glib.Interface = &interfaceChildProxy{} + +type interfaceChildProxy struct{ glib.Interface } + +func (i *interfaceChildProxy) Type() glib.Type { return glib.Type(C.GST_TYPE_CHILD_PROXY) } +func (i *interfaceChildProxy) InitFunc() glib.InterfaceInitFunc { + return func(instance *glib.TypeInstance) { + goobj := instance.GoType + + if _, ok := goobj.(interface { + ChildAdded(self *ChildProxy, child *glib.Object, name string) + }); ok { + C.setGstChildProxyChildAdded((C.gpointer)(instance.GTypeInstance)) + } + + if _, ok := goobj.(interface { + ChildRemoved(self *ChildProxy, child *glib.Object, name string) + }); ok { + C.setGstChildProxyChildRemoved((C.gpointer)(instance.GTypeInstance)) + } + + if _, ok := goobj.(interface { + GetChildByIndex(self *ChildProxy, idx uint) *glib.Object + }); ok { + C.setGstChildProxyGetChildByIndex((C.gpointer)(instance.GTypeInstance)) + } + + if _, ok := goobj.(interface { + GetChildByName(self *ChildProxy, name string) *glib.Object + }); ok { + C.setGstChildProxyGetChildByName((C.gpointer)(instance.GTypeInstance)) + } + + if _, ok := goobj.(interface { + GetChildrenCount(self *ChildProxy) uint + }); ok { + C.setGstChildProxyGetChildrenCount((C.gpointer)(instance.GTypeInstance)) + } + } +} + +// ChildProxyImpl is the reference implementation for a ChildProxy implemented by a Go object. +type ChildProxyImpl interface { + ChildAdded(self *ChildProxy, child *glib.Object, name string) + ChildRemoved(self *ChildProxy, child *glib.Object, name string) + GetChildByIndex(self *ChildProxy, idx uint) *glib.Object + GetChildByName(self *ChildProxy, name string) *glib.Object + GetChildrenCount(self *ChildProxy) uint +} + +// ChildProxy is an interface that abstracts handling of property sets for +// elements with children. They all have multiple GstPad or some kind of voice +// objects. Another use case are container elements like GstBin. The element +// implementing the interface acts as a parent for those child objects. +// +// Property names are written as "child-name::property-name". The whole naming +// scheme is recursive. Thus "child1::child2::property" is valid too, if "child1" +// and "child2" implement the GstChildProxy interface. +type ChildProxy struct{ ptr *C.GstChildProxy } + +// ToChildProxy returns a ChildProxy for the given element. If the element does not implement +// a ChildProxy it returns nil. +func ToChildProxy(elem *Element) *ChildProxy { + if proxy := C.toGstChildProxy(elem.Unsafe()); proxy != nil { + return &ChildProxy{proxy} + } + return nil +} + +// Instance returns the underlying GstChildProxy instance. +func (c *ChildProxy) Instance() *C.GstChildProxy { + return C.toGstChildProxy(unsafe.Pointer(c.ptr)) +} + +// ChildAdded emits the "child-added" signal. +func (c *ChildProxy) ChildAdded(child *glib.Object, name string) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + C.gst_child_proxy_child_added( + c.Instance(), + (*C.GObject)(child.Unsafe()), + (*C.gchar)(unsafe.Pointer(cname)), + ) +} + +// ChildRemoved emits the "child-removed" signal. +func (c *ChildProxy) ChildRemoved(child *glib.Object, name string) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + C.gst_child_proxy_child_removed( + c.Instance(), + (*C.GObject)(child.Unsafe()), + (*C.gchar)(unsafe.Pointer(cname)), + ) +} + +// Get gets properties of the parent object and its children. This is a direct alias to looping +// over GetProperty and returning the results in the order of the arguments. If any of the results +// returns nil from an allocation error, nil is returned for the entire slice. +func (c *ChildProxy) Get(names ...string) []*glib.Value { + out := make([]*glib.Value, len(names)) + for i, name := range names { + val := c.GetProperty(name) + if val == nil { + return nil + } + out[i] = val + } + return out +} + +// GetChildByIndex fetches a child by its number. This function can return nil if the object is not +// found. Unref after usage. +func (c *ChildProxy) GetChildByIndex(idx uint) *glib.Object { + gobj := C.gst_child_proxy_get_child_by_index(c.Instance(), C.guint(idx)) + if gobj == nil { + return nil + } + return &glib.Object{GObject: glib.ToGObject(unsafe.Pointer(gobj))} +} + +// GetChildByName fetches a child by name. The virtual method's default implementation uses Object +// together with Object.GetName. If the interface is to be used with GObjects, this method needs +// to be overridden. +// +// This function can return nil if the object is not found. Unref after usage. +func (c *ChildProxy) GetChildByName(name string) *glib.Object { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + gobj := C.gst_child_proxy_get_child_by_name(c.Instance(), (*C.gchar)(unsafe.Pointer(cname))) + if gobj == nil { + return nil + } + return &glib.Object{GObject: glib.ToGObject(unsafe.Pointer(gobj))} +} + +// GetChildrenCount returns the number of child objects the parent contains. +func (c *ChildProxy) GetChildrenCount() uint { + return uint(C.gst_child_proxy_get_children_count(c.Instance())) +} + +// GetProperty gets a single property using the ChildProxy mechanism. The bindings +// take care of freeing the value when it leaves the user's scope. This function +// can return nil if a failure happens trying to allocate GValues. +func (c *ChildProxy) GetProperty(name string) *glib.Value { + value, err := glib.ValueAlloc() + if err != nil { + return nil + } + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + C.gst_child_proxy_get_property(c.Instance(), (*C.gchar)(unsafe.Pointer(cname)), (*C.GValue)(unsafe.Pointer(value.GValue))) + return value +} + +// Lookup looks up which object and and parameter would be affected by the given name. +// If ok is false, the targets could not be found and this function returned nil. +// Unref target after usage. +func (c *ChildProxy) Lookup(name string) (ok bool, target *glib.Object, param *glib.ParamSpec) { + var gtarget *C.GObject + var gspec *C.GParamSpec + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + ok = gobool(C.gst_child_proxy_lookup( + c.Instance(), + (*C.gchar)(unsafe.Pointer(cname)), + >arget, &gspec, + )) + if !ok { + return + } + target = &glib.Object{GObject: glib.ToGObject(unsafe.Pointer(gtarget))} + param = glib.ToParamSpec(unsafe.Pointer(gspec)) + return +} + +// Set takes a map of names to values and applies them using the ChildProxy mechanism. +func (c *ChildProxy) Set(values map[string]*glib.Value) { + for name, value := range values { + c.SetProperty(name, value) + } +} + +// SetProperty sets a single property using the ChildProxy mechanism. +func (c *ChildProxy) SetProperty(name string, value *glib.Value) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + C.gst_child_proxy_set_property( + c.Instance(), + (*C.gchar)(unsafe.Pointer(cname)), + (*C.GValue)(unsafe.Pointer(value.GValue)), + ) +} diff --git a/gst/gst_child_proxy_exports.go b/gst/gst_child_proxy_exports.go new file mode 100644 index 0000000..2347c8d --- /dev/null +++ b/gst/gst_child_proxy_exports.go @@ -0,0 +1,74 @@ +package gst + +/* +#include "gst.go.h" +*/ +import "C" +import ( + "unsafe" + + "github.com/tinyzimmer/go-glib/glib" +) + +func wrapParent(parent *C.GstChildProxy) *ChildProxy { return &ChildProxy{ptr: parent} } + +//export goGstChildProxyChildAdded +func goGstChildProxyChildAdded(parent *C.GstChildProxy, child *C.GObject, name *C.gchar) { + iface := glib.FromObjectUnsafePrivate(unsafe.Pointer(parent)) + caller := iface.(interface { + ChildAdded(self *ChildProxy, child *glib.Object, name string) + }) + caller.ChildAdded( + wrapParent(parent), + &glib.Object{GObject: glib.ToGObject(unsafe.Pointer(child))}, + C.GoString(name), + ) +} + +//export goGstChildProxyChildRemoved +func goGstChildProxyChildRemoved(parent *C.GstChildProxy, child *C.GObject, name *C.gchar) { + iface := glib.FromObjectUnsafePrivate(unsafe.Pointer(parent)) + caller := iface.(interface { + ChildRemoved(self *ChildProxy, child *glib.Object, name string) + }) + caller.ChildRemoved( + wrapParent(parent), + &glib.Object{GObject: glib.ToGObject(unsafe.Pointer(child))}, + C.GoString(name), + ) +} + +//export goGstChildProxyGetChildByIndex +func goGstChildProxyGetChildByIndex(parent *C.GstChildProxy, idx C.guint) *C.GObject { + iface := glib.FromObjectUnsafePrivate(unsafe.Pointer(parent)) + caller := iface.(interface { + GetChildByIndex(self *ChildProxy, idx uint) *glib.Object + }) + obj := caller.GetChildByIndex(wrapParent(parent), uint(idx)) + if obj == nil { + return nil + } + return (*C.GObject)(unsafe.Pointer(obj.GObject)) +} + +//export goGstChildProxyGetChildByName +func goGstChildProxyGetChildByName(parent *C.GstChildProxy, name *C.gchar) *C.GObject { + iface := glib.FromObjectUnsafePrivate(unsafe.Pointer(parent)) + caller := iface.(interface { + GetChildByName(self *ChildProxy, name string) *glib.Object + }) + obj := caller.GetChildByName(wrapParent(parent), C.GoString(name)) + if obj == nil { + return nil + } + return (*C.GObject)(unsafe.Pointer(obj.GObject)) +} + +//export goGstChildProxyGetChildrenCount +func goGstChildProxyGetChildrenCount(parent *C.GstChildProxy) C.guint { + iface := glib.FromObjectUnsafePrivate(unsafe.Pointer(parent)) + caller := iface.(interface { + GetChildrenCount(self *ChildProxy) uint + }) + return C.guint(caller.GetChildrenCount(wrapParent(parent))) +} diff --git a/gst/gst_device.go b/gst/gst_device.go index 61f5b61..006738c 100644 --- a/gst/gst_device.go +++ b/gst/gst_device.go @@ -70,7 +70,7 @@ func (d *Device) HasClasses(classes []string) bool { // while in the PLAYING state. func (d *Device) ReconfigureElement(elem *Element) error { if ok := gobool(C.gst_device_reconfigure_element(d.Instance(), elem.Instance())); !ok { - return fmt.Errorf("Failed to reconfigure element %s", elem.Name()) + return fmt.Errorf("Failed to reconfigure element %s", elem.GetName()) } return nil } diff --git a/gst/gst_element.go b/gst/gst_element.go index 8a737e6..e97ae80 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -29,40 +29,6 @@ GstStateChangeReturn elementParentChangeState (GstElement * element, GstStateCha return parent->change_state(element, transition); } -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" @@ -70,7 +36,6 @@ import ( "fmt" "path" "runtime" - "time" "unsafe" gopointer "github.com/mattn/go-pointer" @@ -117,12 +82,12 @@ const ( // RegisterElement creates a new elementfactory capable of instantiating objects of the given GoElement // and adds the factory to the plugin. A higher rank means more importance when autoplugging. -func RegisterElement(plugin *Plugin, name string, rank Rank, elem glib.GoObjectSubclass, extends glib.Extendable) bool { +func RegisterElement(plugin *Plugin, name string, rank Rank, elem glib.GoObjectSubclass, extends glib.Extendable, interfaces ...glib.Interface) bool { return gobool(C.gst_element_register( plugin.Instance(), C.CString(name), C.guint(rank), - C.GType(glib.RegisterGoType(name, elem, extends)), + C.GType(glib.RegisterGoType(name, elem, extends, interfaces...)), )) } @@ -340,7 +305,7 @@ func (e *Element) IsURIHandler() bool { // Link wraps gst_element_link and links this element to the given one. func (e *Element) Link(elem *Element) error { if ok := C.gst_element_link((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) { - return fmt.Errorf("Failed to link %s to %s", e.Name(), elem.Name()) + return fmt.Errorf("Failed to link %s to %s", e.GetName(), elem.GetName()) } return nil } @@ -349,7 +314,7 @@ func (e *Element) Link(elem *Element) error { // using the provided sink caps. func (e *Element) LinkFiltered(elem *Element, caps *Caps) error { if ok := C.gst_element_link_filtered((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance()), (*C.GstCaps)(caps.Instance())); !gobool(ok) { - return fmt.Errorf("Failed to link %s to %s with provider caps", e.Name(), elem.Name()) + return fmt.Errorf("Failed to link %s to %s with provided caps", e.GetName(), elem.GetName()) } return nil } @@ -459,152 +424,3 @@ func (e *Element) URIHandler() URIHandler { } return &gstURIHandler{ptr: e.Instance()} } - -// ExtendsElement signifies a GoElement that extends a GstElement. -var ExtendsElement glib.Extendable = &extendElement{parent: glib.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 glib.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 glib.GoObjectSubclass) { - 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_factory.go b/gst/gst_element_factory.go index b5c0eea..9ce79d2 100644 --- a/gst/gst_element_factory.go +++ b/gst/gst_element_factory.go @@ -14,11 +14,23 @@ import ( // This is meant for internal usage and is exported for visibility to other packages. func FromGstElementUnsafe(elem unsafe.Pointer) *Element { return wrapElement(toGObject(elem)) } -// NewElement is a generic wrapper around `gst_element_factory_make`. -func NewElement(name string) (*Element, error) { - elemName := C.CString(name) +// NewElement creates a new element using the factory of the given name. +func NewElement(factory string) (*Element, error) { + return NewElementWithName(factory, "") +} + +// NewElementWithName creates a new element and sets it's name to the given value. +func NewElementWithName(factory string, name string) (*Element, error) { + elemName := C.CString(factory) defer C.free(unsafe.Pointer(elemName)) - elem := C.gst_element_factory_make((*C.gchar)(elemName), nil) + var elem *C.GstElement + if name == "" { + elem = C.gst_element_factory_make((*C.gchar)(elemName), nil) + } else { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + elem = C.gst_element_factory_make((*C.gchar)(elemName), (*C.gchar)(cname)) + } if elem == nil { return nil, fmt.Errorf("Could not create element: %s", name) } diff --git a/gst/gst_element_impl.go b/gst/gst_element_impl.go new file mode 100644 index 0000000..086d9ff --- /dev/null +++ b/gst/gst_element_impl.go @@ -0,0 +1,196 @@ +package gst + +/* +#include "gst.go.h" + +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" +import ( + "time" + "unsafe" + + "github.com/tinyzimmer/go-glib/glib" +) + +// ExtendsElement implements an Extendable object based on a GstElement. +var ExtendsElement glib.Extendable = &extendElement{parent: glib.ExtendsObject} + +// ElementImpl is an interface containing go equivalents 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 glib.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 glib.GoObjectSubclass) { + 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_message_stringer.go b/gst/gst_message_stringer.go index f9d539b..92daa48 100644 --- a/gst/gst_message_stringer.go +++ b/gst/gst_message_stringer.go @@ -72,11 +72,11 @@ func (m *Message) String() string { case MessageStructureChange: chgType, elem, busy := m.ParseStructureChange() - msg += fmt.Sprintf("Structure change of type %s from %s. (in progress: %v)", chgType.String(), elem.Name(), busy) + msg += fmt.Sprintf("Structure change of type %s from %s. (in progress: %v)", chgType.String(), elem.GetName(), busy) case MessageStreamStatus: statusType, elem := m.ParseStreamStatus() - msg += fmt.Sprintf("Stream status from %s: %s", elem.Name(), statusType.String()) + msg += fmt.Sprintf("Stream status from %s: %s", elem.GetName(), statusType.String()) case MessageApplication: msg += "Message posted by the application, possibly via an application-specific element." @@ -165,7 +165,7 @@ func (m *Message) String() string { if obj != nil && propVal != nil { goval, err := propVal.GoValue() if err != nil { - msg += fmt.Sprintf("Object %s had property '%s' changed to %+v", obj.Name(), propName, goval) + msg += fmt.Sprintf("Object %s had property '%s' changed to %+v", obj.GetName(), propName, goval) } } diff --git a/gst/gst_object.go b/gst/gst_object.go index c9e6bb0..2b4ec66 100644 --- a/gst/gst_object.go +++ b/gst/gst_object.go @@ -4,6 +4,7 @@ package gst import "C" import ( + "time" "unsafe" "github.com/tinyzimmer/go-glib/glib" @@ -14,70 +15,36 @@ type Object struct{ *glib.InitiallyUnowned } // FromGstObjectUnsafe returns an Object wrapping the given pointer. It meant for internal // usage and exported for visibility to other packages. -func FromGstObjectUnsafe(ptr unsafe.Pointer) *Object { - return wrapObject(toGObject(ptr)) -} - -// Unsafe returns the unsafe pointer to the underlying object. This method is primarily -// for internal usage and is exposed for visibility in other packages. -func (o *Object) Unsafe() unsafe.Pointer { - if o == nil || o.GObject == nil { - return nil - } - return unsafe.Pointer(o.GObject) -} +func FromGstObjectUnsafe(ptr unsafe.Pointer) *Object { return wrapObject(toGObject(ptr)) } // Instance returns the native C GstObject. func (o *Object) Instance() *C.GstObject { return C.toGstObject(o.Unsafe()) } -// BaseObject returns this object for embedding structs. +// BaseObject is a convenience method for retrieving this object from embedded structs. func (o *Object) BaseObject() *Object { return o } // GstObject is an alias to Instance on the underlying GstObject of any extending struct. func (o *Object) GstObject() *C.GstObject { return C.toGstObject(o.Unsafe()) } -// Class returns the GObjectClass of this instance. -func (o *Object) Class() *C.GObjectClass { return C.getGObjectClass(o.Unsafe()) } +// GObject returns the underlying GObject instance. +func (o *Object) GObject() *glib.Object { return o.InitiallyUnowned.Object } -// Name returns the name of this object. -func (o *Object) Name() string { +// GetName returns the name of this object. +func (o *Object) GetName() string { cName := C.gst_object_get_name((*C.GstObject)(o.Instance())) defer C.free(unsafe.Pointer(cName)) return C.GoString(cName) } -// Interfaces returns the interfaces associated with this object. -func (o *Object) Interfaces() []string { - var size C.guint - ifaces := C.g_type_interfaces(C.gsize(o.TypeFromInstance()), &size) - if int(size) == 0 { +// GetValue retrieves the value for the given controlled property at the given timestamp. +func (o *Object) GetValue(property string, timestamp time.Duration) *glib.Value { + cprop := C.CString(property) + defer C.free(unsafe.Pointer(cprop)) + gval := C.gst_object_get_value(o.Instance(), (*C.gchar)(cprop), C.GstClockTime(timestamp.Nanoseconds())) + if gval == nil { return nil } - defer C.g_free((C.gpointer)(ifaces)) - out := make([]string, int(size)) - for _, t := range (*[1 << 30]int)(unsafe.Pointer(ifaces))[:size:size] { - out = append(out, glib.Type(t).Name()) - } - return out -} - -// ListProperties returns a list of the properties associated with this object. -// The default values assumed in the parameter spec reflect the values currently -// set in this object, or their defaults. -// -// Unref after usage. -func (o *Object) ListProperties() []*glib.ParamSpec { - var size C.guint - props := C.g_object_class_list_properties((*C.GObjectClass)(o.Class()), &size) - if props == nil { - return nil - } - defer C.g_free((C.gpointer)(props)) - out := make([]*glib.ParamSpec, 0) - for _, prop := range (*[1 << 30]*C.GParamSpec)(unsafe.Pointer(props))[:size:size] { - out = append(out, glib.ToParamSpec(unsafe.Pointer(prop))) - } - return out + return glib.ValueFromNative(unsafe.Pointer(gval)) } // Log logs a message to the given category from this object using the currently registered @@ -85,3 +52,25 @@ func (o *Object) ListProperties() []*glib.ParamSpec { func (o *Object) Log(cat *DebugCategory, level DebugLevel, message string) { cat.logDepth(level, message, 2, (*C.GObject)(o.Unsafe())) } + +// Clear will will clear all references to this object. If the reference is already null +// the the function does nothing. Otherwise the reference count is decreased and the pointer +// set to null. +func (o *Object) Clear() { + if ptr := o.Unsafe(); ptr != nil { + C.gst_clear_object((**C.GstObject)(unsafe.Pointer(&ptr))) + } +} + +// Ref increments the reference count on object. This function does not take the lock on object +// because it relies on atomic refcounting. For convenience the same object is returned. +func (o *Object) Ref() *Object { + C.gst_object_ref((C.gpointer)(o.Unsafe())) + return o +} + +// Unref decrements the reference count on object. If reference count hits zero, destroy object. +// This function does not take the lock on object as it relies on atomic refcounting. +func (o *Object) Unref() { + C.gst_object_unref((C.gpointer)(o.Unsafe())) +} diff --git a/gst/gst_tag_setter.go b/gst/gst_tag_setter.go index 53deca1..dd748ad 100644 --- a/gst/gst_tag_setter.go +++ b/gst/gst_tag_setter.go @@ -9,8 +9,15 @@ import ( ) // InterfaceTagSetter represents the GstTagsetter interface GType. Use this when querying bins -// for elements that implement a TagSetter. -var InterfaceTagSetter = glib.Type(C.GST_TYPE_TAG_SETTER) +// for elements that implement a TagSetter. Extending this interface is not yet implemented. +var InterfaceTagSetter glib.Interface = &interfaceTagSetter{} + +type interfaceTagSetter struct{} + +func (i *interfaceTagSetter) Type() glib.Type { return glib.Type(C.GST_TYPE_TAG_SETTER) } +func (i *interfaceTagSetter) InitFunc() glib.InterfaceInitFunc { + return func(instance *glib.TypeInstance) {} +} // TagSetter is an interface that elements can implement to provide Tag writing capabilities. type TagSetter interface { diff --git a/gst/gst_uri_handler.go b/gst/gst_uri_handler.go index ccb3d17..0a76863 100644 --- a/gst/gst_uri_handler.go +++ b/gst/gst_uri_handler.go @@ -27,21 +27,18 @@ import ( ) // InterfaceURIHandler represents the GstURIHandler interface GType. Use this when querying bins -// for elements that implement a URIHandler, or when signaling that a GoElement provides this -// interface. +// for elements that implement a URIHandler, or when signaling that a GoObjectSubclass provides this +// interface. Note that the way this interface is implemented, it can only be used once per plugin. var InterfaceURIHandler glib.Interface = &interfaceURIHandler{} -type interfaceURIHandler struct { - glib.Interface -} +type interfaceURIHandler struct{ glib.Interface } -func (i *interfaceURIHandler) Type() glib.Type { - return glib.Type(C.GST_TYPE_URI_HANDLER) -} - -func (i *interfaceURIHandler) InitFunc(t *glib.TypeInstance) unsafe.Pointer { - globalURIHdlr = t.GoType.(URIHandler) - return unsafe.Pointer(C.uriHandlerInit) +func (i *interfaceURIHandler) Type() glib.Type { return glib.Type(C.GST_TYPE_URI_HANDLER) } +func (i *interfaceURIHandler) InitFunc() glib.InterfaceInitFunc { + return func(instance *glib.TypeInstance) { + globalURIHdlr = instance.GoType.(URIHandler) + C.uriHandlerInit((C.gpointer)(instance.GTypeInstance), nil) + } } // URIHandler represents an interface that elements can implement to provide URI handling