finish allocator and meta implementations, add meta implementation to buffers

This commit is contained in:
tinyzimmer
2020-09-29 15:37:22 +03:00
parent b91c738443
commit 87ea132c45
5 changed files with 286 additions and 2 deletions

View File

@@ -31,6 +31,16 @@ func goStrings(argc C.int, argv **C.gchar) []string {
return gostrings 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 // newQuarkFromString creates a new GQuark (or returns an existing one) for the given
// string // string
func newQuarkFromString(str string) C.uint { func newQuarkFromString(str string) C.uint {
@@ -39,3 +49,7 @@ func newQuarkFromString(str string) C.uint {
quark := C.g_quark_from_string(cstr) quark := C.g_quark_from_string(cstr)
return quark return quark
} }
func quarkToString(q C.GQuark) string {
return C.GoString(C.g_quark_to_string(q))
}

View File

@@ -56,3 +56,55 @@ func goBusFunc(bus *C.GstBus, cMsg *C.GstMessage, userData C.gpointer) C.gboolea
return gboolean(true) 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()
}
}

View File

@@ -24,6 +24,9 @@ func NewAllocationParams() *AllocationParams {
return params return params
} }
// Instance returns the underlying GstAllocationParams.
func (a *AllocationParams) Instance() *C.GstAllocationParams { return a.ptr }
// Init initializes these AllocationParams to their original values. // Init initializes these AllocationParams to their original values.
func (a *AllocationParams) Init() { C.gst_allocation_params_init(a.ptr) } func (a *AllocationParams) Init() { C.gst_allocation_params_init(a.ptr) }

View File

@@ -1,6 +1,14 @@
package gst package gst
// #include "gst.go.h" /*
#include "gst.go.h"
extern void goGDestroyNotifyFunc (gpointer data);
void cgoDestroyNotifyFunc (gpointer data) {
goGDestroyNotifyFunc(data);
}
*/
import "C" import "C"
import ( import (
@@ -11,6 +19,7 @@ import (
"unsafe" "unsafe"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
gopointer "github.com/mattn/go-pointer"
) )
// Buffer is a go representation of a GstBuffer. // Buffer is a go representation of a GstBuffer.
@@ -18,6 +27,30 @@ type Buffer struct {
ptr *C.GstBuffer 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. // NewBufferFromBytes returns a new buffer from the given byte slice.
func NewBufferFromBytes(b []byte) *Buffer { func NewBufferFromBytes(b []byte) *Buffer {
str := string(b) str := string(b)
@@ -36,9 +69,54 @@ func NewBufferFromReader(rdr io.Reader) (*Buffer, error) {
return NewBufferFromBytes(out), nil 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. // Instance returns the underlying GstBuffer instance.
func (b *Buffer) Instance() *C.GstBuffer { return C.toGstBuffer(unsafe.Pointer(b.ptr)) } 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. // Reader returns an io.Reader for this buffer.
func (b *Buffer) Reader() io.Reader { return bytes.NewBuffer(b.Bytes()) } 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 // GetMeta retrieves the metadata on the buffer for the given api. If none exists
// then nil is returned. // then nil is returned.
func (b *Buffer) GetMeta(api glib.Type) *Meta { func (b *Buffer) GetMeta(api glib.Type) *Meta {

View File

@@ -1,6 +1,28 @@
package gst 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 "C"
import ( import (
"unsafe" "unsafe"
@@ -27,6 +49,88 @@ type MetaInfo struct {
ptr *C.GstMetaInfo 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. // Instance returns the underlying GstMetaInfo instance.
func (m *MetaInfo) Instance() *C.GstMetaInfo { return m.ptr } func (m *MetaInfo) Instance() *C.GstMetaInfo { return m.ptr }