gstreamer-graphix/gstreamer/pipeline.go

179 lines
4.9 KiB
Go

package gstreamer
import (
"context"
"fmt"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/tinyzimmer/go-gst/gst"
"sync"
)
func (p *Pipeline) Create() (chan PipelineUpdates, error) {
gstreamerLogger := log.With().Str("Component", "gstreamer").Str("Pipeline", p.Name).Logger()
gstreamerLogger.Info().Msg("Creating pipeline")
p.logger = gstreamerLogger
pipeline, err := gst.NewPipeline(p.Name)
if err != nil {
return nil, err
}
p.pipeline = pipeline
elements := map[string]element{}
gstreamerLogger.Debug().Msg("Creating input")
err = p.InputElement.create(pipeline, gstreamerLogger)
if err != nil {
return nil, err
}
for name, e := range p.InputElement.elements {
elements[name] = e
}
lastElement := p.InputElement.getLastElement()
for _, overlay := range p.Overlays {
gstreamerLogger.Debug().Str("Overlay", overlay.getName()).Msg("Adding overlay")
overlayElement, _ := overlay.create(gstreamerLogger)
e := &element{
element: overlayElement,
name: overlay.getName(),
}
err = pipeline.Add(overlayElement)
if err != nil {
log.Error().Msgf("could not add %s to pipeline: %v", overlay.getName(), err)
return nil, err
}
err = lastElement.linkElementWrapper(e, gstreamerLogger)
if err != nil {
log.Error().Msgf("could not link %s to %s: %v", overlay.getName(), lastElement.name, err)
return nil, err
}
lastElement = e
elements[overlay.getName()] = *lastElement
}
gstreamerLogger.Debug().Msg("Creating output")
err = p.OutputElement.create(pipeline, lastElement, gstreamerLogger)
if err != nil {
return nil, err
}
for name, e := range p.OutputElement.elements {
elements[name] = e
}
p.elements = elements
pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool {
switch msg.Type() {
case gst.MessageEOS: // When end-of-stream is received flush the pipeline and stop the main loop
p.logger.Debug().Msg("EOS received")
pipeline.BlockSetState(gst.StateNull)
case gst.MessageError: // Error messages are always fatal
err := msg.ParseError()
p.logger.Error().Msg(err.Error())
if debug := err.DebugString(); debug != "" {
p.logger.Debug().Msg(debug)
}
pipeline.BlockSetState(gst.StateNull)
case gst.MessageStreamStart:
p.logger.Info().Msgf("STARTING Playout of %s", p.Name)
clock := pipeline.GetPipelineClock()
now := clock.GetTime()
for _, overlay := range p.Overlays {
if overlay.getBlendInTime() != -1 {
clock.NewSingleShotID(now + overlay.getBlendInTime()).WaitAsync(overlay.show())
}
if overlay.getBlendOutTime() != -1 {
clock.NewSingleShotID(now + overlay.getBlendOutTime()).WaitAsync(overlay.hide())
}
}
}
return true
})
p.ctx, p.ctxCancel = context.WithCancel(gstreamer.ctx)
updateChannel := make(chan PipelineUpdates)
go p.overlayUpdater(updateChannel, gstreamerLogger)
p.PipelineUpdates = updateChannel
gstreamer.AddPipeline(p)
return updateChannel, nil
}
//TODO check if output is decklink
func (p *Pipeline) Start() error {
p.Lock()
defer p.Unlock()
//output, err := gstreamer.getDecklinkOutput(p.Name)
//setPropertyWrapper(p.elements[p.OutputElement.Name].element, "device-number", output)
err := p.pipeline.SetState(gst.StatePlaying)
if err != nil {
p.logger.Error().Msgf("Failed to set pipeline %s to playing: %v", p.Name, err)
return fmt.Errorf("Failed to set pipeline %s to playing: %v", p.Name, err)
}
return nil
}
func (p *Pipeline) Stop() error {
p.Lock()
defer p.Unlock()
err := p.pipeline.SetState(gst.StateNull)
if err != nil {
p.logger.Error().Msgf("Failed to set pipeline %s to null: %v", p.Name, err)
return fmt.Errorf("Failed to set pipeline %s to null: %v", p.Name, err)
}
return nil
}
func (p *Pipeline) resizeAllTextOverlays() {
for _, overlay := range p.Overlays {
if x, ok := overlay.(*TextOverlay); ok {
go x.resizeTextOverlay(p.logger)
}
}
}
func (p *Pipeline) overlayUpdater(updateChannel chan PipelineUpdates, log zerolog.Logger) {
for {
select {
case <-p.ctx.Done():
return
case updates := <-updateChannel:
log.Info().Msgf("Updating overlays with %d text overlays", len(updates.Overlays))
var wg sync.WaitGroup
p.Lock()
for _, overlay := range updates.Overlays {
wg.Add(1)
go func(overlay Overlay) {
defer wg.Done()
o, ok := p.Overlays[overlay.getName()]
if !ok {
log.Error().Msgf("Could not find overlay %s", overlay.getName())
return
}
overlay.setElement(o.getElement())
overlay.update(log)
p.Overlays[overlay.getName()] = overlay
}(overlay)
}
wg.Wait()
p.Unlock()
p.notifySubscribers()
log.Debug().Msg("Overlays updated")
}
}
}
func (p *Pipeline) SubscribeToUpdates(updateFunc NotifyOnPipelineUpdate) {
p.Lock()
p.notifyOnUpdate = append(p.notifyOnUpdate, updateFunc)
p.Unlock()
}
func (p *Pipeline) notifySubscribers() {
p.RLock()
for _, updateFunc := range p.notifyOnUpdate {
go updateFunc(p)
}
p.RUnlock()
}