mirror of
https://github.com/go-gst/go-gst.git
synced 2025-10-06 00:17:00 +08:00
async state change & pad flags example
This commit is contained in:
232
examples/plugins/async-identity/asyncidentity.go
Normal file
232
examples/plugins/async-identity/asyncidentity.go
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
//lint:file-ignore U1000 Ignore all unused code, this is example code
|
||||||
|
|
||||||
|
// +plugin:Name=async-identity
|
||||||
|
// +plugin:Description=A go-gst example plugin with async state changes
|
||||||
|
// +plugin:Version=v0.0.1
|
||||||
|
// +plugin:License=gst.LicenseLGPL
|
||||||
|
// +plugin:Source=go-gst
|
||||||
|
// +plugin:Package=examples
|
||||||
|
// +plugin:Origin=https://github.com/go-gst/go-gst
|
||||||
|
// +plugin:ReleaseDate=2024-09-13
|
||||||
|
//
|
||||||
|
// +element:Name=asyncidentity
|
||||||
|
// +element:Rank=gst.RankNone
|
||||||
|
// +element:Impl=asyncidentity
|
||||||
|
// +element:Subclass=gst.ExtendsElement
|
||||||
|
//
|
||||||
|
//go:generate gst-plugin-gen
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gst/go-glib/glib"
|
||||||
|
"github.com/go-gst/go-gst/gst"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_cat = gst.NewDebugCategory(
|
||||||
|
"asyncidentity",
|
||||||
|
gst.DebugColorNone,
|
||||||
|
"asyncidentity element",
|
||||||
|
)
|
||||||
|
|
||||||
|
_srcPadTemplate = gst.NewPadTemplate("generic-src", gst.PadDirectionSource,
|
||||||
|
gst.PadPresenceAlways, gst.NewAnyCaps())
|
||||||
|
_sinkPadTemplate = gst.NewPadTemplate("generic-sink", gst.PadDirectionSink,
|
||||||
|
gst.PadPresenceAlways, gst.NewAnyCaps())
|
||||||
|
_properties = []*glib.ParamSpec{
|
||||||
|
glib.NewUint64Param(
|
||||||
|
"delay",
|
||||||
|
"ns state change delay",
|
||||||
|
"Duration in nanoseconds to wait until a state changes",
|
||||||
|
_delayNsMin, _delayNsMax, _delayNsDefault,
|
||||||
|
glib.ParameterReadWrite,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_propDelayNs = 0
|
||||||
|
|
||||||
|
_delayNsMin = uint64(0)
|
||||||
|
_delayNsMax = uint64(time.Second) * 10
|
||||||
|
_delayNsDefault = uint64(time.Second)
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {}
|
||||||
|
|
||||||
|
type asyncidentity struct {
|
||||||
|
// inner state
|
||||||
|
sinkpad *gst.Pad
|
||||||
|
srcpad *gst.Pad
|
||||||
|
|
||||||
|
asyncPending atomic.Bool
|
||||||
|
|
||||||
|
// property storage
|
||||||
|
delayNs atomic.Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ glib.GoObjectSubclass = (*asyncidentity)(nil)
|
||||||
|
|
||||||
|
func (g *asyncidentity) New() glib.GoObjectSubclass { return &asyncidentity{} }
|
||||||
|
|
||||||
|
func (g *asyncidentity) ClassInit(klass *glib.ObjectClass) {
|
||||||
|
class := gst.ToElementClass(klass)
|
||||||
|
class.SetMetadata(
|
||||||
|
"Async Identity Example",
|
||||||
|
"General",
|
||||||
|
"An async state changing identity like element",
|
||||||
|
"Artem Martus <artemmartus2012@gmail.com>",
|
||||||
|
)
|
||||||
|
|
||||||
|
class.AddStaticPadTemplate(_srcPadTemplate)
|
||||||
|
class.AddStaticPadTemplate(_sinkPadTemplate)
|
||||||
|
|
||||||
|
class.InstallProperties(_properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ glib.GoObject = (*asyncidentity)(nil)
|
||||||
|
|
||||||
|
func (g *asyncidentity) SetProperty(obj *glib.Object, id uint, value *glib.Value) {
|
||||||
|
self := gst.ToElement(obj)
|
||||||
|
|
||||||
|
switch id {
|
||||||
|
case _propDelayNs:
|
||||||
|
newDelayErased, err := value.GoValue()
|
||||||
|
if err != nil {
|
||||||
|
self.Error("Failed unmarshalling the 'delay' property", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newDelay, ok := newDelayErased.(uint64)
|
||||||
|
if !ok {
|
||||||
|
self.Error("Failed Go-casting the 'delay' interface{} into uint64",
|
||||||
|
fmt.Errorf("interfaced value: %+v", newDelayErased))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oldDelay := g.delayNs.Swap(newDelay)
|
||||||
|
|
||||||
|
self.Log(_cat, gst.LevelInfo,
|
||||||
|
fmt.Sprintf("Changed delay property %s => %s",
|
||||||
|
time.Duration(oldDelay),
|
||||||
|
time.Duration(newDelay),
|
||||||
|
))
|
||||||
|
default:
|
||||||
|
self.Error("Tried to set unknown property",
|
||||||
|
fmt.Errorf("prop id %d: %s", id, value.TypeName()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *asyncidentity) GetProperty(obj *glib.Object, id uint) *glib.Value {
|
||||||
|
var (
|
||||||
|
out *glib.Value
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch id {
|
||||||
|
case _propDelayNs:
|
||||||
|
out, err = glib.GValue(g.delayNs.Load())
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown property id: %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
self := gst.ToElement(obj)
|
||||||
|
self.Error("Get property error", err)
|
||||||
|
out = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *asyncidentity) Constructed(self *glib.Object) {
|
||||||
|
elem := gst.ToElement(self)
|
||||||
|
srcPad := gst.NewPadFromTemplate(_srcPadTemplate, "src")
|
||||||
|
sinkPad := gst.NewPadFromTemplate(_sinkPadTemplate, "sink")
|
||||||
|
|
||||||
|
sinkPad.SetChainFunction(g.sink_chain_function)
|
||||||
|
|
||||||
|
// Have to set proxy flags on a pads
|
||||||
|
proxyFlags := gst.PadFlagProxyAllocation | gst.PadFlagProxyCaps | gst.PadFlagProxyScheduling
|
||||||
|
sinkPad.SetFlags(proxyFlags)
|
||||||
|
srcPad.SetFlags(proxyFlags)
|
||||||
|
|
||||||
|
// Or setup query & event functions like so
|
||||||
|
|
||||||
|
// sinkPad.SetQueryFunction(func(self *gst.Pad, parent *gst.Object, query *gst.Query) bool {
|
||||||
|
// return srcPad.PeerQuery(query)
|
||||||
|
// })
|
||||||
|
// sinkPad.SetEventFunction(func(self *gst.Pad, parent *gst.Object, event *gst.Event) bool {
|
||||||
|
// return srcPad.PushEvent(event)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// srcPad.SetQueryFunction(func(self *gst.Pad, parent *gst.Object, query *gst.Query) bool {
|
||||||
|
// return sinkPad.PeerQuery(query)
|
||||||
|
// })
|
||||||
|
// srcPad.SetEventFunction(func(self *gst.Pad, parent *gst.Object, event *gst.Event) bool {
|
||||||
|
// return sinkPad.PushEvent(event)
|
||||||
|
// })
|
||||||
|
|
||||||
|
elem.AddPad(srcPad)
|
||||||
|
elem.AddPad(sinkPad)
|
||||||
|
|
||||||
|
g.srcpad = srcPad
|
||||||
|
g.sinkpad = sinkPad
|
||||||
|
|
||||||
|
g.delayNs.Store(_delayNsDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *asyncidentity) sink_chain_function(
|
||||||
|
_self *gst.Pad,
|
||||||
|
_parent *gst.Object,
|
||||||
|
buffer *gst.Buffer,
|
||||||
|
) gst.FlowReturn {
|
||||||
|
|
||||||
|
return g.srcpad.Push(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// var _ gst.ElementImpl = (*asyncidentity)(nil)
|
||||||
|
|
||||||
|
func (g *asyncidentity) ChangeState(el *gst.Element, transition gst.StateChange) gst.StateChangeReturn {
|
||||||
|
if ret := el.ParentChangeState(transition); ret == gst.StateChangeFailure {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
switch transition {
|
||||||
|
case gst.StateChangeNullToReady:
|
||||||
|
// async will be ignored due to target state <= READY
|
||||||
|
case gst.StateChangeReadyToPaused:
|
||||||
|
// async will be ignored due to no_preroll
|
||||||
|
return gst.StateChangeNoPreroll
|
||||||
|
case gst.StateChangePausedToPlaying:
|
||||||
|
fallthrough
|
||||||
|
case gst.StateChangePlayingToPaused:
|
||||||
|
g.asyncStateChange(el)
|
||||||
|
return gst.StateChangeAsync
|
||||||
|
case gst.StateChangePausedToReady:
|
||||||
|
// async will be ignored due to target state <= READY
|
||||||
|
case gst.StateChangeReadyToNull:
|
||||||
|
}
|
||||||
|
|
||||||
|
// check against forcing state change
|
||||||
|
if g.asyncPending.Load() {
|
||||||
|
return gst.StateChangeAsync
|
||||||
|
}
|
||||||
|
|
||||||
|
return gst.StateChangeSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *asyncidentity) asyncStateChange(el *gst.Element) {
|
||||||
|
msg := gst.NewAsyncStartMessage(el)
|
||||||
|
_ = el.PostMessage(msg)
|
||||||
|
go func(el *gst.Element) {
|
||||||
|
g.asyncPending.Store(true)
|
||||||
|
delay := time.Duration(g.delayNs.Load())
|
||||||
|
<-time.After(delay)
|
||||||
|
msg := gst.NewAsyncDoneMessage(el, gst.ClockTimeNone)
|
||||||
|
_ = el.PostMessage(msg)
|
||||||
|
g.asyncPending.Store(false)
|
||||||
|
}(el)
|
||||||
|
}
|
Reference in New Issue
Block a user