Files
rtsp-simple-server/internal/staticsources/rpicamera/source.go
2025-04-13 19:13:26 +02:00

156 lines
4.2 KiB
Go

// Package rpicamera contains the Raspberry Pi Camera static source.
package rpicamera
import (
"time"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/defs"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
"github.com/bluenviron/mediamtx/internal/unit"
)
func paramsFromConf(logLevel conf.LogLevel, cnf *conf.Path) params {
return params{
LogLevel: func() string {
switch logLevel {
case conf.LogLevel(logger.Debug):
return "debug"
case conf.LogLevel(logger.Info):
return "info"
case conf.LogLevel(logger.Warn):
return "warn"
}
return "error"
}(),
CameraID: uint32(cnf.RPICameraCamID),
Width: uint32(cnf.RPICameraWidth),
Height: uint32(cnf.RPICameraHeight),
HFlip: cnf.RPICameraHFlip,
VFlip: cnf.RPICameraVFlip,
Brightness: float32(cnf.RPICameraBrightness),
Contrast: float32(cnf.RPICameraContrast),
Saturation: float32(cnf.RPICameraSaturation),
Sharpness: float32(cnf.RPICameraSharpness),
Exposure: cnf.RPICameraExposure,
AWB: cnf.RPICameraAWB,
AWBGainRed: float32(cnf.RPICameraAWBGains[0]),
AWBGainBlue: float32(cnf.RPICameraAWBGains[1]),
Denoise: cnf.RPICameraDenoise,
Shutter: uint32(cnf.RPICameraShutter),
Metering: cnf.RPICameraMetering,
Gain: float32(cnf.RPICameraGain),
EV: float32(cnf.RPICameraEV),
ROI: cnf.RPICameraROI,
HDR: cnf.RPICameraHDR,
TuningFile: cnf.RPICameraTuningFile,
Mode: cnf.RPICameraMode,
FPS: float32(cnf.RPICameraFPS),
AfMode: cnf.RPICameraAfMode,
AfRange: cnf.RPICameraAfRange,
AfSpeed: cnf.RPICameraAfSpeed,
LensPosition: float32(cnf.RPICameraLensPosition),
AfWindow: cnf.RPICameraAfWindow,
FlickerPeriod: uint32(cnf.RPICameraFlickerPeriod),
TextOverlayEnable: cnf.RPICameraTextOverlayEnable,
TextOverlay: cnf.RPICameraTextOverlay,
Codec: cnf.RPICameraCodec,
IDRPeriod: uint32(cnf.RPICameraIDRPeriod),
Bitrate: uint32(cnf.RPICameraBitrate),
Profile: cnf.RPICameraProfile,
Level: cnf.RPICameraLevel,
}
}
// Source is a Raspberry Pi Camera static source.
type Source struct {
LogLevel conf.LogLevel
Parent defs.StaticSourceParent
}
// Log implements logger.Writer.
func (s *Source) Log(level logger.Level, format string, args ...interface{}) {
s.Parent.Log(level, "[RPI Camera source] "+format, args...)
}
// Run implements StaticSource.
func (s *Source) Run(params defs.StaticSourceRunParams) error {
medi := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
}
medias := []*description.Media{medi}
var stream *stream.Stream
onData := func(pts int64, ntp time.Time, au [][]byte) {
if stream == nil {
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
Desc: &description.Session{Medias: medias},
GenerateRTPPackets: true,
})
if res.Err != nil {
return
}
stream = res.Stream
}
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
PTS: pts,
NTP: ntp,
},
AU: au,
})
}
defer func() {
if stream != nil {
s.Parent.SetNotReady(defs.PathSourceStaticSetNotReadyReq{})
}
}()
cam := &camera{
params: paramsFromConf(s.LogLevel, params.Conf),
onData: onData,
}
err := cam.initialize()
if err != nil {
return err
}
defer cam.close()
cameraErr := make(chan error)
go func() {
cameraErr <- cam.wait()
}()
for {
select {
case err := <-cameraErr:
return err
case cnf := <-params.ReloadConf:
cam.reloadParams(paramsFromConf(s.LogLevel, cnf))
case <-params.Context.Done():
return nil
}
}
}
// APISourceDescribe implements StaticSource.
func (*Source) APISourceDescribe() defs.APIPathSourceOrReader {
return defs.APIPathSourceOrReader{
Type: "rpiCameraSource",
ID: "",
}
}