[pulseaudio-discuss] Musings on stream device and volume restore rules

Colin Guthrie gmane at colin.guthr.ie
Tue Mar 24 14:08:11 PDT 2009

Disclaimer... I'm a bit tired and haven't proofed this as much as I'd 
like. And I wrote most of it while in an airport and quite bored.

The suggestions are my own thoughts and opinions but are perhaps based 
on inaccurate assumptions, so feel free to correct me and change my opinion!

Stream Memory Database and Restoration Rules

This document outlines the needs and use case for the stream memory 
capabilities in pulseaudio and how this information should be used. It 
finishes with recommendations for implementation.


The system employed needs to be simple and allow for fully server based 
management and application of the rules. The logic should be kept simple 
with minimal overhead but still be powerful enough to meet all the needs 
of current actual and potential use cases.

Current System

Currently module-stream-restore stores various metadata on streams it 
sees. This currently consists of:
  * volume
  * channel map
  * mute status
  * device (optional)

It stores this metadata under a key that governs how it is matched again 
in the future.

The key itself has two parts, a prefix from a predefined list and a 
suffix relating to the specific stream in some way.

Prefixes include:
  * sink-input-by-media-role
  * sink-input-by-application-id
  * sink-input-by-application-name
  * sink-input-by-media-name

(where "sink-input" could also be "source-output" for input streams)

The specific identifier is suffixed on the end of this, to create a 
final key that is stored and queried in the database to give final keys 
of e.g.:
  * sink-input-by-media-role:event
  * sink-input-by application-name:MPlayer
  * etc.

When a stream is started, module-stream-restore will pick exactly one 
key that it will use to identify a stream. If the stream has a media 
role property it will use *-by-media-role. It then falls back to check 
the different identifiers, in order, as outlined above.


The current system has a couple limitations.
  1. If a stream has a media role, it is impossible to save a volume for 
the specific application with that role as the key 
sink-input-by-media-role:xxxx will always take precidence over a 
sink-input-by-application-name:yyyy keyed rule.
  2. If an application is modified to start providing new metadata (e.g. 
a role) the entries for that specific application that were used 
(sink-input-by-application-name) will no longer be matched and the data 
will be stale in the file. This is not really a major problem.
  3. The current system only allows for one device to be matched, not an 
ordered priority list.

NB 3 is only strictly problemati in the case of Phonon integration which 
uses such a scheme.

Use Case

To think about what we need to change in the system we need to look at 
what the user wants out of the system.

For the most part we want routing rules to be automatic, but still allow 
the user the degree of control they need.

For comparison, Phonon has a device priority list system that allows 
each role to prioritise the output devices they want to use. When a 
stream with that role is played, it will go through the list of devices 
until it finds one that is currently avaialble (i.e. it keeps track of 
devices that are no longer present - e.g. USB sound cards) and uses 
that. It offers this priority list for each role, as well as a "global" 
list, which is used when no role is provided by the application. 
Generally speaking, users seem happy with this arrangement and this 
degree of flexibility (albeit there are not many Phonon applications to 
expliot this flexibility fully yet.

Looking at native pulse applications, the "default" sink option in 
pavucontrol (or more correctly in the daemon itself) is the most 
commonly misunderstood option and amounts to by far and away the most 
common support issue: "I've changed my default to my USB but application 
X still plays sound via my Interal card". From an internal prespective, 
we do not consider this to be a "default" sink at all, but rather a 
fallback and arguably calling it "default" was a mistake, but the fact 
remains that users want an "active" default capability in the system 
somewhere, where changing the default device will actually move any 
active streams across to that device if they are currently using a 
different default. Probably the second most common support question is: 
"I set my USB to default, but then unplugged it and the application 
started playing on my internal speakers... Great! But when I plugged my 
USB back in it didn't move it back. What's up?".

What to do

With the above use cases in mind, it's clear that the current system 
needs to be revised to cope. In summary the problems we want to address are:
  1. Allow for an "active" default
  2. Allow for a prioritised list of devices to be created (on a 
per-role basis - finer grained control would be too much).
  3. Allow for applications which have the same role to have independant 
volumes saved.

To this end, I think the current volume/map/mute/device database is 
incorrect. With the flat volume support, I think per-application volumes 
are more appropriate, rather than per-role.

I think that a key->device->priority table is needed to allow for a 
given role identifier to have it's own rules written appropriately. 
Pulse needs to remember the sinks (and sources) both past and present 
(including their "nice" name (description)) and provide a way for a UI 
app to purge a currently unavailable sink (e.g. remove it from the cache).

When a stream is encountered, it's role should be checked and the 
prioritised list of devices loaded for that role. As in Phonon, if a 
stream does not have a role a "default" priority list should be used. 
(Optional) A subsequent check should be made to see if any specific 
rules state that this individual application has a preferred device to 
allow for fine grained control. If such a rule exists the device should 
be move to the top of the already loaded priority list or injected there 
if it does not exist (unlikely). The stream is then restored to the 
first available device. This priority list should be saved with the 
stream so it can be easily re-evaluated at any point should new sinks 
become available later.

If any priority list is changed (e.g. by a client application) all 
streams should have their cached priority list invalidated and rebuilt 
(perhaps the rebuilding can be lazy - e.g. it's only loaded when needed?)

With regards to volumes etc., these should be saved against a given 
application and not a role. To do so at the role level is unlikely to be 
a useful end user feature.

With this system in place, a neat and effective UI can be produced and a 
good mapping into KDE can also exist which will be good for cross 
desktop accpetance of PulseAudio in the linux world.


Currently the routing decision is only processed by 
module-stream-restore, but this is not sufficient. We need other modules 
to be able to participate in this process. In order to acheive the 
above, a new hook (or similar) should be created to allow separate 
modules to have a say in building the priority list. 
PA_CORE_HOOK_SINK_INPUT_ROUTE should be called when a new stream is 
created and whenever the routing logic needs to be be recalculated (e.g. 
the rules themselves change (or a module participating in rule 
calculation is [un]loaded), *not* when a new device comes or goes) I 
would suggest that only the device priority list should be handled in 
this hook. The volume restoration should probably be handled within the 


Colin Guthrie

Day Job:
   Tribalogic Limited [http://www.tribalogic.net/]
Open Source:
   Mandriva Linux Contributor [http://www.mandriva.com/]
   PulseAudio Hacker [http://www.pulseaudio.org/]
   Trac Hacker [http://trac.edgewall.org/]

More information about the pulseaudio-discuss mailing list