package ffmpeg import ( "encoding/json" "errors" "net/url" "strconv" "strings" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/aac" "github.com/AlexxIT/go2rtc/pkg/core" ) type Producer struct { core.Connection url string query url.Values ffmpeg core.Producer } // NewProducer - FFmpeg producer with auto selection video/audio codec based on client capabilities func NewProducer(url string) (core.Producer, error) { p := &Producer{} i := strings.IndexByte(url, '#') p.url, p.query = url[:i], streams.ParseQuery(url[i+1:]) // ffmpeg.NewProducer support only one audio if len(p.query["video"]) != 0 || len(p.query["audio"]) != 1 { return nil, errors.New("ffmpeg: unsupported params: " + url[i:]) } p.ID = core.NewID() p.FormatName = "ffmpeg" p.Medias = []*core.Media{ { // we can support only audio, because don't know FmtpLine for H264 and PayloadType for MJPEG Kind: core.KindAudio, Direction: core.DirectionRecvonly, // codecs in order from best to worst Codecs: []*core.Codec{ // OPUS will always marked as OPUS/48000/2 {Name: core.CodecOpus, ClockRate: 48000, Channels: 2}, {Name: core.CodecPCML, ClockRate: 16000}, {Name: core.CodecPCM, ClockRate: 16000}, {Name: core.CodecPCMA, ClockRate: 16000}, {Name: core.CodecPCMU, ClockRate: 16000}, {Name: core.CodecPCML, ClockRate: 8000}, {Name: core.CodecPCM, ClockRate: 8000}, {Name: core.CodecPCMA, ClockRate: 8000}, {Name: core.CodecPCMU, ClockRate: 8000}, // AAC has unknown problems on Dahua two way {Name: core.CodecAAC, ClockRate: 16000, FmtpLine: aac.FMTP + "1408"}, }, }, } return p, nil } func (p *Producer) Start() error { var err error if p.ffmpeg, err = streams.GetProducer(p.newURL()); err != nil { return err } for i, media := range p.ffmpeg.GetMedias() { track, err := p.ffmpeg.GetTrack(media, media.Codecs[0]) if err != nil { return err } p.Receivers[i].Replace(track) } return p.ffmpeg.Start() } func (p *Producer) Stop() error { if p.ffmpeg == nil { return nil } return p.ffmpeg.Stop() } func (p *Producer) MarshalJSON() ([]byte, error) { if p.ffmpeg == nil { return json.Marshal(p.Connection) } return json.Marshal(p.ffmpeg) } func (p *Producer) newURL() string { s := p.url // rewrite codecs in url from auto to known presets from defaults for _, receiver := range p.Receivers { codec := receiver.Codec switch codec.Name { case core.CodecOpus: s += "#audio=opus/16000" case core.CodecAAC: s += "#audio=aac/16000" case core.CodecPCML: s += "#audio=pcml/" + strconv.Itoa(int(codec.ClockRate)) case core.CodecPCM: s += "#audio=pcm/" + strconv.Itoa(int(codec.ClockRate)) case core.CodecPCMA: s += "#audio=pcma/" + strconv.Itoa(int(codec.ClockRate)) case core.CodecPCMU: s += "#audio=pcmu/" + strconv.Itoa(int(codec.ClockRate)) } } // add other params for key, values := range p.query { if key != "audio" { for _, value := range values { s += "#" + key + "=" + value } } } return s }