github.com/noriah/catnip@v1.8.5/input/backend.go (about) 1 package input 2 3 import ( 4 "fmt" 5 "os/exec" 6 "runtime" 7 8 "github.com/pkg/errors" 9 ) 10 11 type Backend interface { 12 // Init should do nothing if called more than once. 13 Init() error 14 Close() error 15 16 Devices() ([]Device, error) 17 DefaultDevice() (Device, error) 18 Start(SessionConfig) (Session, error) 19 } 20 21 type NamedBackend struct { 22 Name string 23 Backend 24 } 25 26 var Backends []NamedBackend 27 28 // RegisterBackend registers a backend globally. This function is not 29 // thread-safe, and most packages should call it on init(). 30 func RegisterBackend(name string, b Backend) { 31 Backends = append(Backends, NamedBackend{ 32 Name: name, 33 Backend: b, 34 }) 35 } 36 37 // Get all installed backend names. 38 func GetAllBackendNames() []string { 39 out := make([]string, len(Backends)) 40 for i, backend := range Backends { 41 out[i] = backend.Name 42 } 43 return out 44 } 45 46 // Get the default backend depending on runtime.GOOS 47 func DefaultBackend() string { 48 switch runtime.GOOS { 49 case "windows": 50 if HasBackend("ffmpeg-dshow") { 51 return "ffmpeg-dshow" 52 } 53 54 case "darwin": 55 if HasBackend("portaudio") { 56 return "portaudio" 57 } 58 59 if HasBackend("ffmpeg-avfoundation") { 60 return "ffmpeg-avfoundation" 61 } 62 63 case "linux": 64 if HasBackend("pipewire") { 65 if path, _ := exec.LookPath("pw-cat"); path != "" { 66 return "pipewire" 67 } 68 } 69 70 if HasBackend("parec") { 71 if path, _ := exec.LookPath("parec"); path != "" { 72 return "parec" 73 } 74 } 75 76 if HasBackend("ffmpeg-alsa") { 77 return "ffmpeg-alsa" 78 } 79 } 80 81 return "" 82 } 83 84 // FindBackend is a helper function that finds a backend. It returns nil if the 85 // backend is not found. 86 func FindBackend(name string) Backend { 87 for _, backend := range Backends { 88 if backend.Name == name { 89 return backend 90 } 91 } 92 return nil 93 } 94 95 func HasBackend(name string) bool { 96 return FindBackend(name) != nil 97 } 98 99 func InitBackend(bknd string) (Backend, error) { 100 backend := FindBackend(bknd) 101 if backend == nil { 102 return nil, fmt.Errorf("backend not found: %q; check list-backends", bknd) 103 } 104 105 if err := backend.Init(); err != nil { 106 return nil, errors.Wrap(err, "failed to initialize input backend") 107 } 108 109 return backend, nil 110 } 111 112 func GetDevice(backend Backend, device string) (Device, error) { 113 if device == "" { 114 def, err := backend.DefaultDevice() 115 if err != nil { 116 return nil, errors.Wrap(err, "failed to get default device") 117 } 118 return def, nil 119 } 120 121 devices, err := backend.Devices() 122 if err != nil { 123 return nil, errors.Wrap(err, "failed to get devices") 124 } 125 126 for idx := range devices { 127 if devices[idx].String() == device { 128 return devices[idx], nil 129 } 130 } 131 132 return nil, errors.Errorf("device %q not found; check list-devices", device) 133 }