mirror of
https://github.com/go-gst/go-gst.git
synced 2025-10-07 00:43:29 +08:00
start building out audio and rtp libraries
This commit is contained in:
2
go.sum
2
go.sum
@@ -2,3 +2,5 @@ github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o
|
|||||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||||
github.com/tinyzimmer/go-glib v0.0.18 h1:zSlJK5NDcquHK4FFQ2cF6tRavo2Y+6jc3Qowj1sN+oQ=
|
github.com/tinyzimmer/go-glib v0.0.18 h1:zSlJK5NDcquHK4FFQ2cF6tRavo2Y+6jc3Qowj1sN+oQ=
|
||||||
github.com/tinyzimmer/go-glib v0.0.18/go.mod h1:zy2cs6eXSTtqqYrv9/UgYMDfr4pWKuYPSzwX87cBGX4=
|
github.com/tinyzimmer/go-glib v0.0.18/go.mod h1:zy2cs6eXSTtqqYrv9/UgYMDfr4pWKuYPSzwX87cBGX4=
|
||||||
|
github.com/tinyzimmer/go-glib v0.0.19 h1:3DCrMb+tNHKXzpH6k/KXs9Gt3Wghz/nUbD6UT+HaCo4=
|
||||||
|
github.com/tinyzimmer/go-glib v0.0.19/go.mod h1:zy2cs6eXSTtqqYrv9/UgYMDfr4pWKuYPSzwX87cBGX4=
|
||||||
|
38
gst/audio/c_util.go
Normal file
38
gst/audio/c_util.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
|
||||||
|
GstClockTime
|
||||||
|
framesToClockTime (gint frames, gint rate) { return GST_FRAMES_TO_CLOCK_TIME(frames, rate); }
|
||||||
|
|
||||||
|
gint
|
||||||
|
clockTimeToFrames(GstClockTime ct, gint rate) { return GST_CLOCK_TIME_TO_FRAMES(ct, rate); }
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// FramesToDuration calculates the Clocktime (which is usually referred to as a time.Duration in the bindings)
|
||||||
|
// from the given frames and rate.
|
||||||
|
func FramesToDuration(frames, rate int) time.Duration {
|
||||||
|
ct := C.framesToClockTime(C.gint(frames), C.gint(rate))
|
||||||
|
return time.Duration(ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationToFrames calculates the number of frames from the given duration and sample rate.
|
||||||
|
func DurationToFrames(dur time.Duration, rate int) int {
|
||||||
|
return int(C.clockTimeToFrames(C.GstClockTime(dur.Nanoseconds()), C.gint(rate)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// gboolean converts a go bool to a C.gboolean.
|
||||||
|
func gboolean(b bool) C.gboolean {
|
||||||
|
if b {
|
||||||
|
return C.gboolean(1)
|
||||||
|
}
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gobool provides an easy type conversion between C.gboolean and a go bool.
|
||||||
|
func gobool(b C.gboolean) bool {
|
||||||
|
return int(b) > 0
|
||||||
|
}
|
2
gst/audio/doc.go
Normal file
2
gst/audio/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package audio contains bindings for the gstaudio C API.
|
||||||
|
package audio
|
6
gst/audio/gst.go.h
Normal file
6
gst/audio/gst.go.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __GST_AUDIO_GO_H__
|
||||||
|
#define __GST_AUDIO_GO_H__
|
||||||
|
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
|
|
||||||
|
#endif
|
132
gst/audio/gst_audio_buffer.go
Normal file
132
gst/audio/gst_audio_buffer.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
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/tinyzimmer/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) }
|
55
gst/audio/gst_audio_channel_mixer.go
Normal file
55
gst/audio/gst_audio_channel_mixer.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
// /*
|
||||||
|
// #include "gst.go.h"
|
||||||
|
// */
|
||||||
|
// import "C"
|
||||||
|
// import (
|
||||||
|
// "runtime"
|
||||||
|
// "unsafe"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // ChannelMixer is a structure for mixing audio channels.
|
||||||
|
// type ChannelMixer struct {
|
||||||
|
// ptr *C.GstAudioChannelMixer
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // NewChannelMixer creates a new channel mixer with the given parameters.
|
||||||
|
// // It returns nil if the format is not supported.
|
||||||
|
// func NewChannelMixer(flags ChannelMixerFlags, format Format, inPositions, outPositions []ChannelPosition) *ChannelMixer {
|
||||||
|
// mixer := C.gst_audio_channel_mixer_new(
|
||||||
|
// C.GstAudioChannelMixerFlags(flags),
|
||||||
|
// C.GstAudioFormat(format),
|
||||||
|
// C.gint(len(inPositions)),
|
||||||
|
// (*C.GstAudioChannelPosition)(unsafe.Pointer(&inPositions[0])),
|
||||||
|
// C.gint(len(outPositions)),
|
||||||
|
// (*C.GstAudioChannelPosition)(unsafe.Pointer(&outPositions[0])),
|
||||||
|
// )
|
||||||
|
// if mixer == nil {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// wrapped := &ChannelMixer{mixer}
|
||||||
|
// runtime.SetFinalizer(wrapped, (*ChannelMixer).Free)
|
||||||
|
// return wrapped
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // MixSamples will mix the given samples.
|
||||||
|
// //
|
||||||
|
// // In case the samples are interleaved, in must be a slice with a single element of interleaved samples.
|
||||||
|
// //
|
||||||
|
// // If non-interleaved samples are used, in must be a slice of data with an element for each channel
|
||||||
|
|
||||||
|
// // Free frees the channel mixer. The bindings will usually take care of this for you.
|
||||||
|
// func (c *ChannelMixer) Free() { C.gst_audio_channel_mixer_free(c.ptr) }
|
||||||
|
|
||||||
|
// // ChannelMixerFlags are flags used to configure a ChannelMixer
|
||||||
|
// type ChannelMixerFlags int
|
||||||
|
|
||||||
|
// // Castings for ChannelMixerFlags
|
||||||
|
// const (
|
||||||
|
// ChannelMixerFlagsNone ChannelMixerFlags = C.GST_AUDIO_CHANNEL_MIXER_FLAGS_NONE // (0) – no flag
|
||||||
|
// ChannelMixerFlagsNonInterleavedIn ChannelMixerFlags = C.GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN // (1) – input channels are not interleaved
|
||||||
|
// ChannelMixerFlagsNonInterleavedOut ChannelMixerFlags = C.GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT // (2) – output channels are not interleaved
|
||||||
|
// ChannelMixerFlagsUnpositionedIn ChannelMixerFlags = C.GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN // (4) – input channels are explicitly unpositioned
|
||||||
|
// ChannelMixerFlagsUnpositionedOut ChannelMixerFlags = C.GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_OUT // (8) – output channels are explicitly unpositioned
|
||||||
|
// )
|
140
gst/audio/gst_audio_channels.go
Normal file
140
gst/audio/gst_audio_channels.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFallbackChannelMask gets the fallback channel-mask for the given number of channels.
|
||||||
|
func GetFallbackChannelMask(channels int) uint64 {
|
||||||
|
return uint64(C.gst_audio_channel_get_fallback_mask(C.gint(channels)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPositionsFromMask converts the channels present in mask to a position array (which
|
||||||
|
// should have at least channels entries ensured by caller). If mask is set to 0, it is considered
|
||||||
|
// as 'not present' for purpose of conversion. A partially valid mask with less bits set than the
|
||||||
|
// number of channels is considered valid. This function returns nil if the arguments are invalid.
|
||||||
|
func ChannelPositionsFromMask(channels int, mask uint64) []ChannelPosition {
|
||||||
|
var out C.GstAudioChannelPosition
|
||||||
|
ret := C.gst_audio_channel_positions_from_mask(C.gint(channels), C.guint64(mask), &out)
|
||||||
|
if !gobool(ret) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
outsl := make([]ChannelPosition, channels)
|
||||||
|
tmp := (*[1 << 30]C.GstAudioChannelPosition)(unsafe.Pointer(&out))[:channels:channels]
|
||||||
|
for i, s := range tmp {
|
||||||
|
outsl[i] = ChannelPosition(s)
|
||||||
|
}
|
||||||
|
return outsl
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPositionsToMask converts the given channel positions to a bitmask. If forceOrder is true
|
||||||
|
// it additionally checks the channels in the order required by GStreamer.
|
||||||
|
func ChannelPositionsToMask(positions []ChannelPosition, forceOrder bool) (mask uint64, ok bool) {
|
||||||
|
var out C.guint64
|
||||||
|
ok = gobool(C.gst_audio_channel_positions_to_mask(
|
||||||
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&positions[0])),
|
||||||
|
C.gint(len(positions)),
|
||||||
|
gboolean(forceOrder),
|
||||||
|
&out,
|
||||||
|
))
|
||||||
|
if ok {
|
||||||
|
mask = uint64(out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPositionsToString converts the given positions into a human-readable string.
|
||||||
|
func ChannelPositionsToString(positions []ChannelPosition) string {
|
||||||
|
ret := C.gst_audio_channel_positions_to_string(
|
||||||
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&positions[0])),
|
||||||
|
C.gint(len(positions)),
|
||||||
|
)
|
||||||
|
defer C.g_free((C.gpointer)(unsafe.Pointer(ret)))
|
||||||
|
return C.GoString(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPositionsToValidOrder reorders the given positions from any order to the GStreamer order.
|
||||||
|
func ChannelPositionsToValidOrder(positions []ChannelPosition) (ok bool) {
|
||||||
|
ok = gobool(C.gst_audio_channel_positions_to_valid_order(
|
||||||
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&positions[0])),
|
||||||
|
C.gint(len(positions)),
|
||||||
|
))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreValidChannelPositions checks if the positions are all valid. If forceOrder is true, it also checks
|
||||||
|
// if they are in the order required by GStreamer.
|
||||||
|
func AreValidChannelPositions(positions []ChannelPosition, forceOrder bool) bool {
|
||||||
|
return gobool(C.gst_audio_check_valid_channel_positions(
|
||||||
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&positions[0])),
|
||||||
|
C.gint(len(positions)),
|
||||||
|
gboolean(forceOrder),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPosition represents audio channel positions.
|
||||||
|
//
|
||||||
|
// These are the channels defined in SMPTE 2036-2-2008 Table 1 for 22.2 audio systems with the
|
||||||
|
// Surround and Wide channels from DTS Coherent Acoustics (v.1.3.1) and 10.2 and 7.1 layouts.
|
||||||
|
// In the caps the actual channel layout is expressed with a channel count and a channel mask,
|
||||||
|
// which describes the existing channels. The positions in the bit mask correspond to the enum
|
||||||
|
// values. For negotiation it is allowed to have more bits set in the channel mask than the
|
||||||
|
// number of channels to specify the allowed channel positions but this is not allowed in negotiated
|
||||||
|
// caps. It is not allowed in any situation other than the one mentioned below to have less
|
||||||
|
// bits set in the channel mask than the number of channels.
|
||||||
|
//
|
||||||
|
// ChannelPositionMono can only be used with a single mono channel that has no direction information
|
||||||
|
// and would be mixed into all directional channels. This is expressed in caps by having a single
|
||||||
|
// channel and no channel mask.
|
||||||
|
//
|
||||||
|
// ChannelPositionNone can only be used if all channels have this position. This is expressed in caps
|
||||||
|
// by having a channel mask with no bits set.
|
||||||
|
//
|
||||||
|
// As another special case it is allowed to have two channels without a channel mask. This implicitly
|
||||||
|
// means that this is a stereo stream with a front left and front right channel.
|
||||||
|
type ChannelPosition int
|
||||||
|
|
||||||
|
// ChannelPosition castings
|
||||||
|
const (
|
||||||
|
ChannelPositionNone ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_NONE // (-3) – used for position-less channels, e.g. from a sound card that records 1024 channels; mutually exclusive with any other channel position
|
||||||
|
ChannelPositionMono ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_MONO // (-2) – Mono without direction; can only be used with 1 channel
|
||||||
|
ChannelPositionInvalid ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_INVALID // (-1) – invalid position
|
||||||
|
ChannelPositionFrontLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT // (0) – Front left
|
||||||
|
ChannelPositionFrontRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT // (1) – Front right
|
||||||
|
ChannelPositionFrontCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER // (2) – Front center
|
||||||
|
ChannelPositionLFE1 ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_LFE1 // (3) – Low-frequency effects 1 (subwoofer)
|
||||||
|
ChannelPositionRearLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_REAR_LEFT // (4) – Rear left
|
||||||
|
ChannelPositionRearRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT // (5) – Rear right
|
||||||
|
ChannelPositionFrontLeftOfCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER // (6) – Front left of center
|
||||||
|
ChannelPositionFrontRightOfCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER // (7) – Front right of center
|
||||||
|
ChannelPositionRearCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_REAR_CENTER // (8) – Rear center
|
||||||
|
ChannelPositionLFE2 ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_LFE2 // (9) – Low-frequency effects 2 (subwoofer)
|
||||||
|
ChannelPositionSideLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT // (10) – Side left
|
||||||
|
ChannelPositionSideRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT // (11) – Side right
|
||||||
|
ChannelPositionTopFrontLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT // (12) – Top front left
|
||||||
|
ChannelPositionTopFrontRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT // (13) – Top front right
|
||||||
|
ChannelPositionTopFrontCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER // (14) – Top front center
|
||||||
|
ChannelPositionTopCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_CENTER // (15) – Top center
|
||||||
|
ChannelPositionTopRearLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT // (16) – Top rear left
|
||||||
|
ChannelPositionTopRearRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT // (17) – Top rear right
|
||||||
|
ChannelPositionTopSideLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT // (18) – Top side right
|
||||||
|
ChannelPositionTopSideRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT // (19) – Top rear right
|
||||||
|
ChannelPositionTopRearCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER // (20) – Top rear center
|
||||||
|
ChannelPositionBottomFrontCenter ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER // (21) – Bottom front center
|
||||||
|
ChannelPositionBottomFrontLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT // (22) – Bottom front left
|
||||||
|
ChannelPositionBottomFrontRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT // (23) – Bottom front right
|
||||||
|
ChannelPositionWideLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT // (24) – Wide left (between front left and side left)
|
||||||
|
ChannelPositionWideRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT // (25) – Wide right (between front right and side right)
|
||||||
|
ChannelPositionSurroundLeft ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT // (26) – Surround left (between rear left and side left)
|
||||||
|
ChannelPositionSurroundRight ChannelPosition = C.GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT // (27) – Surround right (between rear right and side right)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c ChannelPosition) String() string {
|
||||||
|
// ugly hack
|
||||||
|
return strings.TrimSuffix(strings.TrimPrefix(ChannelPositionsToString([]ChannelPosition{c}), "[ "), " ]")
|
||||||
|
}
|
197
gst/audio/gst_audio_format.go
Normal file
197
gst/audio/gst_audio_format.go
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/tinyzimmer/go-gst/gst"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FormatInfo is a structure containing information about an audio format.
|
||||||
|
type FormatInfo struct {
|
||||||
|
ptr *C.GstAudioFormatInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns the format for this info.
|
||||||
|
func (f *FormatInfo) Format() Format { return Format(f.ptr.format) }
|
||||||
|
|
||||||
|
// Name returns the name for this info.
|
||||||
|
func (f *FormatInfo) Name() string { return C.GoString(f.ptr.name) }
|
||||||
|
|
||||||
|
// Description returns a user readable description for this info.
|
||||||
|
func (f *FormatInfo) Description() string { return C.GoString(f.ptr.description) }
|
||||||
|
|
||||||
|
// Flags returns the flags on this info.
|
||||||
|
func (f *FormatInfo) Flags() FormatFlags { return FormatFlags(f.ptr.flags) }
|
||||||
|
|
||||||
|
// Endianness returns the endianness for this info.
|
||||||
|
func (f *FormatInfo) Endianness() int { return int(f.ptr.endianness) }
|
||||||
|
|
||||||
|
// Width returns the amount of bits used for one sample.
|
||||||
|
func (f *FormatInfo) Width() int { return int(f.ptr.width) }
|
||||||
|
|
||||||
|
// Depth returns the amount of valid bits in width.
|
||||||
|
func (f *FormatInfo) Depth() int { return int(f.ptr.depth) }
|
||||||
|
|
||||||
|
// Silence returns the data for a single silent sample.
|
||||||
|
func (f *FormatInfo) Silence() []byte {
|
||||||
|
return C.GoBytes(unsafe.Pointer(&f.ptr.silence), C.gint(f.Width()/8))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnpackFormat is the format of unpacked samples.
|
||||||
|
func (f *FormatInfo) UnpackFormat() Format { return Format(f.ptr.unpack_format) }
|
||||||
|
|
||||||
|
// Layout represents the layout of audio samples for different channels.
|
||||||
|
type Layout int
|
||||||
|
|
||||||
|
// Castings for Layouts
|
||||||
|
const (
|
||||||
|
LayoutInterleaved Layout = C.GST_AUDIO_LAYOUT_INTERLEAVED // (0) – interleaved audio
|
||||||
|
LayoutNonInterleaved Layout = C.GST_AUDIO_LAYOUT_NON_INTERLEAVED // (1) – non-interleaved audio
|
||||||
|
)
|
||||||
|
|
||||||
|
// FormatFlags are the different audio flags that a format can have.
|
||||||
|
type FormatFlags int
|
||||||
|
|
||||||
|
// Castings for FormatFlags
|
||||||
|
const (
|
||||||
|
FormatFlagInteger FormatFlags = C.GST_AUDIO_FORMAT_FLAG_INTEGER // (1) – integer samples
|
||||||
|
FormatFlagFloat FormatFlags = C.GST_AUDIO_FORMAT_FLAG_FLOAT // (2) – float samples
|
||||||
|
FormatFlagSigned FormatFlags = C.GST_AUDIO_FORMAT_FLAG_SIGNED // (4) – signed samples
|
||||||
|
FormatFlagComplex FormatFlags = C.GST_AUDIO_FORMAT_FLAG_COMPLEX // (16) – complex layout
|
||||||
|
FormatFlagUnpack FormatFlags = C.GST_AUDIO_FORMAT_FLAG_UNPACK // (32) – the format can be used in GstAudioFormatUnpack and GstAudioFormatPack functions
|
||||||
|
)
|
||||||
|
|
||||||
|
// PackFlags are the different flags that can be used when packing and unpacking.
|
||||||
|
type PackFlags int
|
||||||
|
|
||||||
|
// Castings for PackFlags
|
||||||
|
const (
|
||||||
|
PackFlagNone PackFlags = C.GST_AUDIO_PACK_FLAG_NONE // (0) – No flag
|
||||||
|
PackFlagTruncateRange PackFlags = C.GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE // (1) – When the source has a smaller depth than the target format, set the least significant bits of the target to 0. This is likely slightly faster but less accurate. When this flag is not specified, the most significant bits of the source are duplicated in the least significant bits of the destination.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Format is an enum describing the most common audio formats
|
||||||
|
type Format int
|
||||||
|
|
||||||
|
func (f Format) String() string {
|
||||||
|
return C.GoString(C.gst_audio_format_to_string(C.GstAudioFormat(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info returns the info for this Format.
|
||||||
|
func (f Format) Info() *FormatInfo {
|
||||||
|
return &FormatInfo{
|
||||||
|
ptr: C.gst_audio_format_get_info(C.GstAudioFormat(f)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatFromString returns the format for the given string representation.
|
||||||
|
func FormatFromString(format string) Format {
|
||||||
|
f := C.CString(format)
|
||||||
|
defer C.free(unsafe.Pointer(f))
|
||||||
|
return Format(C.gst_audio_format_from_string((*C.gchar)(unsafe.Pointer(f))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LittleEndian represents little-endian format
|
||||||
|
const LittleEndian int = C.G_LITTLE_ENDIAN
|
||||||
|
|
||||||
|
// BigEndian represents big-endian format
|
||||||
|
const BigEndian int = C.G_BIG_ENDIAN
|
||||||
|
|
||||||
|
// FormatFromInteger returns a Format with the given parameters. Signed is whether signed or unsigned format.
|
||||||
|
// Endianness can be either LittleEndian or BigEndian. Width is the amount of bits used per sample, and depth
|
||||||
|
// is the amount of used bits in width.
|
||||||
|
func FormatFromInteger(signed bool, endianness, width, depth int) Format {
|
||||||
|
return Format(C.gst_audio_format_build_integer(
|
||||||
|
gboolean(signed),
|
||||||
|
C.gint(endianness),
|
||||||
|
C.gint(width),
|
||||||
|
C.gint(depth),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawFormats returns all the raw formats supported by GStreamer.
|
||||||
|
func RawFormats() []Format {
|
||||||
|
var l C.guint
|
||||||
|
formats := C.gst_audio_formats_raw(&l)
|
||||||
|
out := make([]Format, int(l))
|
||||||
|
tmpslice := (*[1 << 30]C.GstAudioFormat)(unsafe.Pointer(formats))[:l:l]
|
||||||
|
for i, s := range tmpslice {
|
||||||
|
out[i] = Format(s)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRawCaps returns a generic raw audio caps for the formats defined. If formats is nil, all supported
|
||||||
|
// formats are used.
|
||||||
|
func MakeRawCaps(formats []Format, layout Layout) *gst.Caps {
|
||||||
|
var caps *C.GstCaps
|
||||||
|
if formats == nil {
|
||||||
|
caps = C.gst_audio_make_raw_caps(nil, C.guint(0), C.GstAudioLayout(layout))
|
||||||
|
} else {
|
||||||
|
caps = C.gst_audio_make_raw_caps((*C.GstAudioFormat)(unsafe.Pointer(&formats[0])), C.guint(len(formats)), C.GstAudioLayout(layout))
|
||||||
|
}
|
||||||
|
if caps == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return gst.FromGstCapsUnsafeFull(unsafe.Pointer(caps))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRate is the default sampling rate used in consumer audio
|
||||||
|
const DefaultRate = 44100
|
||||||
|
|
||||||
|
// Castings for Formats
|
||||||
|
const (
|
||||||
|
// DefaultFormat is the default format used in consumer audio
|
||||||
|
DefaultFormat Format = FormatS16LE
|
||||||
|
|
||||||
|
FormatUnknown Format = C.GST_AUDIO_FORMAT_UNKNOWN // (0) – unknown or unset audio format
|
||||||
|
FormatEncoded Format = C.GST_AUDIO_FORMAT_ENCODED // (1) – encoded audio format
|
||||||
|
FormatS8 Format = C.GST_AUDIO_FORMAT_S8 // (2) – 8 bits in 8 bits, signed
|
||||||
|
FormatU8 Format = C.GST_AUDIO_FORMAT_U8 // (3) – 8 bits in 8 bits, unsigned
|
||||||
|
FormatS16LE Format = C.GST_AUDIO_FORMAT_S16LE // (4) – 16 bits in 16 bits, signed, little endian
|
||||||
|
FormatS16BE Format = C.GST_AUDIO_FORMAT_S16BE // (5) – 16 bits in 16 bits, signed, big endian
|
||||||
|
FormatU16LE Format = C.GST_AUDIO_FORMAT_U16LE // (6) – 16 bits in 16 bits, unsigned, little endian
|
||||||
|
FormatU16BE Format = C.GST_AUDIO_FORMAT_U16BE // (7) – 16 bits in 16 bits, unsigned, big endian
|
||||||
|
FormatS2432LE Format = C.GST_AUDIO_FORMAT_S24_32LE // (8) – 24 bits in 32 bits, signed, little endian
|
||||||
|
FormatS2332BE Format = C.GST_AUDIO_FORMAT_S24_32BE // (9) – 24 bits in 32 bits, signed, big endian
|
||||||
|
FormatU2432LE Format = C.GST_AUDIO_FORMAT_U24_32LE // (10) – 24 bits in 32 bits, unsigned, little endian
|
||||||
|
FormatU2432BE Format = C.GST_AUDIO_FORMAT_U24_32BE // (11) – 24 bits in 32 bits, unsigned, big endian
|
||||||
|
FormatS32LE Format = C.GST_AUDIO_FORMAT_S32LE // (12) – 32 bits in 32 bits, signed, little endian
|
||||||
|
FormatS32BE Format = C.GST_AUDIO_FORMAT_S32BE // (13) – 32 bits in 32 bits, signed, big endian
|
||||||
|
FormatU32LE Format = C.GST_AUDIO_FORMAT_U32LE // (14) – 32 bits in 32 bits, unsigned, little endian
|
||||||
|
FormatU32BE Format = C.GST_AUDIO_FORMAT_U32BE // (15) – 32 bits in 32 bits, unsigned, big endian
|
||||||
|
FormatS24LE Format = C.GST_AUDIO_FORMAT_S24LE // (16) – 24 bits in 24 bits, signed, little endian
|
||||||
|
FormatS24BE Format = C.GST_AUDIO_FORMAT_S24BE // (17) – 24 bits in 24 bits, signed, big endian
|
||||||
|
FormatU24LE Format = C.GST_AUDIO_FORMAT_U24LE // (18) – 24 bits in 24 bits, unsigned, little endian
|
||||||
|
FormatU24BE Format = C.GST_AUDIO_FORMAT_U24BE // (19) – 24 bits in 24 bits, unsigned, big endian
|
||||||
|
FormatS20LE Format = C.GST_AUDIO_FORMAT_S20LE // (20) – 20 bits in 24 bits, signed, little endian
|
||||||
|
FormatS20BE Format = C.GST_AUDIO_FORMAT_S20BE // (21) – 20 bits in 24 bits, signed, big endian
|
||||||
|
FormatU20LE Format = C.GST_AUDIO_FORMAT_U20LE // (22) – 20 bits in 24 bits, unsigned, little endian
|
||||||
|
FormatU20BE Format = C.GST_AUDIO_FORMAT_U20BE // (23) – 20 bits in 24 bits, unsigned, big endian
|
||||||
|
FormatS18LE Format = C.GST_AUDIO_FORMAT_S18LE // (24) – 18 bits in 24 bits, signed, little endian
|
||||||
|
FormatS18BE Format = C.GST_AUDIO_FORMAT_S18BE // (25) – 18 bits in 24 bits, signed, big endian
|
||||||
|
FormatU18LE Format = C.GST_AUDIO_FORMAT_U18LE // (26) – 18 bits in 24 bits, unsigned, little endian
|
||||||
|
FormatU18BE Format = C.GST_AUDIO_FORMAT_U18BE // (27) – 18 bits in 24 bits, unsigned, big endian
|
||||||
|
FormatF32LE Format = C.GST_AUDIO_FORMAT_F32LE // (28) – 32-bit floating point samples, little endian
|
||||||
|
FormatF32BE Format = C.GST_AUDIO_FORMAT_F32BE // (29) – 32-bit floating point samples, big endian
|
||||||
|
FormatF64LE Format = C.GST_AUDIO_FORMAT_F64LE // (30) – 64-bit floating point samples, little endian
|
||||||
|
FormatF64BE Format = C.GST_AUDIO_FORMAT_F64BE // (31) – 64-bit floating point samples, big endian
|
||||||
|
FormatS16 Format = C.GST_AUDIO_FORMAT_S16 // (4) – 16 bits in 16 bits, signed, native endianness
|
||||||
|
FormatU16 Format = C.GST_AUDIO_FORMAT_U16 // (6) – 16 bits in 16 bits, unsigned, native endianness
|
||||||
|
FormatS2432 Format = C.GST_AUDIO_FORMAT_S24_32 // (8) – 24 bits in 32 bits, signed, native endianness
|
||||||
|
FormatU2432 Format = C.GST_AUDIO_FORMAT_U24_32 // (10) – 24 bits in 32 bits, unsigned, native endianness
|
||||||
|
FormatS32 Format = C.GST_AUDIO_FORMAT_S32 // (12) – 32 bits in 32 bits, signed, native endianness
|
||||||
|
FormatU32 Format = C.GST_AUDIO_FORMAT_U32 // (14) – 32 bits in 32 bits, unsigned, native endianness
|
||||||
|
FormatS24 Format = C.GST_AUDIO_FORMAT_S24 // (16) – 24 bits in 24 bits, signed, native endianness
|
||||||
|
FormatU24 Format = C.GST_AUDIO_FORMAT_U24 // (18) – 24 bits in 24 bits, unsigned, native endianness
|
||||||
|
FormatS20 Format = C.GST_AUDIO_FORMAT_S20 // (20) – 20 bits in 24 bits, signed, native endianness
|
||||||
|
FormatU20 Format = C.GST_AUDIO_FORMAT_U20 // (22) – 20 bits in 24 bits, unsigned, native endianness
|
||||||
|
FormatS18 Format = C.GST_AUDIO_FORMAT_S18 // (24) – 18 bits in 24 bits, signed, native endianness
|
||||||
|
FormatU18 Format = C.GST_AUDIO_FORMAT_U18 // (26) – 18 bits in 24 bits, unsigned, native endianness
|
||||||
|
FormatF32 Format = C.GST_AUDIO_FORMAT_F32 // (28) – 32-bit floating point samples, native endianness
|
||||||
|
FormatF64 Format = C.GST_AUDIO_FORMAT_F64 // (30) – 64-bit floating point samples, native endianness
|
||||||
|
)
|
124
gst/audio/gst_audio_info.go
Normal file
124
gst/audio/gst_audio_info.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
|
||||||
|
gint channels(GstAudioInfo * info)
|
||||||
|
{
|
||||||
|
return GST_AUDIO_INFO_CHANNELS(info);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/tinyzimmer/go-gst/gst"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Flags contains extra audio flags
|
||||||
|
type Flags int
|
||||||
|
|
||||||
|
// Flags castings
|
||||||
|
const (
|
||||||
|
FlagNone Flags = C.GST_AUDIO_FLAG_NONE // (0) - no valid flag
|
||||||
|
FlagUnpositioned Flags = C.GST_AUDIO_FLAG_UNPOSITIONED // (1) – the position array explicitly contains unpositioned channels.
|
||||||
|
)
|
||||||
|
|
||||||
|
func wrapInfoFull(ptr *C.GstAudioInfo) *Info {
|
||||||
|
info := &Info{ptr}
|
||||||
|
runtime.SetFinalizer(info, (*Info).Free)
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info is a structure used for describing audio properties. This can be filled in from caps
|
||||||
|
// or coverted back to caps.
|
||||||
|
type Info struct {
|
||||||
|
ptr *C.GstAudioInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInfo returns a new Info that is also initialized.
|
||||||
|
func NewInfo() *Info { return wrapInfoFull(C.gst_audio_info_new()) }
|
||||||
|
|
||||||
|
// InfoFromCaps parses the provided caps and creates an info. It returns true if the caps could be parsed.
|
||||||
|
func InfoFromCaps(caps *gst.Caps) (*Info, bool) {
|
||||||
|
info := NewInfo()
|
||||||
|
return info, gobool(C.gst_audio_info_from_caps(info.ptr, (*C.GstCaps)(unsafe.Pointer(caps.Instance()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatInfo returns the format info for the audio.
|
||||||
|
func (i *Info) FormatInfo() *FormatInfo { return &FormatInfo{i.ptr.finfo} }
|
||||||
|
|
||||||
|
// Flags returns additional flags for the audio.
|
||||||
|
func (i *Info) Flags() Flags { return Flags(i.ptr.flags) }
|
||||||
|
|
||||||
|
// Layout returns the audio layout.
|
||||||
|
func (i *Info) Layout() Layout { return Layout(i.ptr.layout) }
|
||||||
|
|
||||||
|
// Rate returns the audio sample rate.
|
||||||
|
func (i *Info) Rate() int { return int(i.ptr.rate) }
|
||||||
|
|
||||||
|
// Channels returns the number of channels.
|
||||||
|
func (i *Info) Channels() int { return int(C.channels(i.ptr)) }
|
||||||
|
|
||||||
|
// BPF returns the number of bytes for one frame. This is the size of one sample * Channels.
|
||||||
|
func (i *Info) BPF() int { return int(i.ptr.bpf) }
|
||||||
|
|
||||||
|
// Positions returns the positions for each channel.
|
||||||
|
func (i *Info) Positions() []ChannelPosition {
|
||||||
|
l := i.Channels()
|
||||||
|
out := make([]ChannelPosition, int(l))
|
||||||
|
tmpslice := (*[1 << 30]C.GstAudioChannelPosition)(unsafe.Pointer(&i.ptr.position))[:l:l]
|
||||||
|
for i, s := range tmpslice {
|
||||||
|
out[i] = ChannelPosition(s)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the info with the default values.
|
||||||
|
func (i *Info) Init() { C.gst_audio_info_init(i.ptr) }
|
||||||
|
|
||||||
|
// Free frees the AudioInfo structure. This is usually handled for you by the bindings.
|
||||||
|
func (i *Info) Free() { C.gst_audio_info_free(i.ptr) }
|
||||||
|
|
||||||
|
// Convert converts among various gst.Format types. This function handles gst.FormatBytes, gst.FormatTime,
|
||||||
|
// and gst.FormatDefault. For raw audio, gst.FormatDefault corresponds to audio frames. This function can
|
||||||
|
// be used to handle pad queries of the type gst.QueryConvert. To provide a value from a time.Duration, use the
|
||||||
|
// Nanoseconds() method.
|
||||||
|
func (i *Info) Convert(srcFmt gst.Format, srcVal int64, destFmt gst.Format) (int64, bool) {
|
||||||
|
var out C.gint64
|
||||||
|
ret := C.gst_audio_info_convert(
|
||||||
|
i.ptr,
|
||||||
|
C.GstFormat(srcFmt),
|
||||||
|
C.gint64(srcVal),
|
||||||
|
C.GstFormat(destFmt),
|
||||||
|
&out,
|
||||||
|
)
|
||||||
|
return int64(out), gobool(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a copy of this Info.
|
||||||
|
func (i *Info) Copy() *Info {
|
||||||
|
return wrapInfoFull(C.gst_audio_info_copy(i.ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEqual checks if the two infos are equal.
|
||||||
|
func (i *Info) IsEqual(info *Info) bool {
|
||||||
|
return gobool(C.gst_audio_info_is_equal(i.ptr, info.ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormat sets the format for this info. This initializes info first and no values are preserved.
|
||||||
|
func (i *Info) SetFormat(format Format, rate int, positions []ChannelPosition) {
|
||||||
|
C.gst_audio_info_set_format(
|
||||||
|
i.ptr,
|
||||||
|
C.GstAudioFormat(format),
|
||||||
|
C.gint(rate),
|
||||||
|
C.gint(len(positions)),
|
||||||
|
(*C.GstAudioChannelPosition)(unsafe.Pointer(&positions[0])),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCaps returns the caps representation of this info.
|
||||||
|
func (i *Info) ToCaps() *gst.Caps {
|
||||||
|
return gst.FromGstCapsUnsafeFull(unsafe.Pointer(C.gst_audio_info_to_caps(i.ptr)))
|
||||||
|
}
|
8
gst/audio/pkg_config.go
Normal file
8
gst/audio/pkg_config.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: gstreamer-plugins-base-1.0
|
||||||
|
#cgo CFLAGS: -Wno-deprecated-declarations
|
||||||
|
#cgo LDFLAGS: -lgstaudio-1.0
|
||||||
|
*/
|
||||||
|
import "C"
|
@@ -20,72 +20,86 @@ func cbWrapElement(elem *C.GstElement) *Element {
|
|||||||
|
|
||||||
//export goGstBinAddElement
|
//export goGstBinAddElement
|
||||||
func goGstBinAddElement(bin *C.GstBin, element *C.GstElement) C.gboolean {
|
func goGstBinAddElement(bin *C.GstBin, element *C.GstElement) C.gboolean {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
var ret bool
|
||||||
caller := elem.(interface {
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
|
caller := obj.(interface {
|
||||||
AddElement(self *Bin, element *Element) bool
|
AddElement(self *Bin, element *Element) bool
|
||||||
})
|
})
|
||||||
return gboolean(caller.AddElement(cbWrapBin(bin), cbWrapElement(element)))
|
ret = caller.AddElement(wrapBin(gobj), cbWrapElement(element))
|
||||||
|
})
|
||||||
|
return gboolean(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinDeepElementAdded
|
//export goGstBinDeepElementAdded
|
||||||
func goGstBinDeepElementAdded(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) {
|
func goGstBinDeepElementAdded(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
caller := elem.(interface {
|
caller := obj.(interface {
|
||||||
DeepElementAdded(self *Bin, subbin *Bin, child *Element)
|
DeepElementAdded(self *Bin, subbin *Bin, child *Element)
|
||||||
})
|
})
|
||||||
caller.DeepElementAdded(cbWrapBin(bin), cbWrapBin(subbin), cbWrapElement(child))
|
caller.DeepElementAdded(wrapBin(gobj), cbWrapBin(subbin), cbWrapElement(child))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinDeepElementRemoved
|
//export goGstBinDeepElementRemoved
|
||||||
func goGstBinDeepElementRemoved(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) {
|
func goGstBinDeepElementRemoved(bin *C.GstBin, subbin *C.GstBin, child *C.GstElement) {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
caller := elem.(interface {
|
caller := obj.(interface {
|
||||||
DeepElementRemoved(self *Bin, subbin *Bin, child *Element)
|
DeepElementRemoved(self *Bin, subbin *Bin, child *Element)
|
||||||
})
|
})
|
||||||
caller.DeepElementRemoved(cbWrapBin(bin), cbWrapBin(subbin), cbWrapElement(child))
|
caller.DeepElementRemoved(wrapBin(gobj), cbWrapBin(subbin), cbWrapElement(child))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinDoLatency
|
//export goGstBinDoLatency
|
||||||
func goGstBinDoLatency(bin *C.GstBin) C.gboolean {
|
func goGstBinDoLatency(bin *C.GstBin) C.gboolean {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
var ret bool
|
||||||
caller := elem.(interface {
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
|
caller := obj.(interface {
|
||||||
DoLatency(self *Bin) bool
|
DoLatency(self *Bin) bool
|
||||||
})
|
})
|
||||||
return gboolean(caller.DoLatency(cbWrapBin(bin)))
|
ret = caller.DoLatency(wrapBin(gobj))
|
||||||
|
})
|
||||||
|
return gboolean(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinElementAdded
|
//export goGstBinElementAdded
|
||||||
func goGstBinElementAdded(bin *C.GstBin, child *C.GstElement) {
|
func goGstBinElementAdded(bin *C.GstBin, child *C.GstElement) {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
caller := elem.(interface {
|
caller := obj.(interface {
|
||||||
ElementAdded(self *Bin, child *Element)
|
ElementAdded(self *Bin, child *Element)
|
||||||
})
|
})
|
||||||
caller.ElementAdded(cbWrapBin(bin), cbWrapElement(child))
|
caller.ElementAdded(wrapBin(gobj), cbWrapElement(child))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinElementRemoved
|
//export goGstBinElementRemoved
|
||||||
func goGstBinElementRemoved(bin *C.GstBin, child *C.GstElement) {
|
func goGstBinElementRemoved(bin *C.GstBin, child *C.GstElement) {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
caller := elem.(interface {
|
caller := obj.(interface {
|
||||||
ElementRemoved(self *Bin, child *Element)
|
ElementRemoved(self *Bin, child *Element)
|
||||||
})
|
})
|
||||||
caller.ElementRemoved(cbWrapBin(bin), cbWrapElement(child))
|
caller.ElementRemoved(wrapBin(gobj), cbWrapElement(child))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinHandleMessage
|
//export goGstBinHandleMessage
|
||||||
func goGstBinHandleMessage(bin *C.GstBin, message *C.GstMessage) {
|
func goGstBinHandleMessage(bin *C.GstBin, message *C.GstMessage) {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
caller := elem.(interface {
|
caller := obj.(interface {
|
||||||
HandleMessage(self *Bin, msg *Message)
|
HandleMessage(self *Bin, msg *Message)
|
||||||
})
|
})
|
||||||
caller.HandleMessage(cbWrapBin(bin), wrapMessage(message))
|
caller.HandleMessage(wrapBin(gobj), wrapMessage(message))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goGstBinRemoveElement
|
//export goGstBinRemoveElement
|
||||||
func goGstBinRemoveElement(bin *C.GstBin, element *C.GstElement) C.gboolean {
|
func goGstBinRemoveElement(bin *C.GstBin, element *C.GstElement) C.gboolean {
|
||||||
elem := glib.FromObjectUnsafePrivate(unsafe.Pointer(bin))
|
var ret bool
|
||||||
caller := elem.(interface {
|
glib.WithPointerTransferOriginal(unsafe.Pointer(bin), func(gobj *glib.Object, obj glib.GoObjectSubclass) {
|
||||||
RemoveElement(self *Bin, element *Element) bool
|
caller := obj.(interface {
|
||||||
|
RemoveElement(self *Bin, child *Element) bool
|
||||||
})
|
})
|
||||||
return gboolean(caller.RemoveElement(cbWrapBin(bin), cbWrapElement(element)))
|
ret = caller.RemoveElement(wrapBin(gobj), cbWrapElement(element))
|
||||||
|
})
|
||||||
|
return gboolean(ret)
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,14 @@ type MetaInfo struct {
|
|||||||
ptr *C.GstMetaInfo
|
ptr *C.GstMetaInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromGstMetaInfoUnsafe wraps the given unsafe pointer in a MetaInfo instance.
|
||||||
|
func FromGstMetaInfoUnsafe(ptr unsafe.Pointer) *MetaInfo {
|
||||||
|
if ptr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &MetaInfo{ptr: (*C.GstMetaInfo)(ptr)}
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterAPIType registers and returns a GType for the given api name and associates it with tags.
|
// RegisterAPIType registers and returns a GType for the given api name and associates it with tags.
|
||||||
func RegisterAPIType(name string, tags []string) glib.Type {
|
func RegisterAPIType(name string, tags []string) glib.Type {
|
||||||
cTags := gcharStrings(tags)
|
cTags := gcharStrings(tags)
|
||||||
|
19
gst/rtp/c_util.go
Normal file
19
gst/rtp/c_util.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package rtp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// gboolean converts a go bool to a C.gboolean.
|
||||||
|
func gboolean(b bool) C.gboolean {
|
||||||
|
if b {
|
||||||
|
return C.gboolean(1)
|
||||||
|
}
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gobool provides an easy type conversion between C.gboolean and a go bool.
|
||||||
|
func gobool(b C.gboolean) bool {
|
||||||
|
return int(b) > 0
|
||||||
|
}
|
2
gst/rtp/doc.go
Normal file
2
gst/rtp/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package rtp contains bindings for the GStreamer RTP library.
|
||||||
|
package rtp
|
6
gst/rtp/gst.go.h
Normal file
6
gst/rtp/gst.go.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __GST_RTP_GO_H__
|
||||||
|
#define __GST_RTP_GO_H__
|
||||||
|
|
||||||
|
#include <gst/rtp/rtp.h>
|
||||||
|
|
||||||
|
#endif
|
95
gst/rtp/gst_rtp_meta.go
Normal file
95
gst/rtp/gst_rtp_meta.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package rtp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "gst.go.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/tinyzimmer/go-glib/glib"
|
||||||
|
"github.com/tinyzimmer/go-gst/gst"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxCSRCCount is the maximum number of elements that can be added to a CSRC.
|
||||||
|
const MaxCSRCCount uint = C.GST_RTP_SOURCE_META_MAX_CSRC_COUNT // 15
|
||||||
|
|
||||||
|
// SourceMeta is a wrapper around GstRTPSourceMeta.
|
||||||
|
type SourceMeta struct {
|
||||||
|
ptr *C.GstRTPSourceMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceMetaAPIType returns the GType for GstRTPSourceMeta.
|
||||||
|
func SourceMetaAPIType() glib.Type {
|
||||||
|
return glib.Type(C.gst_rtp_source_meta_api_get_type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceMetaInfo returns the MetaInfo for GstRTPSourceMeta.
|
||||||
|
func SourceMetaInfo() *gst.MetaInfo {
|
||||||
|
return gst.FromGstMetaInfoUnsafe(unsafe.Pointer(C.gst_rtp_source_meta_get_info()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendCSRC appends the given CSRC to the list of contributing sources in meta.
|
||||||
|
func (s *SourceMeta) AppendCSRC(csrc []uint32) bool {
|
||||||
|
return gobool(C.gst_rtp_source_meta_append_csrc(
|
||||||
|
s.ptr,
|
||||||
|
(*C.guint32)(unsafe.Pointer(&csrc[0])),
|
||||||
|
C.guint(len(csrc)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceMeta retrieves the SourceMeta from the given buffer.
|
||||||
|
func GetSourceMeta(buffer *gst.Buffer) *SourceMeta {
|
||||||
|
meta := C.gst_buffer_get_rtp_source_meta((*C.GstBuffer)(unsafe.Pointer(buffer.Instance())))
|
||||||
|
if meta == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &SourceMeta{ptr: meta}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSourceMeta attaches the given RTP source information to the buffer.
|
||||||
|
func AddSourceMeta(buffer *gst.Buffer, ssrc *uint32, csrc []uint32) *SourceMeta {
|
||||||
|
var meta *C.GstRTPSourceMeta
|
||||||
|
if ssrc == nil && csrc == nil {
|
||||||
|
meta = C.gst_buffer_add_rtp_source_meta(
|
||||||
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
||||||
|
nil, nil, 0,
|
||||||
|
)
|
||||||
|
} else if ssrc == nil {
|
||||||
|
meta = C.gst_buffer_add_rtp_source_meta(
|
||||||
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
||||||
|
nil,
|
||||||
|
(*C.guint32)(unsafe.Pointer(&csrc[0])), C.guint(len(csrc)),
|
||||||
|
)
|
||||||
|
} else if csrc == nil {
|
||||||
|
meta = C.gst_buffer_add_rtp_source_meta(
|
||||||
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
||||||
|
(*C.guint32)(unsafe.Pointer(ssrc)),
|
||||||
|
nil, 0,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
meta = C.gst_buffer_add_rtp_source_meta(
|
||||||
|
(*C.GstBuffer)(unsafe.Pointer(buffer.Instance())),
|
||||||
|
(*C.guint32)(unsafe.Pointer(ssrc)),
|
||||||
|
(*C.guint32)(unsafe.Pointer(&csrc[0])), C.guint(len(csrc)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if meta == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &SourceMeta{ptr: meta}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceCount returns the total number of RTP sources found in this meta, both SSRC and CSRC.
|
||||||
|
func (s *SourceMeta) GetSourceCount() uint {
|
||||||
|
return uint(C.gst_rtp_source_meta_get_source_count(s.ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSSRC sets the SSRC on meta. If ssrc is nil, the SSRC of meta will be unset.
|
||||||
|
func (s *SourceMeta) SetSSRC(ssrc *uint32) bool {
|
||||||
|
if ssrc == nil {
|
||||||
|
return gobool(C.gst_rtp_source_meta_set_ssrc(s.ptr, nil))
|
||||||
|
}
|
||||||
|
return gobool(C.gst_rtp_source_meta_set_ssrc(s.ptr, (*C.guint32)(unsafe.Pointer(ssrc))))
|
||||||
|
}
|
8
gst/rtp/pkg_config.go
Normal file
8
gst/rtp/pkg_config.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package rtp
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: gstreamer-plugins-base-1.0
|
||||||
|
#cgo LDFLAGS: -lgstrtp-1.0
|
||||||
|
#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall
|
||||||
|
*/
|
||||||
|
import "C"
|
@@ -1,5 +1,2 @@
|
|||||||
// Package video contains bindings for the gstvideo C API.
|
// Package video contains bindings for the gstvideo C API.
|
||||||
//
|
|
||||||
// This library should be linked to by getting cflags and libs from gstreamer-plugins-base-1.0.pc
|
|
||||||
// and adding -lgstvideo-1.0 to the library flags.
|
|
||||||
package video
|
package video
|
||||||
|
Reference in New Issue
Block a user