mediamtx/internal/core/source_static.go
Azsde 4841189456
Add support for libcamera Autofocus parameters (#1417)
* [DEV #1416] Add support for libcamera Autofocus parameters

* [DEVMINOR] Fix Go formatting

* [DEV] Support AF window parameter for rpiCamera

* [DEV] Update default .yml file to add rpiCameraAfWindow parameter
2023-02-10 12:20:50 +01:00

233 lines
5.5 KiB
Go

package core
import (
"context"
"fmt"
"strings"
"time"
"github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/logger"
"github.com/aler9/rtsp-simple-server/internal/rpicamera"
)
const (
sourceStaticRetryPause = 5 * time.Second
)
type sourceStaticImpl interface {
Log(logger.Level, string, ...interface{})
run(context.Context) error
apiSourceDescribe() interface{}
}
type sourceStaticParent interface {
log(logger.Level, string, ...interface{})
sourceStaticSetReady(context.Context, pathSourceStaticSetReadyReq)
sourceStaticSetNotReady(context.Context, pathSourceStaticSetNotReadyReq)
}
// sourceStatic is a static source.
type sourceStatic struct {
parent sourceStaticParent
ctx context.Context
ctxCancel func()
impl sourceStaticImpl
running bool
done chan struct{}
chSourceStaticImplSetReady chan pathSourceStaticSetReadyReq
chSourceStaticImplSetNotReady chan pathSourceStaticSetNotReadyReq
}
func newSourceStatic(
conf *conf.PathConf,
readTimeout conf.StringDuration,
writeTimeout conf.StringDuration,
readBufferCount int,
parent sourceStaticParent,
) *sourceStatic {
s := &sourceStatic{
parent: parent,
chSourceStaticImplSetReady: make(chan pathSourceStaticSetReadyReq),
chSourceStaticImplSetNotReady: make(chan pathSourceStaticSetNotReadyReq),
}
switch {
case strings.HasPrefix(conf.Source, "rtsp://") ||
strings.HasPrefix(conf.Source, "rtsps://"):
s.impl = newRTSPSource(
conf.Source,
conf.SourceProtocol,
conf.SourceAnyPortEnable,
conf.SourceFingerprint,
readTimeout,
writeTimeout,
readBufferCount,
s)
case strings.HasPrefix(conf.Source, "rtmp://") ||
strings.HasPrefix(conf.Source, "rtmps://"):
s.impl = newRTMPSource(
conf.Source,
conf.SourceFingerprint,
readTimeout,
writeTimeout,
s)
case strings.HasPrefix(conf.Source, "http://") ||
strings.HasPrefix(conf.Source, "https://"):
s.impl = newHLSSource(
conf.Source,
conf.SourceFingerprint,
s)
case conf.Source == "rpiCamera":
s.impl = newRPICameraSource(
rpicamera.Params{
CameraID: conf.RPICameraCamID,
Width: conf.RPICameraWidth,
Height: conf.RPICameraHeight,
HFlip: conf.RPICameraHFlip,
VFlip: conf.RPICameraVFlip,
Brightness: conf.RPICameraBrightness,
Contrast: conf.RPICameraContrast,
Saturation: conf.RPICameraSaturation,
Sharpness: conf.RPICameraSharpness,
Exposure: conf.RPICameraExposure,
AWB: conf.RPICameraAWB,
Denoise: conf.RPICameraDenoise,
Shutter: conf.RPICameraShutter,
Metering: conf.RPICameraMetering,
Gain: conf.RPICameraGain,
EV: conf.RPICameraEV,
ROI: conf.RPICameraROI,
TuningFile: conf.RPICameraTuningFile,
Mode: conf.RPICameraMode,
FPS: conf.RPICameraFPS,
IDRPeriod: conf.RPICameraIDRPeriod,
Bitrate: conf.RPICameraBitrate,
Profile: conf.RPICameraProfile,
Level: conf.RPICameraLevel,
AfMode: conf.RPICameraAfMode,
AfRange: conf.RPICameraAfRange,
AfSpeed: conf.RPICameraAfSpeed,
LensPosition: conf.RPICameraLensPosition,
AfWindow: conf.RPICameraAfWindow,
},
s)
}
return s
}
func (s *sourceStatic) close() {
if s.running {
s.stop()
}
}
func (s *sourceStatic) start() {
if s.running {
panic("should not happen")
}
s.running = true
s.impl.Log(logger.Info, "started")
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
s.done = make(chan struct{})
go s.run()
}
func (s *sourceStatic) stop() {
if !s.running {
panic("should not happen")
}
s.running = false
s.impl.Log(logger.Info, "stopped")
s.ctxCancel()
// we must wait since s.ctx is not thread safe
<-s.done
}
func (s *sourceStatic) log(level logger.Level, format string, args ...interface{}) {
s.parent.log(level, format, args...)
}
func (s *sourceStatic) run() {
defer close(s.done)
outer:
for {
s.runInner()
select {
case <-time.After(sourceStaticRetryPause):
case <-s.ctx.Done():
break outer
}
}
s.ctxCancel()
}
func (s *sourceStatic) runInner() {
innerCtx, innerCtxCancel := context.WithCancel(context.Background())
implErr := make(chan error)
go func() {
implErr <- s.impl.run(innerCtx)
}()
for {
select {
case err := <-implErr:
innerCtxCancel()
s.impl.Log(logger.Info, "ERR: %v", err)
return
case req := <-s.chSourceStaticImplSetReady:
s.parent.sourceStaticSetReady(s.ctx, req)
case req := <-s.chSourceStaticImplSetNotReady:
s.parent.sourceStaticSetNotReady(s.ctx, req)
case <-s.ctx.Done():
innerCtxCancel()
<-implErr
return
}
}
}
// apiSourceDescribe implements source.
func (s *sourceStatic) apiSourceDescribe() interface{} {
return s.impl.apiSourceDescribe()
}
// sourceStaticImplSetReady is called by a sourceStaticImpl.
func (s *sourceStatic) sourceStaticImplSetReady(req pathSourceStaticSetReadyReq) pathSourceStaticSetReadyRes {
req.res = make(chan pathSourceStaticSetReadyRes)
select {
case s.chSourceStaticImplSetReady <- req:
return <-req.res
case <-s.ctx.Done():
return pathSourceStaticSetReadyRes{err: fmt.Errorf("terminated")}
}
}
// sourceStaticImplSetNotReady is called by a sourceStaticImpl.
func (s *sourceStatic) sourceStaticImplSetNotReady(req pathSourceStaticSetNotReadyReq) {
req.res = make(chan struct{})
select {
case s.chSourceStaticImplSetNotReady <- req:
<-req.res
case <-s.ctx.Done():
}
}