10

The output of both pactl list sink-inputs and pacmd list-sink-inputs contains a Properties section:

Properties:
    media.name = "ALSA Playback"
    application.name = "ALSA plug-in [snapclient]"
    native-protocol.peer = "UNIX socket client"
    native-protocol.version = "29"
    application.process.id = "6393"
    application.process.user = "root"
    application.process.host = "xxxxxx"
    application.process.binary = "snapclient"
    application.language = "C"
    application.process.machine_id = "8dadf95c2f504864bc0f8b3ab149cbe0"
    application.process.session_id = "c4"
    module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [snapclient]"

I am wondering if there is a way to directly look up the index of a sink-input by either the application.process.id or application.process.binary, without resorting to parsing the many lines of output of the aforementioned commands or writing a separate C program.

Jakob Hansen
  • 101
  • 1
  • 4
  • This would be a great question on [unix.se] but it's not about programming. You just want to know how to resolve a name to an ID in pulse audio. – Evan Carroll Jun 03 '20 at 16:02

4 Answers4

5

Some commands also accept the unique name instead of the id, but the ones you are trying to use seem to not be able to do so.. probably because the name is not unique and there could be mulitple matches. You need to parse it yourself. This is what I came up with:

pacmd list-sink-inputs |
tr '\n' '\r' |
perl -pe 's/.*? *index: ([0-9]+).+?application\.name = "([^\r]+)"\r.+?(?=index:|$)/\2:\1\r/g' |
tr '\r' '\n'

perl -pe is like sed, just better. This basically matches [anything] [index]: [id] [anything] [application.name] = [name] [anything] and formats the output to something like

"speech-dispatcher":166
"SoX":407
"Clementine":413

Which you then can grep or sed.

Maybe you want to adjust application\.name to something more suitable to you.

phil294
  • 10,038
  • 8
  • 65
  • 98
  • 2
    This is very useful for what I'm trying to do now, thanks! – Try431 Dec 05 '18 at 20:50
  • ... or awk. A slightly modified example: `pacmd list-source-outputs | tr '\n' '\r' | perl -pe 's/ *index: ([0-9]+).+?media\.name = "([^\r]+)"\r.+?(?=index:|$)/\2:\1\r/g' | tr '\r' '\n' | awk -F ":" '/AudioCallbackDriver/ {print $2}'` – Marc.2377 Nov 20 '19 at 19:46
  • 1
    application.name isn't always set, but application.process.binary seems to be: `pacmd list-sink-inputs | tr '\n' '\r' | perl -pe 's/ *index: ([0-9]+).+?application\.process\.binary = "([^\r]+)"\r.+?(?=index:|$)/\2:\1\r/g' | tr '\r' '\n'` – Allanrbo Jun 07 '20 at 03:33
  • my first sink is `properties: media.name = "LADSPA Stream" media.role = "filter" module-stream-restore.id = "sink-input-by-media-role:filter"` so this doesn't work for me – br4nnigan Jul 12 '21 at 21:54
  • @br4nnigan So your sink does not have an `application.name` property? What about `application.process.binary`, as hinted by Allanrbo above? You need to specify some usable property at least or your respective non-matching sinks will simply be ignored. – phil294 Jul 12 '21 at 23:41
  • application.process.binary is also not always set for things like `pactl load-module module-echo-cancel`, this is now getting sink input indexes mixed up :( – Baa Apr 17 '22 at 16:41
2

Although it does parse the output and doesn't yet do any matching for the id you may be looking for, this method will provide a way, with some modifications, to get that information by sink id:

pactl list sink-inputs | while read -r line ; do

  #echo "Processing ${line}"
  echo $line | grep -oP 'Sink Input #\K[^$]'
  echo $line | grep -oP 'application.process.id = "\K[^"]+'
  echo $line | grep -oP 'application.process.binary = "\K[^"]+'

done
Greywood
  • 21
  • 4
0

Here's a short solution to OP's question:

$ cat pulse-get-sink-input-for-pid
#!/bin/bash
if [ $# -ne 1 ]; then >&2 echo Need PID; exit 1; fi
export pid=$1
pactl list sink-inputs | perl -nle '/Sink Input #(\d+)/ and $si = $1; /application.process.id.*?(\d+)/ and ($1 == $ENV{pid}) and print "$si"'
Metamorphic
  • 732
  • 6
  • 16
0

The very long, easy to understand, maintainable way to do it (using application.process.binary)

Result

ffmpeg | 3
firefox-esr | 24
firefox-esr | 58
vlc | 43

Bash script

str="$(pacmd list-sink-inputs)"

list1=$(printf '%s' "$str" | grep 'index: ')
list2=$(printf '%s' "$str" | grep 'application.process.binary = "')

arr1=()
arr2=()

while read line; do
    arr1+=("${line:7}")
done <<< "$list1"

while read line; do
    arr2+=("${line:30:-1}")
done <<< "$list2"

for index in ${!arr1[@]}; do
    s1="${arr1[index]}"
    s2="${arr2[index]}"

    echo "$s2 | $s1"
done

Another way to do it (a bit more obscure)

str="$(pacmd list-sink-inputs)"

arr1=($(printf '%s' "$str" | grep 'index: ' | rev | cut -d' ' -f 1 | rev))
arr2=($(printf '%s' "$str" | grep 'application.process.binary' | cut -d'"' -f 2))

for index in ${!arr1[@]}; do
    s1="${arr1[index]}"
    s2="${arr2[index]}"

    echo "$s2 | $s1"
done

And finally, the shortest solution I found :

str="$(pacmd list-sink-inputs)"

paste -d\| \
<(printf '%s' "$str" | grep 'application.process.binary' | cut -d'"' -f 2) \
<(printf '%s' "$str" | grep 'index: ' | rev | cut -d' ' -f 1 | rev)
Santa Claus
  • 984
  • 1
  • 12
  • 26
  • 1
    Sadly all of these fail and get the indexes mixed up if there are any sink inputs that don't have an application.process.binary such as `pactl load-module module-echo-cancel` :( – Baa Apr 17 '22 at 16:42
  • @Baa Interesting, I did not consider this case! – Santa Claus Apr 17 '22 at 22:55