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) +}