From a4b212d90677ac18efd7806f3e4a1b4d80b6fea7 Mon Sep 17 00:00:00 2001 From: Rob van Oostenrijk Date: Mon, 29 Jan 2024 14:20:27 +0400 Subject: [PATCH 1/3] Update build.yml --- .github/workflows/build.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e709fad7..200be190 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,6 +98,21 @@ jobs: uses: actions/upload-artifact@v3 with: { name: go2rtc_mac_arm64, path: go2rtc } + - name: Build go2rtc_freebsd_amd64 + env: { GOOS: freebsd, GOARCH: amd64 } + run: go build -ldflags "-s -w" -trimpath + - name: Upload go2rtc_freebsd_amd64 + uses: actions/upload-artifact@v3 + with: { name: go2rtc_freebsd_amd64, path: go2rtc } + + - name: Build go2rtc_freebsd_arm64 + env: { GOOS: freebsd, GOARCH: arm64 } + run: go build -ldflags "-s -w" -trimpath + - name: Upload go2rtc_freebsd_arm64 + uses: actions/upload-artifact@v3 + with: { name: go2rtc_freebsd_arm64, path: go2rtc } + + docker-master: name: Build docker master runs-on: ubuntu-latest From 93dad05bde201e2c06352f8ceaf70311ad8854b7 Mon Sep 17 00:00:00 2001 From: Rob van Oostenrijk Date: Mon, 29 Jan 2024 21:14:47 +0400 Subject: [PATCH 2/3] Added FreeBSD Binaries (#2) Co-authored-by: Rob van Oostenrijk --- .github/workflows/build.yml | 1 - internal/ffmpeg/device/device_freebsd.go | 86 ++++++++++++++++++++ internal/ffmpeg/hardware/hardware_freebsd.go | 37 +++++++++ pkg/mdns/syscall_freebsd.go | 24 ++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 internal/ffmpeg/device/device_freebsd.go create mode 100644 internal/ffmpeg/hardware/hardware_freebsd.go create mode 100644 pkg/mdns/syscall_freebsd.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 200be190..cc923de0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,7 +112,6 @@ jobs: uses: actions/upload-artifact@v3 with: { name: go2rtc_freebsd_arm64, path: go2rtc } - docker-master: name: Build docker master runs-on: ubuntu-latest diff --git a/internal/ffmpeg/device/device_freebsd.go b/internal/ffmpeg/device/device_freebsd.go new file mode 100644 index 00000000..ac9b5e43 --- /dev/null +++ b/internal/ffmpeg/device/device_freebsd.go @@ -0,0 +1,86 @@ +package device + +import ( + "net/url" + "os/exec" + "regexp" + "strings" + + "github.com/AlexxIT/go2rtc/internal/api" + "github.com/AlexxIT/go2rtc/pkg/core" +) + +func queryToInput(query url.Values) string { + video := query.Get("video") + audio := query.Get("audio") + + if video == "" && 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() { + // [AVFoundation indev @ 0x147f04510] AVFoundation video devices: + // [AVFoundation indev @ 0x147f04510] [0] FaceTime HD Camera + // [AVFoundation indev @ 0x147f04510] [1] Capture screen 0 + // [AVFoundation indev @ 0x147f04510] AVFoundation audio devices: + // [AVFoundation indev @ 0x147f04510] [0] MacBook Pro Microphone + cmd := exec.Command( + Bin, "-hide_banner", "-list_devices", "true", "-f", "avfoundation", "-i", "", + ) + b, _ := cmd.CombinedOutput() + + re := regexp.MustCompile(`\[\d+] (.+)`) + + var kind string + for _, line := range strings.Split(string(b), "\n") { + switch { + case strings.HasSuffix(line, "video devices:"): + kind = core.KindVideo + continue + case strings.HasSuffix(line, "audio devices:"): + kind = core.KindAudio + continue + } + + m := re.FindStringSubmatch(line) + if m == nil { + continue + } + + name := m[1] + + switch kind { + case core.KindVideo: + videos = append(videos, name) + case core.KindAudio: + audios = append(audios, name) + } + + streams = append(streams, &api.Source{ + Name: name, URL: "ffmpeg:device?" + kind + "=" + name, + }) + } +} diff --git a/internal/ffmpeg/hardware/hardware_freebsd.go b/internal/ffmpeg/hardware/hardware_freebsd.go new file mode 100644 index 00000000..8392d2b1 --- /dev/null +++ b/internal/ffmpeg/hardware/hardware_freebsd.go @@ -0,0 +1,37 @@ +package hardware + +import ( + "github.com/AlexxIT/go2rtc/internal/api" +) + +const ProbeVideoToolboxH264 = "-f lavfi -i testsrc2=size=svga -t 1 -c h264_videotoolbox -f null -" +const ProbeVideoToolboxH265 = "-f lavfi -i testsrc2=size=svga -t 1 -c hevc_videotoolbox -f null -" + +func ProbeAll(bin string) []*api.Source { + return []*api.Source{ + { + Name: runToString(bin, ProbeVideoToolboxH264), + URL: "ffmpeg:...#video=h264#hardware=" + EngineVideoToolbox, + }, + { + Name: runToString(bin, ProbeVideoToolboxH265), + URL: "ffmpeg:...#video=h265#hardware=" + EngineVideoToolbox, + }, + } +} + +func ProbeHardware(bin, name string) string { + switch name { + case "h264": + if run(bin, ProbeVideoToolboxH264) { + return EngineVideoToolbox + } + + case "h265": + if run(bin, ProbeVideoToolboxH265) { + return EngineVideoToolbox + } + } + + return EngineSoftware +} diff --git a/pkg/mdns/syscall_freebsd.go b/pkg/mdns/syscall_freebsd.go new file mode 100644 index 00000000..c1f1225b --- /dev/null +++ b/pkg/mdns/syscall_freebsd.go @@ -0,0 +1,24 @@ +package mdns + +import ( + "syscall" +) + +func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) { + // change SO_REUSEADDR and REUSEPORT flags simultaneously for BSD-like OS + // https://github.com/AlexxIT/go2rtc/issues/626 + // https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ/14388707 + if opt == syscall.SO_REUSEADDR { + if err = syscall.SetsockoptInt(int(fd), level, opt, value); err != nil { + return + } + + opt = syscall.SO_REUSEPORT + } + + return syscall.SetsockoptInt(int(fd), level, opt, value) +} + +func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) (err error) { + return syscall.SetsockoptIPMreq(int(fd), level, opt, mreq) +} From f73ee41d9312eea64c789cc390cdba8201c3b22e Mon Sep 17 00:00:00 2001 From: Rob van Oostenrijk Date: Tue, 30 Jan 2024 19:10:53 +0400 Subject: [PATCH 3/3] Updated FreeBSD ffmpeg integrations --- internal/ffmpeg/device/device_freebsd.go | 105 ++++++++++--------- internal/ffmpeg/hardware/hardware_freebsd.go | 51 ++++++--- 2 files changed, 95 insertions(+), 61 deletions(-) diff --git a/internal/ffmpeg/device/device_freebsd.go b/internal/ffmpeg/device/device_freebsd.go index ac9b5e43..f3a26a30 100644 --- a/internal/ffmpeg/device/device_freebsd.go +++ b/internal/ffmpeg/device/device_freebsd.go @@ -2,6 +2,7 @@ package device import ( "net/url" + "os" "os/exec" "regexp" "strings" @@ -11,76 +12,86 @@ import ( ) func queryToInput(query url.Values) string { - video := query.Get("video") - audio := query.Get("audio") - - if video == "" && audio == "" { - return "" - } - - // https://ffmpeg.org/ffmpeg-devices.html#avfoundation - input := "-f avfoundation" - - if video != "" { - video = indexToItem(videos, video) + if video := query.Get("video"); video != "" { + // https://ffmpeg.org/ffmpeg-devices.html#video4linux2_002c-v4l2 + input := "-f v4l2" 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": + case "video_size", "pixel_format", "input_format", "framerate", "use_libv4l2": input += " -" + key + " " + value[0] } } + + return input + " -i " + indexToItem(videos, video) } - if audio != "" { - audio = indexToItem(audios, audio) + if audio := query.Get("audio"); audio != "" { + input := "-f oss" + + for key, value := range query { + switch key { + case "channels", "sample_rate": + input += " -" + key + " " + value[0] + } + } + + return input + " -i " + indexToItem(audios, audio) } - return input + ` -i "` + video + `:` + audio + `"` + return "" } func initDevices() { - // [AVFoundation indev @ 0x147f04510] AVFoundation video devices: - // [AVFoundation indev @ 0x147f04510] [0] FaceTime HD Camera - // [AVFoundation indev @ 0x147f04510] [1] Capture screen 0 - // [AVFoundation indev @ 0x147f04510] AVFoundation audio devices: - // [AVFoundation indev @ 0x147f04510] [0] MacBook Pro Microphone - cmd := exec.Command( - Bin, "-hide_banner", "-list_devices", "true", "-f", "avfoundation", "-i", "", - ) - b, _ := cmd.CombinedOutput() + files, err := os.ReadDir("/dev") + if err != nil { + return + } - re := regexp.MustCompile(`\[\d+] (.+)`) - - var kind string - for _, line := range strings.Split(string(b), "\n") { - switch { - case strings.HasSuffix(line, "video devices:"): - kind = core.KindVideo - continue - case strings.HasSuffix(line, "audio devices:"): - kind = core.KindAudio + for _, file := range files { + if !strings.HasPrefix(file.Name(), core.KindVideo) { continue } - m := re.FindStringSubmatch(line) - if m == nil { - continue - } + name := "/dev/" + file.Name() - name := m[1] + cmd := exec.Command( + Bin, "-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", name, + ) + b, _ := cmd.CombinedOutput() + + // [video4linux2,v4l2 @ 0x860b92280] Raw : yuyv422 : YUYV 4:2:2 : 640x480 160x120 176x144 320x176 320x240 352x288 432x240 544x288 640x360 752x416 800x448 800x600 864x480 960x544 960x720 1024x576 1184x656 1280x720 1280x960 + // [video4linux2,v4l2 @ 0x860b92280] Compressed: mjpeg : Motion-JPEG : 640x480 160x120 176x144 320x176 320x240 352x288 432x240 544x288 640x360 752x416 800x448 800x600 864x480 960x544 960x720 1024x576 1184x656 1280x720 1280x960 + re := regexp.MustCompile("(Raw *|Compressed): +(.+?) : +(.+?) : (.+)") + m := re.FindAllStringSubmatch(string(b), -1) + for _, i := range m { + size, _, _ := strings.Cut(i[4], " ") + stream := &api.Source{ + Name: i[3], + Info: i[4], + URL: "ffmpeg:device?video=" + name + "&input_format=" + i[2] + "&video_size=" + size, + } + + if i[1] != "Compressed" { + stream.URL += "#video=h264#hardware" + } - switch kind { - case core.KindVideo: videos = append(videos, name) - case core.KindAudio: - audios = append(audios, name) + streams = append(streams, stream) + } + } + + err = exec.Command(Bin, "-f", "oss", "-i", "/dev/dsp", "-t", "1", "-f", "null", "-").Run() + if err == nil { + stream := &api.Source{ + Name: "OSS default", + Info: " ", + URL: "ffmpeg:device?audio=default&channels=1&sample_rate=16000&#audio=opus", } - streams = append(streams, &api.Source{ - Name: name, URL: "ffmpeg:device?" + kind + "=" + name, - }) + audios = append(audios, "default") + streams = append(streams, stream) } } diff --git a/internal/ffmpeg/hardware/hardware_freebsd.go b/internal/ffmpeg/hardware/hardware_freebsd.go index 8392d2b1..6ef753ac 100644 --- a/internal/ffmpeg/hardware/hardware_freebsd.go +++ b/internal/ffmpeg/hardware/hardware_freebsd.go @@ -1,36 +1,59 @@ package hardware import ( + "runtime" + "github.com/AlexxIT/go2rtc/internal/api" ) -const ProbeVideoToolboxH264 = "-f lavfi -i testsrc2=size=svga -t 1 -c h264_videotoolbox -f null -" -const ProbeVideoToolboxH265 = "-f lavfi -i testsrc2=size=svga -t 1 -c hevc_videotoolbox -f null -" +const ( + ProbeV4L2M2MH264 = "-f lavfi -i testsrc2 -t 1 -c h264_v4l2m2m -f null -" + ProbeV4L2M2MH265 = "-f lavfi -i testsrc2 -t 1 -c hevc_v4l2m2m -f null -" + ProbeRKMPPH264 = "-f lavfi -i testsrc2 -t 1 -c h264_rkmpp_encoder -f null -" + ProbeRKMPPH265 = "-f lavfi -i testsrc2 -t 1 -c hevc_rkmpp_encoder -f null -" +) func ProbeAll(bin string) []*api.Source { return []*api.Source{ { - Name: runToString(bin, ProbeVideoToolboxH264), - URL: "ffmpeg:...#video=h264#hardware=" + EngineVideoToolbox, + Name: runToString(bin, ProbeV4L2M2MH264), + URL: "ffmpeg:...#video=h264#hardware=" + EngineV4L2M2M, }, { - Name: runToString(bin, ProbeVideoToolboxH265), - URL: "ffmpeg:...#video=h265#hardware=" + EngineVideoToolbox, + Name: runToString(bin, ProbeV4L2M2MH265), + URL: "ffmpeg:...#video=h265#hardware=" + EngineV4L2M2M, + }, + { + Name: runToString(bin, ProbeRKMPPH264), + URL: "ffmpeg:...#video=h264#hardware=" + EngineRKMPP, + }, + { + Name: runToString(bin, ProbeRKMPPH265), + URL: "ffmpeg:...#video=h265#hardware=" + EngineRKMPP, }, } } func ProbeHardware(bin, name string) string { - switch name { - case "h264": - if run(bin, ProbeVideoToolboxH264) { - return EngineVideoToolbox + if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" { + switch name { + case "h264": + if run(bin, ProbeV4L2M2MH264) { + return EngineV4L2M2M + } + if run(bin, ProbeRKMPPH264) { + return EngineRKMPP + } + case "h265": + if run(bin, ProbeV4L2M2MH265) { + return EngineV4L2M2M + } + if run(bin, ProbeRKMPPH265) { + return EngineRKMPP + } } - case "h265": - if run(bin, ProbeVideoToolboxH265) { - return EngineVideoToolbox - } + return EngineSoftware } return EngineSoftware