From f2ab0657c06cefa768b7d4735b5b45e00d36398c Mon Sep 17 00:00:00 2001 From: tinyzimmer <38474291+tinyzimmer@users.noreply.github.com> Date: Tue, 29 Sep 2020 22:05:29 +0300 Subject: [PATCH] add GstBufferPool impl --- gst/cgo_exports.go | 7 ++ gst/gst.go.h | 30 ++++++ gst/gst_buffer_list.go | 126 +++++++++++++++++++++++++ gst/gst_buffer_pool.go | 210 +++++++++++++++++++++++++++++++++++++++++ gst/gst_constants.go | 12 +++ gst/gst_wrappers.go | 12 +++ 6 files changed, 397 insertions(+) create mode 100644 gst/gst_buffer_list.go create mode 100644 gst/gst_buffer_pool.go diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index b325be4..cd26034 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -13,6 +13,13 @@ import ( gopointer "github.com/mattn/go-pointer" ) +//export goBufferListForEachCb +func goBufferListForEachCb(buf **C.GstBuffer, idx C.guint, userData C.gpointer) C.gboolean { + cbIface := gopointer.Restore(unsafe.Pointer(userData)) + cbFunc := cbIface.(func(*Buffer, uint) bool) + return gboolean(cbFunc(wrapBuffer(*buf), uint(idx))) +} + //export goBufferMetaForEachCb func goBufferMetaForEachCb(buf *C.GstBuffer, meta **C.GstMeta, userData C.gpointer) C.gboolean { cbIface := gopointer.Restore(unsafe.Pointer(userData)) diff --git a/gst/gst.go.h b/gst/gst.go.h index 9ac1106..02e9d91 100644 --- a/gst/gst.go.h +++ b/gst/gst.go.h @@ -9,6 +9,7 @@ inline GstAllocator * toGstAllocator (void *p) { return (GST_ALLOCATOR_CAST(p)); } inline GstBin * toGstBin (void *p) { return (GST_BIN(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)); } @@ -53,6 +54,35 @@ inline GstBuffer * makeBufferWritable (GstBuffer * buf) return (gst_buffer_make_writable(buf)); } +inline GType bufferListType () +{ + return GST_TYPE_BUFFER_LIST; +} + +/* BufferList Utilities */ + +inline gboolean bufferListIsWritable (GstBufferList * bufList) +{ + return gst_buffer_list_is_writable(bufList); +} + +inline GstBufferList * makeBufferListWritable (GstBufferList * bufList) +{ + return gst_buffer_list_make_writable(bufList); +} + +inline void addToBufferList (GstBufferList * bufList, GstBuffer * buf) +{ + gst_buffer_list_add(bufList, buf); +} + +/* BufferPool utilities */ + +inline gboolean bufferPoolIsFlushing (GstBufferPool * pool) +{ + return GST_BUFFER_POOL_IS_FLUSHING(pool); +} + /* Object Utilities */ inline GObjectClass * getGObjectClass (void * p) { return (G_OBJECT_GET_CLASS(p)); } diff --git a/gst/gst_buffer_list.go b/gst/gst_buffer_list.go new file mode 100644 index 0000000..cb41be4 --- /dev/null +++ b/gst/gst_buffer_list.go @@ -0,0 +1,126 @@ +package gst + +/* +#include "gst.go.h" + +extern gboolean goBufferListForEachCb (GstBuffer ** buffer, guint idx, gpointer user_data); + +gboolean cgoBufferListForEachCb (GstBuffer ** buffer, guint idx, gpointer user_data) +{ + return goBufferListForEachCb(buffer, idx, user_data); +} +*/ +import "C" +import ( + "unsafe" + + gopointer "github.com/mattn/go-pointer" +) + +// BufferList is a go wrapper around a GstBufferList for grouping Buffers +type BufferList struct { + ptr *C.GstBufferList +} + +// NewBufferList returns a new empty BufferList. +func NewBufferList() *BufferList { + return wrapBufferList(C.gst_buffer_list_new()) +} + +// NewBufferListSized creates a new BufferList with the given size. +func NewBufferListSized(size uint) *BufferList { + return wrapBufferList(C.gst_buffer_list_new_sized(C.guint(size))) +} + +// Instance returns the underlying GstBufferList. +func (b *BufferList) Instance() *C.GstBufferList { return C.toGstBufferList(unsafe.Pointer(b.ptr)) } + +// CalculateSize calculates the size of the data contained in this buffer list by adding the size of all buffers. +func (b *BufferList) CalculateSize() int64 { + return int64(C.gst_buffer_list_calculate_size(b.Instance())) +} + +// Copy creates a shallow copy of the given buffer list. This will make a newly allocated copy of the +// source list with copies of buffer pointers. The refcount of buffers pointed to will be increased by one. +func (b *BufferList) Copy() *BufferList { + return wrapBufferList(C.gst_buffer_list_copy(b.Instance())) +} + +// DeepCopy creates a copy of the given buffer list. This will make a newly allocated copy of each buffer +// that the source buffer list contains. +func (b *BufferList) DeepCopy() *BufferList { + return wrapBufferList(C.gst_buffer_list_copy_deep(b.Instance())) +} + +// IsWritable returns true if this BufferList is writable. +func (b *BufferList) IsWritable() bool { + return gobool(C.bufferListIsWritable(b.Instance())) +} + +// MakeWritable makes a writable buffer list from this one. If the source buffer list is already writable, +// this will simply return the same buffer list. A copy will otherwise be made using Copy. +func (b *BufferList) MakeWritable() *BufferList { + return wrapBufferList(C.makeBufferListWritable(b.Instance())) +} + +// ForEach calls the given function for each buffer in list. +// +// The function can modify the passed buffer pointer or its contents. The return value defines if this +// function returns or if the remaining buffers in the list should be skipped. +func (b *BufferList) ForEach(f func(buf *Buffer, idx uint) bool) { + fPtr := gopointer.Save(f) + defer gopointer.Unref(fPtr) + C.gst_buffer_list_foreach( + b.Instance(), + C.GstBufferListFunc(C.cgoBufferListForEachCb), + (C.gpointer)(unsafe.Pointer(fPtr)), + ) +} + +// GetBufferAt gets the buffer at idx. +// +// You must make sure that idx does not exceed the number of buffers available. +func (b *BufferList) GetBufferAt(idx uint) *Buffer { + return wrapBuffer(C.gst_buffer_list_get(b.Instance(), C.guint(idx))) +} + +// GetWritableBufferAt gets the buffer at idx, ensuring it is a writable buffer. +// +// You must make sure that idx does not exceed the number of buffers available. +func (b *BufferList) GetWritableBufferAt(idx uint) *Buffer { + return wrapBuffer(C.gst_buffer_list_get_writable(b.Instance(), C.guint(idx))) +} + +// Insert inserts a buffer at idx in the list. Other buffers are moved to make room for this new buffer. +// +// A -1 value for idx will append the buffer at the end. +func (b *BufferList) Insert(idx int, buf *Buffer) { + C.gst_buffer_list_insert(b.Instance(), C.gint(idx), buf.Instance()) +} + +// Length returns the number of buffers in the list. +func (b *BufferList) Length() uint { + return uint(C.gst_buffer_list_length(b.Instance())) +} + +// Ref increases the refcount of the given buffer list by one. +// +// Note that the refcount affects the writability of list and its data, see MakeWritable. +// It is important to note that keeping additional references to GstBufferList instances +// can potentially increase the number of memcpy operations in a pipeline. +func (b *BufferList) Ref() *BufferList { + C.gst_buffer_list_ref(b.Instance()) + return b +} + +// Remove removes length buffers from the list starting at index. All following buffers +// are moved to close the gap. +func (b *BufferList) Remove(idx, length uint) { + C.gst_buffer_list_remove(b.Instance(), C.guint(idx), C.guint(length)) +} + +// Unref decreases the refcount of the buffer list. If the refcount reaches 0, the buffer +// list will be freed. +func (b *BufferList) Unref() { + C.gst_buffer_list_unref(b.Instance()) +} diff --git a/gst/gst_buffer_pool.go b/gst/gst_buffer_pool.go new file mode 100644 index 0000000..e06e18b --- /dev/null +++ b/gst/gst_buffer_pool.go @@ -0,0 +1,210 @@ +package gst + +/* +#include "gst.go.h" +*/ +import "C" +import ( + "unsafe" + + "github.com/gotk3/gotk3/glib" +) + +// BufferPool is a go wrapper around a GstBufferPool. +// +// For more information refer to the official documentation: +// https://gstreamer.freedesktop.org/documentation/gstreamer/gstbufferpool.html?gi-language=c +type BufferPool struct{ *Object } + +// NewBufferPool returns a new BufferPool instance. +func NewBufferPool() *BufferPool { + pool := C.gst_buffer_pool_new() + return wrapBufferPool(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(pool))}) +} + +// Instance returns the underlying GstBufferPool instance. +func (b *BufferPool) Instance() *C.GstBufferPool { return C.toGstBufferPool(b.Unsafe()) } + +// IsFlushing returns true if this BufferPool is currently flushing. +func (b *BufferPool) IsFlushing() bool { return gobool(C.bufferPoolIsFlushing(b.Instance())) } + +// BufferPoolAcquireParams represents parameters to an AcquireBuffer call. +type BufferPoolAcquireParams struct { + Format Format // format (GstFormat) – the format of start and stop + Start int64 // start (gint64) – the start position + Stop int64 // stop (gint64) – the stop position + Flags BufferPoolAcquireFlags // flags (GstBufferPoolAcquireFlags) – additional flags +} + +// AcquireBuffer acquires a buffer from this pool. +func (b *BufferPool) AcquireBuffer(params *BufferPoolAcquireParams) (*Buffer, FlowReturn) { + var buf *C.GstBuffer + gparams := &C.GstBufferPoolAcquireParams{ + format: C.GstFormat(params.Format), + start: C.gint64(params.Start), + stop: C.gint64(params.Stop), + flags: C.GstBufferPoolAcquireFlags(params.Flags), + } + ret := C.gst_buffer_pool_acquire_buffer(b.Instance(), &buf, gparams) + return wrapBuffer(buf), FlowReturn(ret) +} + +// GetConfig retrieves a copy of the current configuration of the pool. This configuration can either +// be modified and used for the SetConfig call or it must be freed after usage. +func (b *BufferPool) GetConfig() *BufferPoolConfig { + st := wrapStructure(C.gst_buffer_pool_get_config(b.Instance())) + cfg := BufferPoolConfig(*st) + return &cfg +} + +// GetOptions retrieves a list of supported bufferpool options for the pool. An option would typically +// be enabled with AddOption. +func (b *BufferPool) GetOptions() []string { + opts := C.gst_buffer_pool_get_options(b.Instance()) + defer C.g_free((C.gpointer)(unsafe.Pointer(opts))) + return goStrings(C.sizeOfGCharArray(opts), opts) +} + +// HasOption returns true if this BufferPool supports the given option. +func (b *BufferPool) HasOption(opt string) bool { + gOpt := C.CString(opt) + defer C.free(unsafe.Pointer(gOpt)) + return gobool(C.gst_buffer_pool_has_option(b.Instance(), (*C.gchar)(gOpt))) +} + +// IsActive returns true if this BufferPool is active. A pool can be activated with SetActive. +func (b *BufferPool) IsActive() bool { return gobool(C.gst_buffer_pool_is_active(b.Instance())) } + +// ReleaseBuffer releases the given buffer from the pool. The buffer should have previously been +// allocated from pool with AcquireBiffer. +// +// This function is usually called automatically when the last ref on buffer disappears. +func (b *BufferPool) ReleaseBuffer(buf *Buffer) { + C.gst_buffer_pool_release_buffer(b.Instance(), buf.Instance()) +} + +// SetActive can be used to control the active state of pool. When the pool is inactive, new calls to +// AcquireBuffer will return with FlowFlushing. +// +// Activating the bufferpool will preallocate all resources in the pool based on the configuration of the pool. +// +// Deactivating will free the resources again when there are no outstanding buffers. When there are outstanding buffers, they will be freed as soon as they are all returned to the pool. +func (b *BufferPool) SetActive(active bool) (ok bool) { + return gobool(C.gst_buffer_pool_set_active(b.Instance(), gboolean(active))) +} + +// SetConfig sets the configuration of the pool. If the pool is already configured, and the configurations +// haven't changed, this function will return TRUE. If the pool is active, this method will return FALSE and +// active configurations will remain. Buffers allocated form this pool must be returned or else this function +// will do nothing and return FALSE. +// +// config is a GstStructure that contains the configuration parameters for the pool. A default and mandatory set +// of parameters can be configured with gst_buffer_pool_config_set_params, gst_buffer_pool_config_set_allocator +// and gst_buffer_pool_config_add_option. +// +// If the parameters in config can not be set exactly, this function returns FALSE and will try to update as much +// state as possible. The new state can then be retrieved and refined with GetConfig. +// +// This function takes ownership of the given structure. +func (b *BufferPool) SetConfig(cfg BufferPoolConfig) bool { + return gobool(C.gst_buffer_pool_set_config(b.Instance(), cfg.Instance())) +} + +// SetFlushing enables or disable the flushing state of a pool without freeing or allocating buffers. +func (b *BufferPool) SetFlushing(flushing bool) { + C.gst_buffer_pool_set_flushing(b.Instance(), gboolean(flushing)) +} + +// BufferPoolConfig wraps the Structure interface with extra methods for interacting with BufferPool +// configurations. +type BufferPoolConfig Structure + +// Instance returns the structure backing this config. +func (b *BufferPoolConfig) Instance() *C.GstStructure { return C.toGstStructure(b.ptr) } + +// AddOption enables the option in config. This will instruct the bufferpool to enable the specified option +// on the buffers that it allocates. +// +// The supported options by pool can be retrieved with GetOptions. +func (b *BufferPoolConfig) AddOption(opt string) { + cOpt := C.CString(opt) + defer C.free(unsafe.Pointer(cOpt)) + C.gst_buffer_pool_config_add_option(b.Instance(), (*C.gchar)(unsafe.Pointer(cOpt))) +} + +// GetAllocator retrieves the allocator and params from config. +func (b *BufferPoolConfig) GetAllocator() (*Allocator, *AllocationParams) { + var allocator *C.GstAllocator + var params C.GstAllocationParams + C.gst_buffer_pool_config_get_allocator(b.Instance(), &allocator, ¶ms) + return wrapAllocator(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(allocator))}), &AllocationParams{ptr: ¶ms} +} + +// GetOption retrieves the option at index of the options API array. +func (b *BufferPoolConfig) GetOption(index uint) string { + return C.GoString(C.gst_buffer_pool_config_get_option(b.Instance(), C.guint(index))) +} + +// GetParams retrieves the values from this config. All params return 0 or nil if they could not be fetched. +func (b *BufferPoolConfig) GetParams() (caps *Caps, size, minBuffers, maxBuffers uint) { + var gcaps *C.GstCaps + var gsize, gminBuffers, gmaxBuffers C.guint + ret := gobool(C.gst_buffer_pool_config_get_params( + b.Instance(), + &gcaps, + &gsize, + &gminBuffers, + &gmaxBuffers, + )) + if ret { + return wrapCaps(gcaps), uint(gsize), uint(gminBuffers), uint(gmaxBuffers) + } + return nil, 0, 0, 0 +} + +// HasOption returns true if this config has the given option. +func (b *BufferPoolConfig) HasOption(opt string) bool { + cOpt := C.CString(opt) + defer C.free(unsafe.Pointer(cOpt)) + return gobool(C.gst_buffer_pool_config_has_option(b.Instance(), (*C.gchar)(unsafe.Pointer(cOpt)))) +} + +// NumOptions retrieves the number of values currently stored in the options array of the config structure. +func (b *BufferPoolConfig) NumOptions() uint { + return uint(C.gst_buffer_pool_config_n_options(b.Instance())) +} + +// SetAllocator sets the allocator and params on config. +// +// One of allocator and params can be nil, but not both. When allocator is nil, the default allocator of +// the pool will use the values in param to perform its allocation. When param is nil, the pool will use +// the provided allocator with its default AllocationParams. +// +// A call to SetConfig on the BufferPool can update the allocator and params with the values that it is able to do. +// Some pools are, for example, not able to operate with different allocators or cannot allocate with the values +// specified in params. Use GetConfig on the pool to get the currently used values. +func (b *BufferPoolConfig) SetAllocator(allocator *Allocator, params *AllocationParams) { + C.gst_buffer_pool_config_set_allocator(b.Instance(), allocator.Instance(), params.Instance()) +} + +// SetParams configures the config with the given parameters. +func (b *BufferPoolConfig) SetParams(caps *Caps, size, minBuffers, maxBuffers uint) { + C.gst_buffer_pool_config_set_params( + b.Instance(), + caps.Instance(), + C.guint(size), C.guint(minBuffers), C.guint(maxBuffers), + ) +} + +// Validate that changes made to config are still valid in the context of the expected parameters. This function is a +// helper that can be used to validate changes made by a pool to a config when SetConfig returns FALSE. This expects +// that caps haven't changed and that min_buffers aren't lower then what we initially expected. This does not check if +// options or allocator parameters are still valid, and won't check if size have changed, since changing the size is valid +// to adapt padding. +func (b *BufferPoolConfig) Validate(caps *Caps, size, minBuffers, maxBuffers uint) bool { + return gobool(C.gst_buffer_pool_config_validate_params( + b.Instance(), + caps.Instance(), + C.guint(size), C.guint(minBuffers), C.guint(maxBuffers), + )) +} diff --git a/gst/gst_constants.go b/gst/gst_constants.go index 3d38d08..15b3b1e 100644 --- a/gst/gst_constants.go +++ b/gst/gst_constants.go @@ -52,6 +52,18 @@ const ( BufferCopyDeep BufferCopyFlags = C.GST_BUFFER_COPY_DEEP // (32) – flag indicating that memory should always be copied instead of reffed (Since: 1.2) ) +// BufferPoolAcquireFlags casts GstBufferPoolAcquireFlags to a go type. +type BufferPoolAcquireFlags int + +// Type castings of BufferPoolAcquireFlags +const ( + BufferPoolAcquireFlagNone BufferPoolAcquireFlags = C.GST_BUFFER_POOL_ACQUIRE_FLAG_NONE // (0) – no flags + BufferPoolAcquireFlagKeyUnit BufferPoolAcquireFlags = C.GST_BUFFER_POOL_ACQUIRE_FLAG_KEY_UNIT // (1) – buffer is keyframe + BufferPoolAcquireFlagDontWait BufferPoolAcquireFlags = C.GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT // (2) – when the bufferpool is empty, acquire_buffer will by default block until a buffer is released into the pool again. Setting this flag makes acquire_buffer return GST_FLOW_EOS instead of blocking. + BufferPoolAcquireFlagDiscont BufferPoolAcquireFlags = C.GST_BUFFER_POOL_ACQUIRE_FLAG_DISCONT // (4) – buffer is discont + BufferPoolAcquireFlagLast BufferPoolAcquireFlags = C.GST_BUFFER_POOL_ACQUIRE_FLAG_LAST // (65536) – last flag, subclasses can use private flags starting from this value. +) + // BufferingMode is a representation of GstBufferingMode type BufferingMode int diff --git a/gst/gst_wrappers.go b/gst/gst_wrappers.go index 2915722..2c4b8fb 100644 --- a/gst/gst_wrappers.go +++ b/gst/gst_wrappers.go @@ -99,6 +99,10 @@ func init() { T: glib.Type(C.gst_atomic_queue_get_type()), F: marshalAtomicQueue, }, + { + T: glib.Type(C.bufferListType()), + F: marshalBufferList, + }, // Boxed {T: glib.Type(C.gst_message_get_type()), F: marshalMessage}, @@ -112,6 +116,8 @@ func wrapAllocator(obj *glib.Object) *Allocator { return &Allocator{w func wrapAtomicQueue(queue *C.GstAtomicQueue) *AtomicQueue { return &AtomicQueue{ptr: queue} } func wrapBin(obj *glib.Object) *Bin { return &Bin{wrapElement(obj)} } func wrapBuffer(buf *C.GstBuffer) *Buffer { return &Buffer{ptr: buf} } +func wrapBufferList(bufList *C.GstBufferList) *BufferList { return &BufferList{ptr: bufList} } +func wrapBufferPool(obj *glib.Object) *BufferPool { return &BufferPool{wrapObject(obj)} } func wrapBus(obj *glib.Object) *Bus { return &Bus{Object: wrapObject(obj)} } func wrapClock(obj *glib.Object) *Clock { return &Clock{wrapObject(obj)} } func wrapDevice(obj *glib.Object) *Device { return &Device{wrapObject(obj)} } @@ -280,3 +286,9 @@ func marshalBuffer(p uintptr) (interface{}, error) { c := C.getBufferValue((*C.GValue)(unsafe.Pointer(p))) return wrapBuffer(c), nil } + +func marshalBufferList(p uintptr) (interface{}, error) { + c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) + obj := (*C.GstBufferList)(unsafe.Pointer(c)) + return wrapBufferList(obj), nil +}