Start implementing StreamImageTaker

This commit is contained in:
Dmitrii Okunev
2025-02-15 22:41:29 +00:00
parent 964a16ee42
commit 77a8bb14a8
6 changed files with 125 additions and 15 deletions

View File

@@ -8,40 +8,53 @@ import (
"github.com/xaionaro-go/recoder"
"github.com/xaionaro-go/streamctl/pkg/streamd/config"
"github.com/xaionaro-go/streamctl/pkg/streamtypes"
"github.com/xaionaro-go/xsync"
)
type imageDataProvider struct {
StreamD *StreamD
OBSServer obs_grpc.OBSServer
OBSState *OBSState
StreamImageTakerLocker xsync.Mutex
StreamImageTakes map[streamtypes.StreamID]*streamImageTaker
}
var _ config.ImageDataProvider = (*imageDataProvider)(nil)
func newImageDataProvider(
streamD *StreamD,
obsServer obs_grpc.OBSServer,
obsState *OBSState,
) *imageDataProvider {
return &imageDataProvider{
StreamD: streamD,
OBSServer: obsServer,
OBSState: obsState,
}
}
func (img *imageDataProvider) GetOBSServer(
func (p *imageDataProvider) GetOBSServer(
ctx context.Context,
) (obs_grpc.OBSServer, error) {
return img.OBSServer, nil
return p.OBSServer, nil
}
func (img *imageDataProvider) GetOBSState(
func (p *imageDataProvider) GetOBSState(
ctx context.Context,
) (*streamtypes.OBSState, error) {
return img.OBSState, nil
return &p.StreamD.OBSState, nil
}
func (img *imageDataProvider) GetCurrentStreamFrame(
func (p *imageDataProvider) GetCurrentStreamFrame(
ctx context.Context,
streamID streamtypes.StreamID,
) ([]byte, recoder.VideoCodec, error) {
return nil, 0, fmt.Errorf("not implemented")
return xsync.DoR3(ctx, &p.StreamImageTakerLocker, func() ([]byte, recoder.VideoCodec, error) {
streamImageTaker := p.StreamImageTakes[streamID]
if streamImageTaker == nil || !streamImageTaker.Keepalive() {
var err error
streamImageTaker, err = p.newStreamImageTaker(ctx, streamID)
if err != nil {
return nil, 0, fmt.Errorf("unable to initialize a stream image taker: %w", err)
}
}
return streamImageTaker.GetLastFrame(ctx)
})
}

View File

@@ -120,7 +120,7 @@ func (d *StreamD) restartImageTakerNoLock(ctx context.Context) error {
return
}
imageDataProvider := newImageDataProvider(obsServer, &d.OBSState)
imageDataProvider := newImageDataProvider(d, obsServer)
for {
var (

View File

@@ -0,0 +1,97 @@
package streamd
import (
"context"
"fmt"
"net/url"
"sort"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/recoder"
"github.com/xaionaro-go/recoder/libav"
"github.com/xaionaro-go/streamctl/pkg/streamserver/types/streamportserver"
"github.com/xaionaro-go/streamctl/pkg/streamtypes"
)
func (p *imageDataProvider) newStreamImageTaker(
ctx context.Context,
streamID streamtypes.StreamID,
) (_ret *streamImageTaker, _err error) {
factory := libav.NewRecoderFactory()
r, err := factory.New(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get a recoder factory: %w", err)
}
defer func() {
if _err != nil {
r.Close()
}
}()
myURL, err := getLocalhostEndpoint(ctx, p.StreamD.StreamServer)
if err != nil {
return nil, fmt.Errorf("unable to get an URL to myself: %w", err)
}
myURL.Path = string(streamID)
input, err := r.NewInputFromURL(ctx, myURL.String(), "", recoder.InputConfig{})
if err != nil {
return nil, fmt.Errorf("unable to open URL '%s': %w", myURL.String(), err)
}
defer func() {
if _err != nil {
input.Close()
}
}()
return nil, fmt.Errorf("not implemented")
}
type streamImageTaker struct {
}
func (p *streamImageTaker) Keepalive() bool {
panic("not implemented")
}
func (p *streamImageTaker) GetLastFrame(
ctx context.Context,
) ([]byte, recoder.VideoCodec, error) {
return nil, 0, fmt.Errorf("not implemented")
}
func getLocalhostEndpoint(
ctx context.Context,
streamServer streamportserver.GetPortServerser,
) (_ret *url.URL, _err error) {
defer func() { logger.Debugf(ctx, "getLocalhostEndpoint result: %v %v", _ret, _err) }()
portSrvs, err := streamServer.GetPortServers(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get port servers info: %w", err)
}
sort.Slice(portSrvs, func(i, j int) bool {
a := &portSrvs[i]
b := &portSrvs[j]
if a.IsTLS != b.IsTLS {
return b.IsTLS
}
return false
})
portSrv := portSrvs[0]
logger.Debugf(ctx, "getLocalhostEndpoint: chosen portSrv == %#+v", portSrv)
protoString := portSrv.Type.String()
if portSrv.IsTLS {
protoString += "s"
}
urlString := fmt.Sprintf("%s://%s", protoString, portSrv.ListenAddr)
urlParsed, err := url.Parse(urlString)
if err != nil {
return nil, fmt.Errorf("unable to parse '%s': %w", urlString, err)
}
return urlParsed, nil
}

View File

@@ -421,7 +421,7 @@ func (w *dashboardWindow) startUpdatingNoLock(
w.renderStreamStatus(ctx)
observability.Go(ctx, func() {
t := time.NewTicker(200 * time.Millisecond)
t := time.NewTicker(500 * time.Millisecond)
for {
select {
case <-ctx.Done():

View File

@@ -346,7 +346,7 @@ func (fwd *ActiveStreamForwarding) openInputFor(
recoderInstance recoder.Recoder,
publisher types.Publisher,
) (recoder.Input, error) {
inputURL, err := fwd.getLocalhostEndpoint(ctx)
inputURL, err := fwd.GetLocalhostEndpoint(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get a localhost endpoint: %w", err)
}

View File

@@ -850,8 +850,8 @@ func (s *StreamForwards) findStreamDestinationByID(
)
}
func (s *StreamForwards) getLocalhostEndpoint(ctx context.Context) (_ret *url.URL, _err error) {
defer func() { logger.Debugf(ctx, "getLocalhostEndpoint result: %v %v", _ret, _err) }()
func (s *StreamForwards) GetLocalhostEndpoint(ctx context.Context) (_ret *url.URL, _err error) {
defer func() { logger.Debugf(ctx, "GetLocalhostEndpoint result: %v %v", _ret, _err) }()
portSrvs, err := s.StreamServer.GetPortServers(ctx)
if err != nil {