github.com/noriah/catnip@v1.8.5/input/pipewire/dump.go (about)

     1  package pipewire
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os"
     7  	"os/exec"
     8  
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  type pwObjects []pwObject
    13  
    14  func pwDump(ctx context.Context) (pwObjects, error) {
    15  	cmd := exec.CommandContext(ctx, "pw-dump")
    16  	cmd.Stderr = os.Stderr
    17  
    18  	dumpOutput, err := cmd.Output()
    19  	if err != nil {
    20  		var execErr *exec.ExitError
    21  		if errors.As(err, &execErr) {
    22  			return nil, errors.Wrapf(err, "failed to run pw-dump: %s", execErr.Stderr)
    23  		}
    24  		return nil, errors.Wrap(err, "failed to run pw-dump")
    25  	}
    26  
    27  	var dump pwObjects
    28  	if err := json.Unmarshal(dumpOutput, &dump); err != nil {
    29  		return nil, errors.Wrap(err, "failed to parse pw-dump output")
    30  	}
    31  
    32  	return dump, nil
    33  }
    34  
    35  // Filter filters for the devices that satisfies f.
    36  func (d pwObjects) Filter(fns ...func(pwObject) bool) pwObjects {
    37  	filtered := make(pwObjects, 0, len(d))
    38  loop:
    39  	for _, device := range d {
    40  		for _, f := range fns {
    41  			if !f(device) {
    42  				continue loop
    43  			}
    44  		}
    45  		filtered = append(filtered, device)
    46  	}
    47  	return filtered
    48  }
    49  
    50  // Find returns the first object that satisfies f.
    51  func (d pwObjects) Find(f func(pwObject) bool) *pwObject {
    52  	for i, device := range d {
    53  		if f(device) {
    54  			return &d[i]
    55  		}
    56  	}
    57  	return nil
    58  }
    59  
    60  // ResolvePorts returns all PipeWire port objects that belong to the given
    61  // object.
    62  func (d pwObjects) ResolvePorts(object *pwObject, dir pwPortDirection) pwObjects {
    63  	return d.Filter(
    64  		func(o pwObject) bool { return o.Type == pwInterfacePort },
    65  		func(o pwObject) bool {
    66  			return o.Info.Props.NodeID == object.ID && o.Info.Props.PortDirection == dir
    67  		},
    68  	)
    69  }
    70  
    71  type pwObjectID int64
    72  
    73  type pwObjectType string
    74  
    75  const (
    76  	pwInterfaceDevice pwObjectType = "PipeWire:Interface:Device"
    77  	pwInterfaceNode   pwObjectType = "PipeWire:Interface:Node"
    78  	pwInterfacePort   pwObjectType = "PipeWire:Interface:Port"
    79  	pwInterfaceLink   pwObjectType = "PipeWire:Interface:Link"
    80  )
    81  
    82  type pwObject struct {
    83  	ID   pwObjectID   `json:"id"`
    84  	Type pwObjectType `json:"type"`
    85  	Info struct {
    86  		Props pwInfoProps `json:"props"`
    87  	} `json:"info"`
    88  }
    89  
    90  type pwInfoProps struct {
    91  	pwDeviceProps
    92  	pwNodeProps
    93  	pwPortProps
    94  	MediaClass string `json:"media.class"`
    95  
    96  	JSON json.RawMessage `json:"-"`
    97  }
    98  
    99  func (p *pwInfoProps) UnmarshalJSON(data []byte) error {
   100  	type Alias pwInfoProps
   101  	if err := json.Unmarshal(data, (*Alias)(p)); err != nil {
   102  		return err
   103  	}
   104  	p.JSON = append([]byte(nil), data...)
   105  	return nil
   106  }
   107  
   108  type pwDeviceProps struct {
   109  	DeviceName string `json:"device.name"`
   110  }
   111  
   112  // pwNodeProps is for Audio/Sink only.
   113  type pwNodeProps struct {
   114  	NodeName        string `json:"node.name"`
   115  	NodeNick        string `json:"node.nick"`
   116  	NodeDescription string `json:"node.description"`
   117  }
   118  
   119  // Constants for MediaClass.
   120  const (
   121  	pwAudioDevice       string = "Audio/Device"
   122  	pwAudioSink         string = "Audio/Sink"
   123  	pwStreamOutputAudio string = "Stream/Output/Audio"
   124  )
   125  
   126  type pwPortDirection string
   127  
   128  const (
   129  	pwPortIn  = "in"
   130  	pwPortOut = "out"
   131  )
   132  
   133  type pwPortProps struct {
   134  	PortID        pwObjectID      `json:"port.id"`
   135  	PortName      string          `json:"port.name"`
   136  	PortAlias     string          `json:"port.alias"`
   137  	PortDirection pwPortDirection `json:"port.direction"`
   138  	NodeID        pwObjectID      `json:"node.id"`
   139  	ObjectPath    string          `json:"object.path"`
   140  }