Engage Priority And Preemption - rallytac/pub GitHub Wiki
Engage Priority and Preemption
Users of communications systems very often need the ability to have what they say take precedence over what others are saying. While this capability is extremely important in cases such as emergency situations, it's also very useful in general command and control situations where a team leader, commander, or some other person with higher authority needs to have their voice heard - and nobody else.
Engage's implementation to this is a transmission priority
value associated with the audio stream being sent by a user. This priority is a data field encoded in the transmitting user's audio packets and can have a value between 0
and 255
. These two values have no special meaning to Engage beyond the fact that they exist and are processed accordingly. A transmission value of 0
(which is generally the default) is treated as the lowest priority; while a value of 255
is treated as the very highest. How your application determines what values to use are entirely up to you: it is not dictated by Engage.
The RX Side
When a packet stream is received, the receiving Engage Engine passes the priority value up to the application in the "txPriority
" field of the JSON data accompanying the GROUP_RX_SPEAKERS_CHANGED
event. This way your application can change the user interface if desired to tell the receiving user that the transmitting user is transmitting at an elevated level.
The TX Side
Things are a little more sophisticated on the transmitting side, though.
Setting the priority
Setting transmission priority is done on a transmission-by-transmission basis using parameters passed to the engageBeginGroupTx()
and engageBeginGroupTxAdvanced()
API calls. In the case of engageBeginGroupTx()
, the priority value is passed in the function's priority
parameter. In the call to engageBeginGroupTxAdvanced()
, the value is passed in the priority
field in the JSON parameters passed in the call. In both cases, the priority is retained only for the duration of the transmission. Subsequent API calls reset the priority to 0
unless those API calls specify a priority.
Priority Contention Management
When initiating the transmission - and all throughout the transmission process - Engage uses the transmission priority requested to determine whether transmission can actually begin ... and continue. Here are a few things to consider:
- Is the group half or full-duplex?
- Is someone else already transmitting at the same priority?
- Is someone else already transmitting at a higher priority?
- Has someone else started transmitting while we're already transmitting?
Let's go through these, using two users A
and B
.
First, what if the group is configured for half-duplex? If A
initiates transmission at priority 0
, and nobody else is already transmitting, he will be allowed to transmit. That's pretty straightforward.
Next, if, while A
is transmitting at priority 0
and B
attempts to begin transmitting at priority 0
too; B
's Engage Engine won't allow B
to transmit because it knows that A
is already transmitting. (B
's Engage Engine knows this because it's already receiving a stream from A
). This situation causes B
's API call to begin transmission to fail.
But, it could be that A
started transmitting (at priority 0
) at almost exactly the same time that B
did (also at priority 0
), and that neither's packets have yet arrived at the other's Engage Engine due to network propagation delay. Now we have a race-condition which we have to deal with - we often call this "glaring".
To resolve this particular situation, both A
and B
's Engage Engines use a formula to determine a calculated priority. This calculated priority is simply a number that each Engine calculates independently to determine who should be allowed to continue transmitting (the arrived-at value on each end is the same). If the outcome of the calculation is that A
is allowed to continue to transmit, nothing happens on A
's side while, for B
, that user is prevented from transmitting and a GROUP_TX_USURPED_BY_PRIORITY
event is fired up to B
's application.
This business only happens in the rare case where both
A
andB
have already begun transmitting and their packets are already "in-flight" but have not yet reached the other endpoint.
Now, if the group is configured for full-duplex, none of the above applies as the group allows multiple people to transmit simultaneously - provided they're at the same priority. It's 0
in this example case but the same logic applies if both A
and B
transmit at any other priority level, and that level is the same.
This logic - which does not require a centralized floor-controller or signaling between entities - works not just in the case of transmitting entities having the same priority but also in those cases where transmission priorities are different. And it works the same if the group is configured for half-duplex or full-duplex.
For example: let's imagine that A
is transmitting at priority 0
. Now, B
comes along and attempts to transmit at priority level 42
(42
has no special meaning here - unless, of course, you're a fan of Douglas Adams - its just a non-zero priority). On B
's side, transmission will be allowed because 42
is higher than 0
. On A
's side, transmission will cease and GROUP_TX_USURPED_BY_PRIORITY
will be fired up to A
's application.
Flags
In addition to the priority value for the transmission, Engage also allows for bit flags to be associated with the transmission. These flags have no effect on priority determination but are very useful - particularly on the receiving end. For example, the bit flag that indicates an emergency is coded as value 0x0001
(ENGAGE_TXFLAG_EMERGENCY
) to indicate some sort of emergency situation. Another flag 0x0004
(ENGAGE_TXFLAG_AUTOMATED_SYSTEM
) indicates that the transmission is coming from an automated source such as an announcement system, AI, or some other non-human entity. These are the only well-known/predefined flags in Engage. Implementors are free to use any of the remaining 14 bit flags (the flags
field in the packet stream is 16 bits).
As with priority, setting the flags is done in the calls to engageBeginGroupTx()
and engageBeginGroupTxAdvanced()
as a function parameter or JSON content respectively.
It's very important to understand that the
ENGAGE_TXFLAG_EMERGENCY
does NOT change the transmission priority. If you need to set the emergency bit flag and ensure that the transmission is prioritized, you must set the transmission priority as described above.
On the receiving side, the value of the stream's flags is passed up in the JSON for the GROUP_RX_SPEAKERS_CHANGED
event as "rxFlags
".
Header Extensions Required!!
Something very important to this discussion is that the priority and flags for a packet stream is encoded into the stream as RTP header extensions. If you have not enabled header extensions for the group, much of this is not going to work.
Except ... we wanted to provide for some kind of priority management and to deal with glaring even on basic RTP streams - i.e. those without header extensions. In the absence of header extensions, the logic described above where priority is calculated is still supported. So, even though you may have just a simple RTP stream, you'll still get some basic contention management.