mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-27 04:36:12 +08:00
126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
package ffmpeg
|
|
|
|
import (
|
|
"github.com/AlexxIT/go2rtc/cmd/app"
|
|
"github.com/AlexxIT/go2rtc/cmd/exec"
|
|
"github.com/AlexxIT/go2rtc/cmd/streams"
|
|
"github.com/AlexxIT/go2rtc/pkg/streamer"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func Init() {
|
|
var cfg struct {
|
|
Mod map[string]string `yaml:"ffmpeg"`
|
|
}
|
|
|
|
// defaults
|
|
|
|
cfg.Mod = map[string]string{
|
|
"bin": "ffmpeg",
|
|
|
|
// inputs
|
|
"link": "-hide_banner -i {input}",
|
|
"rtsp": "-hide_banner -fflags nobuffer -flags low_delay -rtsp_transport tcp -i {input}",
|
|
"file": "-hide_banner -re -stream_loop -1 -i {input}",
|
|
|
|
// output
|
|
"out": "-rtsp_transport tcp -f rtsp {output}",
|
|
|
|
// `-g 30` - group of picture, GOP, keyframe interval
|
|
// `-preset superfast` - we can't use ultrafast because it doesn't support `-profile main -level 4.1`
|
|
// `-tune zerolatency` - for minimal latency
|
|
// `-profile main -level 4.1` - most used streaming profile
|
|
"h264": "-codec:v libx264 -g 30 -preset superfast -tune zerolatency -profile main -level 4.1",
|
|
"h264/ultra": "-codec:v libx264 -g 30 -preset ultrafast -tune zerolatency",
|
|
"h264/high": "-codec:v libx264 -g 30 -preset superfast -tune zerolatency",
|
|
"h265": "-codec:v libx265 -g 30 -preset ultrafast -tune zerolatency",
|
|
"opus": "-codec:a libopus -ar 48000 -ac 2",
|
|
"pcmu": "-codec:a pcm_mulaw -ar 8000 -ac 1",
|
|
"pcmu/16000": "-codec:a pcm_mulaw -ar 16000 -ac 1",
|
|
"pcmu/48000": "-codec:a pcm_mulaw -ar 48000 -ac 1",
|
|
"pcma": "-codec:a pcm_alaw -ar 8000 -ac 1",
|
|
"pcma/16000": "-codec:a pcm_alaw -ar 16000 -ac 1",
|
|
"pcma/48000": "-codec:a pcm_alaw -ar 48000 -ac 1",
|
|
"aac/16000": "-codec:a aac -ar 16000 -ac 1",
|
|
}
|
|
|
|
app.LoadConfig(&cfg)
|
|
|
|
tpl := cfg.Mod
|
|
|
|
streams.HandleFunc("ffmpeg", func(s string) (streamer.Producer, error) {
|
|
s = s[7:] // remove `ffmpeg:`
|
|
|
|
var query url.Values
|
|
if i := strings.IndexByte(s, '#'); i > 0 {
|
|
query = parseQuery(s[i+1:])
|
|
s = s[:i]
|
|
}
|
|
|
|
var template string
|
|
switch {
|
|
case strings.HasPrefix(s, "rtsp"):
|
|
template = tpl["rtsp"]
|
|
case strings.Contains(s, "://"):
|
|
template = tpl["link"]
|
|
default:
|
|
template = tpl["file"]
|
|
}
|
|
|
|
s = "exec:" + tpl["bin"] + " " +
|
|
strings.Replace(template, "{input}", s, 1)
|
|
|
|
if query != nil {
|
|
for _, raw := range query["raw"] {
|
|
s += " " + raw
|
|
}
|
|
|
|
// TODO: multiple codecs via -map
|
|
// s += fmt.Sprintf(" -map 0:v:0 -c:v:%d copy", i)
|
|
|
|
for _, video := range query["video"] {
|
|
if video == "copy" {
|
|
s += " -codec:v copy"
|
|
} else {
|
|
s += " " + tpl[video]
|
|
}
|
|
}
|
|
|
|
for _, audio := range query["audio"] {
|
|
if audio == "copy" {
|
|
s += " -codec:v copy"
|
|
} else {
|
|
s += " " + tpl[audio]
|
|
}
|
|
}
|
|
|
|
if query["video"] == nil {
|
|
s += " -vn"
|
|
}
|
|
if query["audio"] == nil {
|
|
s += " -an"
|
|
}
|
|
} else {
|
|
s += " -c copy"
|
|
}
|
|
|
|
s += " " + tpl["out"]
|
|
|
|
return exec.Handle(s)
|
|
})
|
|
}
|
|
|
|
func parseQuery(s string) map[string][]string {
|
|
query := map[string][]string{}
|
|
for _, key := range strings.Split(s, "#") {
|
|
var value string
|
|
i := strings.IndexByte(key, '=')
|
|
if i > 0 {
|
|
key, value = key[:i], key[i+1:]
|
|
}
|
|
query[key] = append(query[key], value)
|
|
}
|
|
return query
|
|
}
|