Files
go-gst/examples/pad-probes/main.go
2023-08-11 16:24:32 +02:00

115 lines
3.6 KiB
Go

// This example demonstrates the use of GStreamer's pad probe APIs.
//
// Probes are callbacks that can be installed by the application and will notify
// the application about the states of the dataflow. Those are mostly used for
// changing pipelines dynamically at runtime or for inspecting/modifying buffers or events
//
// |-[probe]
// /
// {audiotestsrc} - {fakesink}
package main
import (
"errors"
"fmt"
"math"
"github.com/go-gst/go-glib/glib"
"github.com/go-gst/go-gst/examples"
"github.com/go-gst/go-gst/gst"
)
func padProbes(mainLoop *glib.MainLoop) error {
gst.Init(nil)
// Parse the pipeline we want to probe from a static in-line string.
// Here we give our audiotestsrc a name, so we can retrieve that element
// from the resulting pipeline.
pipeline, err := gst.NewPipelineFromString(
"audiotestsrc name=src ! audio/x-raw,format=S16LE,channels=1 ! fakesink",
)
// Get the audiotestsrc element from the pipeline that GStreamer
// created for us while parsing the launch syntax above.
//
// TODO: There are some discrepancies still between methods that check the nil
// value and return an error, versus those that will instead just return nil.
// Need to settle on one way or the other.
src, err := pipeline.GetElementByName("src")
if err != nil {
return err
}
// Get the audiotestsrc's src-pad.
srcPad := src.GetStaticPad("src")
if srcPad == nil {
return errors.New("src pad on src element was nil")
}
// Add a probe handler on the audiotestsrc's src-pad.
// This handler gets called for every buffer that passes the pad we probe.
srcPad.AddProbe(gst.PadProbeTypeBuffer, func(self *gst.Pad, info *gst.PadProbeInfo) gst.PadProbeReturn {
// Interpret the data sent over the pad as a buffer. We know to expect this because of
// the probe mask defined above.
buffer := info.GetBuffer()
// At this point, buffer is only a reference to an existing memory region somewhere.
// When we want to access its content, we have to map it while requesting the required
// mode of access (read, read/write).
// This type of abstraction is necessary, because the buffer in question might not be
// on the machine's main memory itself, but rather in the GPU's memory.
// So mapping the buffer makes the underlying memory region accessible to us.
// See: https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html
mapInfo := buffer.Map(gst.MapRead)
defer buffer.Unmap()
// We know what format the data in the memory region has, since we requested
// it by setting the fakesink's caps. So what we do here is interpret the
// memory region we mapped as an array of signed 16 bit integers.
samples := mapInfo.AsInt16LESlice()
if len(samples) == 0 {
return gst.PadProbeOK
}
// For each buffer (= chunk of samples) calculate the root mean square.
var square float64
for _, i := range samples {
square += float64(i * i)
}
rms := math.Sqrt(square / float64(len(samples)))
fmt.Println("rms:", rms)
return gst.PadProbeOK
})
// Start the pipeline
pipeline.SetState(gst.StatePlaying)
// Block on messages coming in from the bus instead of using the main loop
for {
msg := pipeline.GetPipelineBus().TimedPop(gst.ClockTimeNone)
if msg == nil {
break
}
if err := handleMessage(msg); err != nil {
return err
}
}
return nil
}
func handleMessage(msg *gst.Message) error {
defer msg.Unref()
switch msg.Type() {
case gst.MessageEOS:
return errors.New("end-of-stream")
case gst.MessageError:
return msg.ParseError()
}
return nil
}
func main() {
examples.RunLoop(padProbes)
}