Rewrite FFmpeg devices and add support ALSA for Linux

This commit is contained in:
Alexey Khit
2023-07-04 13:02:10 +03:00
parent 4283ae1022
commit 209d7b47d9
4 changed files with 130 additions and 53 deletions
+28 -11
View File
@@ -3,24 +3,41 @@ package device
import ( import (
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"net/url"
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "strings"
) )
// https://trac.ffmpeg.org/wiki/Capture/Webcam func queryToInput(query url.Values) string {
const deviceInputPrefix = "-f avfoundation" video := query.Get("video")
audio := query.Get("audio")
func deviceInputSuffix(video, audio string) string { if video == "" && audio == "" {
switch { return ""
case video != "" && audio != "":
return `"` + video + `:` + audio + `"`
case video != "":
return `"` + video + `"`
case audio != "":
return `":` + audio + `"`
} }
return ""
// https://ffmpeg.org/ffmpeg-devices.html#avfoundation
input := "-f avfoundation"
if video != "" {
video = indexToItem(videos, video)
for key, value := range query {
switch key {
case "resolution":
input += " -video_size " + value[0]
case "pixel_format", "framerate", "video_size", "capture_cursor", "capture_mouse_clicks", "capture_raw_data":
input += " -" + key + " " + value[0]
}
}
}
if audio != "" {
audio = indexToItem(audios, audio)
}
return input + ` -i "` + video + `:` + audio + `"`
} }
func initDevices() { func initDevices() {
+33 -5
View File
@@ -3,19 +3,36 @@ package device
import ( import (
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"net/url"
"os" "os"
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "strings"
) )
// https://trac.ffmpeg.org/wiki/Capture/Webcam func queryToInput(query url.Values) string {
const deviceInputPrefix = "-f v4l2" if video := query.Get("video"); video != "" {
// https://ffmpeg.org/ffmpeg-devices.html#video4linux2_002c-v4l2
input := "-f v4l2"
func deviceInputSuffix(video, audio string) string { for key, value := range query {
if video != "" { switch key {
return video case "resolution":
input += " -video_size " + value[0]
case "video_size", "pixel_format", "input_format", "framerate", "use_libv4l2":
input += " -" + key + " " + value[0]
}
}
return input + " -i " + indexToItem(videos, video)
} }
if audio := query.Get("audio"); audio != "" {
input := "-f alsa"
return input + " -i " + indexToItem(audios, audio)
}
return "" return ""
} }
@@ -57,4 +74,15 @@ func initDevices() {
streams = append(streams, stream) streams = append(streams, stream)
} }
} }
err = exec.Command(Bin, "-f", "alsa", "-i", "default", "-t", "1", "-f", "null", "-").Run()
if err == nil {
stream := api.Stream{
Name: "ALSA default",
URL: "ffmpeg:device?audio=default#audio=opus",
}
audios = append(audios, "default")
streams = append(streams, stream)
}
} }
+48 -2
View File
@@ -3,12 +3,58 @@ package device
import ( import (
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"net/url"
"os/exec" "os/exec"
"regexp" "regexp"
) )
// https://trac.ffmpeg.org/wiki/DirectShow func queryToInput(query url.Values) string {
const deviceInputPrefix = "-f dshow" video := query.Get("video")
audio := query.Get("audio")
if video == "" && audio == "" {
return ""
}
// https://ffmpeg.org/ffmpeg-devices.html#dshow
input := "-f dshow"
if video != "" {
video = indexToItem(videos, video)
for key, value := range query {
switch key {
case "resolution":
input += " -video_size " + value[0]
case "video_size", "framerate", "pixel_format":
input += " -" + key + " " + value[0]
}
}
}
if audio != "" {
audio = indexToItem(audios, audio)
for key, value := range query {
switch key {
case "sample_rate", "sample_size", "channels", "audio_buffer_size":
input += " -" + key + " " + value[0]
}
}
}
if video != "" {
input += ` -i video="` + video + `"`
if audio != "" {
input += `:audio="` + audio + `"`
}
} else {
input += ` -i audio="` + audio + `"`
}
return input
}
func deviceInputSuffix(video, audio string) string { func deviceInputSuffix(video, audio string) string {
switch { switch {
+21 -35
View File
@@ -1,6 +1,7 @@
package device package device
import ( import (
"errors"
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"net/http" "net/http"
"net/url" "net/url"
@@ -16,45 +17,23 @@ func Init(bin string) {
} }
func GetInput(src string) (string, error) { func GetInput(src string) (string, error) {
i := strings.IndexByte(src, '?')
if i < 0 {
return "", errors.New("empty query: " + src)
}
query, err := url.ParseQuery(src[i+1:])
if err != nil {
return "", err
}
runonce.Do(initDevices) runonce.Do(initDevices)
input := deviceInputPrefix if input := queryToInput(query); input != "" {
return input, nil
var video, audio string
if i := strings.IndexByte(src, '?'); i > 0 {
query, err := url.ParseQuery(src[i+1:])
if err != nil {
return "", err
}
for key, value := range query {
switch key {
case "video":
video = value[0]
case "audio":
audio = value[0]
case "resolution":
input += " -video_size " + value[0]
default: // "input_format", "framerate", "video_size"
input += " -" + key + " " + value[0]
}
}
} }
if video != "" { return "", errors.New("wrong query: " + src)
if i, err := strconv.Atoi(video); err == nil && i < len(videos) {
video = videos[i]
}
}
if audio != "" {
if i, err := strconv.Atoi(audio); err == nil && i < len(audios) {
audio = audios[i]
}
}
input += " -i " + deviceInputSuffix(video, audio)
return input, nil
} }
var Bin string var Bin string
@@ -68,3 +47,10 @@ func apiDevices(w http.ResponseWriter, r *http.Request) {
api.ResponseStreams(w, streams) api.ResponseStreams(w, streams)
} }
func indexToItem(items []string, index string) string {
if i, err := strconv.Atoi(index); err == nil && i < len(items) {
return items[i]
}
return index
}