mirror of
https://github.com/go-gst/go-gst.git
synced 2025-09-26 20:11:18 +08:00
133 lines
4.9 KiB
Go
133 lines
4.9 KiB
Go
package audio
|
|
|
|
/*
|
|
#include "gst.go.h"
|
|
|
|
gpointer audioBufferPlaneData(GstAudioBuffer * buffer, gint plane)
|
|
{
|
|
return GST_AUDIO_BUFFER_PLANE_DATA(buffer, plane);
|
|
}
|
|
|
|
gint audioBufferPlaneSize(GstAudioBuffer * buffer)
|
|
{
|
|
return GST_AUDIO_BUFFER_PLANE_SIZE(buffer);
|
|
}
|
|
|
|
*/
|
|
import "C"
|
|
import (
|
|
"unsafe"
|
|
|
|
"github.com/go-gst/go-gst/gst"
|
|
)
|
|
|
|
// ClipBuffer will return a new buffer clipped to the given segment. The given buffer is no longer valid.
|
|
// The returned buffer may be nil if it is completely outside the configured segment.
|
|
func ClipBuffer(buffer *gst.Buffer, segment *gst.Segment, rate, bytesPerFrame int) *gst.Buffer {
|
|
buf := C.gst_audio_buffer_clip(
|
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Ref().Instance())),
|
|
(*C.GstSegment)(unsafe.Pointer(segment.Instance())),
|
|
C.gint(rate),
|
|
C.gint(bytesPerFrame),
|
|
)
|
|
if buf == nil {
|
|
return nil
|
|
}
|
|
return gst.FromGstBufferUnsafeFull(unsafe.Pointer(buf))
|
|
}
|
|
|
|
// ReorderChannels reorders the buffer against the given positions. The number of channels in each slice
|
|
// must be identical.
|
|
func ReorderChannels(buffer *gst.Buffer, format Format, from []ChannelPosition, to []ChannelPosition) bool {
|
|
return gobool(C.gst_audio_buffer_reorder_channels(
|
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
|
C.GstAudioFormat(format),
|
|
C.gint(len(from)),
|
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&from[0])),
|
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&to[0])),
|
|
))
|
|
}
|
|
|
|
// TruncateBuffer truncates the buffer to finally have the given number of samples, removing the necessary
|
|
// amount of samples from the end and trim number of samples from the beginning. The original buffer is no
|
|
// longer valid. The returned buffer may be nil if the arguments were invalid.
|
|
func TruncateBuffer(buffer *gst.Buffer, bytesPerFrame int, trim, samples int64) *gst.Buffer {
|
|
buf := C.gst_audio_buffer_truncate(
|
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Ref().Instance())),
|
|
C.gint(bytesPerFrame),
|
|
C.gsize(trim),
|
|
C.gsize(samples),
|
|
)
|
|
if buf == nil {
|
|
return nil
|
|
}
|
|
return gst.FromGstBufferUnsafeFull(unsafe.Pointer(buf))
|
|
}
|
|
|
|
// MapBuffer maps an audio gst.Buffer so that it can be read or written.
|
|
//
|
|
// This is especially useful when the gstbuffer is in non-interleaved (planar) layout, in which case this
|
|
// function will use the information in the gstbuffer's attached GstAudioMeta in order to map each channel
|
|
// in a separate "plane" in GstAudioBuffer. If a GstAudioMeta is not attached on the gstbuffer, then it must
|
|
// be in interleaved layout.
|
|
//
|
|
// If a GstAudioMeta is attached, then the GstAudioInfo on the meta is checked against info. Normally, they
|
|
// should be equal, but in case they are not, a g_critical will be printed and the GstAudioInfo from the meta
|
|
// will be used.
|
|
//
|
|
// In non-interleaved buffers, it is possible to have each channel on a separate GstMemory. In this case, each
|
|
// memory will be mapped separately to avoid copying their contents in a larger memory area. Do note though
|
|
// that it is not supported to have a single channel spanning over two or more different GstMemory objects.
|
|
// Although the map operation will likely succeed in this case, it will be highly sub-optimal and it is
|
|
// recommended to merge all the memories in the buffer before calling this function.
|
|
func MapBuffer(info *Info, buffer *gst.Buffer, flags gst.MapFlags) (*Buffer, bool) {
|
|
var audioBuffer C.GstAudioBuffer
|
|
ret := gobool(C.gst_audio_buffer_map(
|
|
&audioBuffer,
|
|
info.ptr,
|
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
|
C.GstMapFlags(flags)),
|
|
)
|
|
if !ret {
|
|
return nil, ret
|
|
}
|
|
return &Buffer{ptr: &audioBuffer}, ret
|
|
}
|
|
|
|
// Buffer is a structure containing the result of a MapBuffer operation. For non-interleaved (planar) buffers,
|
|
// the beginning of each channel in the buffer has its own pointer in the planes array. For interleaved
|
|
// buffers, the Planes slice only contains one item, which is the pointer to the beginning of the buffer,
|
|
// and NumPlanes equals 1.
|
|
//
|
|
// The different channels in planes are always in the GStreamer channel order.
|
|
type Buffer struct {
|
|
ptr *C.GstAudioBuffer
|
|
}
|
|
|
|
// NumSamples returns the size of the buffer in samples.
|
|
func (b *Buffer) NumSamples() int64 { return int64(b.ptr.n_samples) }
|
|
|
|
// NumPlanes returns the number of available planes.
|
|
func (b *Buffer) NumPlanes() int { return int(b.ptr.n_planes) }
|
|
|
|
// Planes returns the planes inside the mapped buffer.
|
|
func (b *Buffer) Planes() [][]byte {
|
|
out := make([][]byte, b.NumPlanes())
|
|
for i := 0; i < b.NumPlanes(); i++ {
|
|
out[i] = C.GoBytes(
|
|
unsafe.Pointer(C.audioBufferPlaneData(b.ptr, C.gint(i))),
|
|
C.audioBufferPlaneSize(b.ptr),
|
|
)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// WritePlane writes data to the plane at the given index.
|
|
func (b *Buffer) WritePlane(idx int, data []byte) {
|
|
bufdata := C.audioBufferPlaneData(b.ptr, C.gint(idx))
|
|
C.memcpy(unsafe.Pointer(bufdata), unsafe.Pointer(&data[0]), C.gsize(len(data)))
|
|
}
|
|
|
|
// Unmap will unmap the mapped buffer. Use this after calling MapBuffer.
|
|
func (b *Buffer) Unmap() { C.gst_audio_buffer_unmap(b.ptr) }
|