diff --git a/gst/cgo_exports.go b/gst/cgo_exports.go index f8f40c5..b325be4 100644 --- a/gst/cgo_exports.go +++ b/gst/cgo_exports.go @@ -13,6 +13,13 @@ import ( gopointer "github.com/mattn/go-pointer" ) +//export goBufferMetaForEachCb +func goBufferMetaForEachCb(buf *C.GstBuffer, meta **C.GstMeta, userData C.gpointer) C.gboolean { + cbIface := gopointer.Restore(unsafe.Pointer(userData)) + cbFunc := cbIface.(func(*Meta) bool) + return gboolean(cbFunc(wrapMeta(*meta))) +} + //export structForEachCb func structForEachCb(fieldID C.GQuark, val *C.GValue, chPtr C.gpointer) C.gboolean { ptr := gopointer.Restore(unsafe.Pointer(chPtr)) diff --git a/gst/gst_buffer.go b/gst/gst_buffer.go index b19dbcb..3206025 100644 --- a/gst/gst_buffer.go +++ b/gst/gst_buffer.go @@ -3,11 +3,17 @@ package gst /* #include "gst.go.h" -extern void goGDestroyNotifyFunc (gpointer data); +extern void goGDestroyNotifyFunc (gpointer data); +extern gboolean goBufferMetaForEachCb (GstBuffer * buffer, GstMeta ** meta, gpointer user_data); void cgoDestroyNotifyFunc (gpointer data) { goGDestroyNotifyFunc(data); } + +gboolean cgoBufferMetaForEachCb (GstBuffer * buffer, GstMeta ** meta, gpointer user_data) +{ + return goBufferMetaForEachCb(buffer, meta, user_data); +} */ import "C" @@ -221,3 +227,170 @@ func (b *Buffer) GetMeta(api glib.Type) *Meta { meta := C.gst_buffer_get_meta(b.Instance(), C.GType(api)) return wrapMeta(meta) } + +// ParentBufferMeta is a go representation of a GstParentBufferMeta +type ParentBufferMeta struct { + Parent *Meta + Buffer *Buffer +} + +// AddParentMeta adds a ParentBufferMeta to this buffer that holds a parent reference +// on the given buffer until the it is freed. +func (b *Buffer) AddParentMeta(buf *Buffer) *ParentBufferMeta { + meta := C.gst_buffer_add_parent_buffer_meta(b.Instance(), buf.Instance()) + return &ParentBufferMeta{ + Parent: wrapMeta(&meta.parent), + Buffer: wrapBuffer(meta.buffer), + } +} + +// AddProtectionMeta attaches ProtectionMeta to this buffer. The structure contains +// cryptographic information relating to the sample contained in the buffer. This +// function takes ownership of the structure. +func (b *Buffer) AddProtectionMeta(info *Structure) *ProtectionMeta { + meta := C.gst_buffer_add_protection_meta(b.Instance(), info.Instance()) + return &ProtectionMeta{ + Meta: wrapMeta(&meta.meta), + Info: wrapStructure(meta.info), + } +} + +// ReferenceTimestampMeta is a go representation of a GstReferenceTimestampMeta. +type ReferenceTimestampMeta struct { + Parent *Meta + Reference *Caps + Timestamp, Duration time.Duration +} + +// AddReferenceTimestampMeta adds a ReferenceTimestampMeta to this buffer that holds a +// timestamp and optional duration (specify -1 to omit) based on a specific timestamp reference. +// +// See the documentation of GstReferenceTimestampMeta for details. +// https://gstreamer.freedesktop.org/documentation/gstreamer/gstbuffer.html?gi-language=c#GstReferenceTimestampMeta +func (b *Buffer) AddReferenceTimestampMeta(ref *Caps, timestamp, duration time.Duration) *ReferenceTimestampMeta { + durClockTime := C.GstClockTime(C.GST_CLOCK_TIME_NONE) + if duration > time.Duration(0) { + durClockTime = C.GstClockTime(duration.Nanoseconds()) + } + tsClockTime := C.GstClockTime(timestamp.Nanoseconds()) + meta := C.gst_buffer_add_reference_timestamp_meta(b.Instance(), ref.Instance(), tsClockTime, durClockTime) + return &ReferenceTimestampMeta{ + Parent: wrapMeta(&meta.parent), + Reference: wrapCaps(meta.reference), + Timestamp: clockTimeToDuration(ClockTime(meta.timestamp)), + Duration: clockTimeToDuration(ClockTime(meta.duration)), + } +} + +// Append will append all the memory from the given buffer to this one. The result buffer will +// contain a concatenation of the memory of the two buffers. +func (b *Buffer) Append(buf *Buffer) *Buffer { + return wrapBuffer(C.gst_buffer_append(b.Instance(), buf.Instance())) +} + +// AppendMemory append the memory block to this buffer. This function takes ownership of +// the memory and thus doesn't increase its refcount. +// +// This function is identical to InsertMemory with an index of -1. +func (b *Buffer) AppendMemory(mem *Memory) { + C.gst_buffer_append_memory(b.Instance(), mem.Instance()) +} + +// AppendRegion will append size bytes at offset from the given buffer to this one. The result +// buffer will contain a concatenation of the memory of this buffer and the requested region of +// the one provided. +func (b *Buffer) AppendRegion(buf *Buffer, offset, size int64) *Buffer { + newbuf := C.gst_buffer_append_region(b.Instance(), buf.Instance(), C.gssize(offset), C.gssize(size)) + return wrapBuffer(newbuf) +} + +// Copy creates a copy of this buffer. This will only copy the buffer's data to a newly allocated +// Memory if needed (if the type of memory requires it), otherwise the underlying data is just referenced. +// Check DeepCopy if you want to force the data to be copied to newly allocated Memory. +func (b *Buffer) Copy() *Buffer { return wrapBuffer(C.gst_buffer_copy(b.Instance())) } + +// DeepCopy creates a copy of the given buffer. This will make a newly allocated copy of the data +// the source buffer contains. +func (b *Buffer) DeepCopy() *Buffer { return wrapBuffer(C.gst_buffer_copy_deep(b.Instance())) } + +// CopyInto copies the information from this buffer into the given one. If the given buffer already +// contains memory and flags contains BufferCopyMemory, the memory from this one will be appended to +// that provided. +// +// Flags indicate which fields will be copied. Offset and size dictate from where and how much memory +// is copied. If size is -1 then all data is copied. The function returns true if the copy was successful. +func (b *Buffer) CopyInto(dest *Buffer, flags BufferCopyFlags, offset, size int64) bool { + ok := C.gst_buffer_copy_into( + dest.Instance(), + b.Instance(), + C.GstBufferCopyFlags(flags), + C.gsize(offset), + C.gsize(size), + ) + return gobool(ok) +} + +// CopyRegion creates a sub-buffer from this one at offset and size. This sub-buffer uses the actual memory +// space of the parent buffer. This function will copy the offset and timestamp fields when the offset is 0. +// If not, they will be set to ClockTimeNone and BufferOffsetNone. +// +// If offset equals 0 and size equals the total size of buffer, the duration and offset end fields are also +// copied. If not they will be set to ClockTimeNone and BufferOffsetNone. +func (b *Buffer) CopyRegion(flags BufferCopyFlags, offset, size int64) *Buffer { + newbuf := C.gst_buffer_copy_region( + b.Instance(), + C.GstBufferCopyFlags(flags), + C.gsize(offset), + C.gsize(size), + ) + return wrapBuffer(newbuf) +} + +// Extract extracts size bytes starting from offset in this buffer. The data extracted may be lower +// than the actual size if the buffer did not contain enough data. +func (b *Buffer) Extract(offset, size int64) []byte { + dest := C.malloc(C.sizeof_char * C.ulong(size)) + defer C.free(dest) + C.gst_buffer_extract(b.Instance(), C.gsize(offset), (C.gpointer)(unsafe.Pointer(dest)), C.gsize(size)) + return C.GoBytes(dest, C.int(size)) +} + +// Fill adds the given byte slice to the buffer at the given offset. The return value reflects the amount +// of data added to the buffer. +func (b *Buffer) Fill(offset int64, data []byte) int64 { + str := string(data) + cStr := C.CString(str) + gsize := C.gst_buffer_fill(b.Instance(), C.gsize(offset), (C.gconstpointer)(unsafe.Pointer(cStr)), C.gsize(len(str))) + return int64(gsize) +} + +// FindMemory looks for the memory blocks that span size bytes starting from offset in buffer. Size can be -1 +// to retrieve all the memory blocks. +// +// Index will contain the index of the first memory block where the byte for offset can be found and length +// contains the number of memory blocks containing the size remaining bytes. Skip contains the number of bytes +// to skip in the memory block at index to get to the byte for offset. All values will be 0 if the memory blocks +// could not be read. +func (b *Buffer) FindMemory(offset, size int64) (index, length uint, skip int64) { + var gindex, glength C.uint + var gskip C.gsize + ok := C.gst_buffer_find_memory(b.Instance(), C.gsize(offset), C.gsize(size), &gindex, &glength, &gskip) + if !gobool(ok) { + return + } + return uint(gindex), uint(glength), int64(gskip) +} + +// ForEachMeta calls the given function for each Meta in this buffer. +// +// The function can modify the passed meta pointer or its contents. The return value defines if this function continues +// or if the remaining metadata items in the buffer should be skipped. +func (b *Buffer) ForEachMeta(f func(meta *Meta) bool) bool { + fPtr := gopointer.Save(f) + defer gopointer.Unref(fPtr) + return gobool(C.gst_buffer_foreach_meta( + b.Instance(), + C.GstBufferForeachMetaFunc(C.cgoBufferMetaForEachCb), + (C.gpointer)(unsafe.Pointer(fPtr)), + )) +} diff --git a/gst/gst_constants.go b/gst/gst_constants.go index 44f5905..c00017f 100644 --- a/gst/gst_constants.go +++ b/gst/gst_constants.go @@ -14,6 +14,22 @@ const ( ClockFormat string = "u:%02u:%02u.%09u" // ClockTimeNone means infinite timeout (unsigned representation of -1) or an otherwise unknown value. ClockTimeNone ClockTime = C.GST_CLOCK_TIME_NONE + // BufferOffsetNone is a constant for no-offset return results. + BufferOffsetNone ClockTime = C.GST_BUFFER_OFFSET_NONE +) + +// BufferCopyFlags casts GstBufferCopyFlags to a go type. +type BufferCopyFlags int + +// Type castings of BufferCopyFlags +const ( + BufferCopyNone BufferCopyFlags = C.GST_BUFFER_COPY_NONE // (0) – copy nothing + BufferCopyBufferFlags BufferCopyFlags = C.GST_BUFFER_COPY_FLAGS // (1) – flag indicating that buffer flags should be copied + BufferCopyTimestamps BufferCopyFlags = C.GST_BUFFER_COPY_TIMESTAMPS // (2) – flag indicating that buffer pts, dts, duration, offset and offset_end should be copied + BufferCopyMeta BufferCopyFlags = C.GST_BUFFER_COPY_META // (4) – flag indicating that buffer meta should be copied + BufferCopyMemory BufferCopyFlags = C.GST_BUFFER_COPY_MEMORY // (8) – flag indicating that buffer memory should be reffed and appended to already existing memory. Unless the memory is marked as NO_SHARE, no actual copy of the memory is made but it is simply reffed. Add GST_BUFFER_COPY_DEEP to force a real copy. + BufferCopyMerge BufferCopyFlags = C.GST_BUFFER_COPY_MERGE // (16) – flag indicating that buffer memory should be merged + BufferCopyDeep BufferCopyFlags = C.GST_BUFFER_COPY_DEEP // (32) – flag indicating that memory should always be copied instead of reffed (Since: 1.2) ) // BufferingMode is a representation of GstBufferingMode diff --git a/gst/gst_protection.go b/gst/gst_protection.go new file mode 100644 index 0000000..fa8dca2 --- /dev/null +++ b/gst/gst_protection.go @@ -0,0 +1,42 @@ +package gst + +//#include "gst.go.h" +import "C" + +// ProtectionMeta is a go wrapper around C GstProtectionMeta. +type ProtectionMeta struct { + Meta *Meta + Info *Structure +} + +// GetProtectionMetaInfo retrieves global ProtectionMetaInfo. +func GetProtectionMetaInfo() *MetaInfo { + return wrapMetaInfo(C.gst_protection_meta_get_info()) +} + +// FilterProtectionSystemByDecryptors tterates the supplied list of UUIDs +// and checks the GstRegistry for all the decryptors supporting one of the supplied UUIDs. +func FilterProtectionSystemByDecryptors(decryptors []string) []string { + gArr := gcharStrings(decryptors) + defer C.g_free((C.gpointer)(gArr)) + avail := C.gst_protection_filter_systems_by_available_decryptors(gArr) + if avail == nil { + return nil + } + defer C.g_free((C.gpointer)(avail)) + return goStrings(C.sizeOfGCharArray(avail), avail) +} + +// SelectProtectionSystem iterates the supplied list of UUIDs and checks the GstRegistry for +// an element that supports one of the supplied UUIDs. If more than one element matches, the +// system ID of the highest ranked element is selected. +func SelectProtectionSystem(decryptors []string) string { + gArr := gcharStrings(decryptors) + defer C.g_free((C.gpointer)(gArr)) + avail := C.gst_protection_select_system(gArr) + if avail == nil { + return "" + } + defer C.g_free((C.gpointer)(avail)) + return C.GoString(avail) +}