diff --git a/gst/c_util.go b/gst/c_util.go index b4ddbd0..091e0ce 100644 --- a/gst/c_util.go +++ b/gst/c_util.go @@ -31,6 +31,16 @@ func goStrings(argc C.int, argv **C.gchar) []string { return gostrings } +func gcharStrings(strs []string) **C.gchar { + gcharSlc := make([]*C.gchar, len(strs)) + for _, s := range strs { + cStr := C.CString(s) + defer C.free(unsafe.Pointer(cStr)) + gcharSlc = append(gcharSlc, cStr) + } + return &gcharSlc[0] +} + // newQuarkFromString creates a new GQuark (or returns an existing one) for the given // string func newQuarkFromString(str string) C.uint { @@ -39,3 +49,7 @@ func newQuarkFromString(str string) C.uint { quark := C.g_quark_from_string(cstr) return quark } + +func quarkToString(q C.GQuark) string { + return C.GoString(C.g_quark_to_string(q)) +} diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index c9c311f..1e01a1f 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -56,3 +56,55 @@ func goBusFunc(bus *C.GstBus, cMsg *C.GstMessage, userData C.gpointer) C.gboolea return gboolean(true) } + +func getMetaInfoCbFuncs(meta *C.GstMeta) *MetaInfoCallbackFuncs { + return registeredMetas[glib.Type(meta.info._type)] +} + +//export goMetaFreeFunc +func goMetaFreeFunc(meta *C.GstMeta, buf *C.GstBuffer) { + cbFuncs := getMetaInfoCbFuncs(meta) + if cbFuncs != nil && cbFuncs.FreeFunc != nil { + cbFuncs.FreeFunc(wrapBuffer(buf)) + } +} + +//export goMetaInitFunc +func goMetaInitFunc(meta *C.GstMeta, params C.gpointer, buf *C.GstBuffer) C.gboolean { + cbFuncs := getMetaInfoCbFuncs(meta) + if cbFuncs != nil && cbFuncs.InitFunc != nil { + paramsIface := gopointer.Restore(unsafe.Pointer(params)) + defer gopointer.Unref(unsafe.Pointer(params)) + return gboolean(cbFuncs.InitFunc(paramsIface, wrapBuffer(buf))) + } + return gboolean(true) +} + +//export goMetaTransformFunc +func goMetaTransformFunc(transBuf *C.GstBuffer, meta *C.GstMeta, buffer *C.GstBuffer, mType C.GQuark, data C.gpointer) C.gboolean { + cbFuncs := getMetaInfoCbFuncs(meta) + if cbFuncs != nil && cbFuncs.TransformFunc != nil { + transformData := (*C.GstMetaTransformCopy)(unsafe.Pointer(data)) + return gboolean(cbFuncs.TransformFunc( + wrapBuffer(transBuf), + wrapBuffer(buffer), + quarkToString(mType), + &MetaTransformCopy{ + Region: gobool(transformData.region), + Offset: int64(transformData.offset), + Size: int64(transformData.size), + }, + )) + } + return gboolean(true) +} + +//export goGDestroyNotifyFunc +func goGDestroyNotifyFunc(ptr C.gpointer) { + funcIface := gopointer.Restore(unsafe.Pointer(ptr)) + defer gopointer.Unref(unsafe.Pointer(ptr)) + f := funcIface.(func()) + if f != nil { + f() + } +} diff --git a/gst/gst_allocator.go b/gst/gst_allocator.go index eaa0387..8fad661 100644 --- a/gst/gst_allocator.go +++ b/gst/gst_allocator.go @@ -24,6 +24,9 @@ func NewAllocationParams() *AllocationParams { return params } +// Instance returns the underlying GstAllocationParams. +func (a *AllocationParams) Instance() *C.GstAllocationParams { return a.ptr } + // Init initializes these AllocationParams to their original values. func (a *AllocationParams) Init() { C.gst_allocation_params_init(a.ptr) } diff --git a/gst/gst_buffer.go b/gst/gst_buffer.go index b4832b7..b8c9cbb 100644 --- a/gst/gst_buffer.go +++ b/gst/gst_buffer.go @@ -1,6 +1,14 @@ package gst -// #include "gst.go.h" +/* +#include "gst.go.h" + +extern void goGDestroyNotifyFunc (gpointer data); + +void cgoDestroyNotifyFunc (gpointer data) { + goGDestroyNotifyFunc(data); +} +*/ import "C" import ( @@ -11,6 +19,7 @@ import ( "unsafe" "github.com/gotk3/gotk3/glib" + gopointer "github.com/mattn/go-pointer" ) // Buffer is a go representation of a GstBuffer. @@ -18,6 +27,30 @@ type Buffer struct { ptr *C.GstBuffer } +// NewEmptyBuffer returns a new empty buffer. +func NewEmptyBuffer() *Buffer { + return wrapBuffer(C.gst_buffer_new()) +} + +// NewBufferAllocate tries to create a newly allocated buffer with data of the given size +// and extra parameters from allocator. If the requested amount of memory can't be allocated, +// nil will be returned. The allocated buffer memory is not cleared. +// +// When allocator is nil, the default memory allocator will be used. +// +// Note that when size == 0, the buffer will not have memory associated with it. +func NewBufferAllocate(alloc *Allocator, params *AllocationParams, size int64) *Buffer { + var gstalloc *C.GstAllocator + if alloc != nil { + gstalloc = alloc.Instance() + } + buf := C.gst_buffer_new_allocate(gstalloc, C.gsize(size), params.Instance()) + if buf == nil { + return nil + } + return wrapBuffer(buf) +} + // NewBufferFromBytes returns a new buffer from the given byte slice. func NewBufferFromBytes(b []byte) *Buffer { str := string(b) @@ -36,9 +69,54 @@ func NewBufferFromReader(rdr io.Reader) (*Buffer, error) { return NewBufferFromBytes(out), nil } +// NewBufferFull allocates a new buffer that wraps the given data. The wrapped buffer will +// have the region from offset and size visible. The maxsize must be at least the size of the +// data provided. +// +// When the buffer is destroyed, notifyFunc will be called if it is not nil. +// +// The prefix/padding must be filled with 0 if flags contains MemoryFlagZeroPrefixed and MemoryFlagZeroPadded respectively. +// +// Example +// +// buf := gst.NewBufferFull(0, []byte("hello-world"), 1024, 0, 1024, func() { +// fmt.Println("buffer was destroyed") +// }) +// if buf != nil { +// buf.Unref() +// } +// +// // > buffer was destroyed +func NewBufferFull(flags MemoryFlags, data []byte, maxSize, offset, size int64, notifyFunc func()) *Buffer { + var notifyData unsafe.Pointer + var gnotifyFunc C.GDestroyNotify + if notifyFunc != nil { + notifyData = gopointer.Save(notifyFunc) + gnotifyFunc = C.GDestroyNotify(C.cgoDestroyNotifyFunc) + } + dataStr := string(data) + dataPtr := unsafe.Pointer(C.CString(dataStr)) + buf := C.gst_buffer_new_wrapped_full( + C.GstMemoryFlags(flags), + (C.gpointer)(dataPtr), + C.gsize(maxSize), C.gsize(offset), C.gsize(size), + (C.gpointer)(notifyData), gnotifyFunc, + ) + if buf == nil { + return nil + } + return wrapBuffer(buf) +} + // Instance returns the underlying GstBuffer instance. func (b *Buffer) Instance() *C.GstBuffer { return C.toGstBuffer(unsafe.Pointer(b.ptr)) } +// Ref increases the ref count on the buffer by one. +func (b *Buffer) Ref() *Buffer { return wrapBuffer(C.gst_buffer_ref(b.Instance())) } + +// Unref decreaes the ref count on the buffer by one. When the refcount reaches zero, the memory is freed. +func (b *Buffer) Unref() { C.gst_buffer_unref(b.Instance()) } + // Reader returns an io.Reader for this buffer. func (b *Buffer) Reader() io.Reader { return bytes.NewBuffer(b.Bytes()) } @@ -104,6 +182,39 @@ func (b *Buffer) Map() *MapInfo { }) } +// AddMeta adds metadata for info to the buffer using the parameters in params. The given +// parameters are passed to the MetaInfo's init function, and as such will only work +// for MetaInfo objects created from the go runtime. +// +// Example +// +// metaInfo := gst.RegisterMeta(glib.TypeFromName("GstObject"), "my-meta", 1024, &gst.MetaInfoCallbackFuncs{ +// InitFunc: func(params interface{}, buffer *gst.Buffer) bool { +// paramStr := params.(string) +// fmt.Println("Buffer initialized with params:", paramStr) +// return true +// }, +// FreeFunc: func(buffer *gst.Buffer) { +// fmt.Println("Buffer was destroyed") +// }, +// }) +// +// buf := gst.NewEmptyBuffer() +// buf.AddMeta(metaInfo, "hello world") +// +// buf.Unref() +// +// // > Buffer initialized with params: hello world +// // > Buffer was destroyed +// +func (b *Buffer) AddMeta(info *MetaInfo, params interface{}) *Meta { + meta := C.gst_buffer_add_meta(b.Instance(), info.Instance(), (C.gpointer)(gopointer.Save(params))) + if meta == nil { + return nil + } + return wrapMeta(meta) +} + // GetMeta retrieves the metadata on the buffer for the given api. If none exists // then nil is returned. func (b *Buffer) GetMeta(api glib.Type) *Meta { diff --git a/gst/gst_meta.go b/gst/gst_meta.go index 8468203..a5f9885 100644 --- a/gst/gst_meta.go +++ b/gst/gst_meta.go @@ -1,6 +1,28 @@ package gst -// #include "gst.go.h" +/* +#include "gst.go.h" + +extern void goMetaFreeFunc (GstMeta * meta, GstBuffer * buffer); +extern gboolean goMetaInitFunc (GstMeta *meta, gpointer params, GstBuffer * buffer); +extern gboolean goMetaTransformFunc (GstBuffer * transBuf, GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data); + +void cgoMetaFreeFunc (GstMeta * meta, GstBuffer * buffer) +{ + goMetaFreeFunc(meta, buffer); +} + +gboolean cgoMetaInitFunc (GstMeta * meta, gpointer params, GstBuffer * buffer) +{ + return goMetaInitFunc(meta, params, buffer); +} + +gboolean cgoMetaTransformFunc (GstBuffer * transBuf, GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data) +{ + return goMetaTransformFunc(transBuf, meta, buffer, type, data); +} + +*/ import "C" import ( "unsafe" @@ -27,6 +49,88 @@ type MetaInfo struct { ptr *C.GstMetaInfo } +// RegisterAPIType registers and returns a GType for the given api name and associates it with tags. +func RegisterAPIType(name string, tags []string) glib.Type { + cTags := gcharStrings(tags) + defer C.g_free((C.gpointer)(unsafe.Pointer(cTags))) + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + newType := C.gst_meta_api_type_register((*C.gchar)(cName), cTags) + return glib.Type(newType) +} + +// GetAPIInfo gets the MetaInfo for the given api type. +func GetAPIInfo(name string) *MetaInfo { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + return wrapMetaInfo(C.gst_meta_get_info((*C.gchar)(cName))) +} + +// GetAPITags retrieves the tags for the given api type. +func GetAPITags(apiType glib.Type) []string { + tags := C.gst_meta_api_type_get_tags(C.GType(apiType)) + return goStrings(C.sizeOfGCharArray(tags), tags) +} + +// APIHasTag returns true if the given api has the given tag. +func APIHasTag(api glib.Type, tag string) bool { + q := newQuarkFromString(tag) + return gobool(C.gst_meta_api_type_has_tag(C.GType(api), q)) +} + +// MetaInitFunc is a function called when meta is initialized in buffer. +type MetaInitFunc func(params interface{}, buffer *Buffer) bool + +// MetaFreeFunc is a function called when meta is freed in buffer. +type MetaFreeFunc func(buffer *Buffer) + +// MetaTransformFunc is a function called for each meta in buf as a result +// of performing a transformation on transbuf. Additional type specific transform +// data is passed to the function as data. +type MetaTransformFunc func(transBuf, buf *Buffer, mType string, data *MetaTransformCopy) bool + +// MetaTransformCopy is extra data passed to a MetaTransformFunc +type MetaTransformCopy struct { + // true if only region is copied + Region bool + // the offset to copy, 0 if region is FALSE, otherwise > 0 + Offset int64 + // the size to copy, -1 or the buffer size when region is FALSE + Size int64 +} + +// MetaInfoCallbackFuncs represents callback functions to includ when registering a new +// meta type. +type MetaInfoCallbackFuncs struct { + InitFunc MetaInitFunc + FreeFunc MetaFreeFunc + TransformFunc MetaTransformFunc +} + +// Register metas internally as well so we can track callback functions +var registeredMetas = make(map[glib.Type]*MetaInfoCallbackFuncs) + +// RegisterMeta registers and returns a new MetaInfo instance denoting the +// given type, name, and size. +func RegisterMeta(api glib.Type, name string, size int64, cbFuncs *MetaInfoCallbackFuncs) *MetaInfo { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + metaInfo := C.gst_meta_register( + C.GType(api), + (*C.gchar)(unsafe.Pointer(cName)), + C.gsize(size), + C.GstMetaInitFunction(C.cgoMetaInitFunc), + C.GstMetaFreeFunction(C.cgoMetaFreeFunc), + C.GstMetaTransformFunction(C.cgoMetaTransformFunc), + ) + if metaInfo == nil { + return nil + } + wrapped := wrapMetaInfo(metaInfo) + registeredMetas[wrapped.Type()] = cbFuncs + return wrapped +} + // Instance returns the underlying GstMetaInfo instance. func (m *MetaInfo) Instance() *C.GstMetaInfo { return m.ptr }