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  }