Janus AudioBridge plugin.
- Author
- Lorenzo Miniero loren.nosp@m.zo@m.nosp@m.eetec.nosp@m.ho.c.nosp@m.om
- Copyright
- GNU General Public License v3
This is a plugin implementing an audio conference bridge for Janus, specifically mixing Opus streams. This means that it replies by providing in the SDP only support for Opus, and disabling video. Opus encoding and decoding is implemented using libopus (http://opus.codec.org). The plugin provides an API to allow peers to join and leave conference rooms. Peers can then mute/unmute themselves by sending specific messages to the plugin: any way a peer mutes/unmutes, an event is triggered to the other participants, so that it can be rendered in the UI accordingly.
Rooms to make available are listed in the plugin configuration file. A pre-filled configuration file is provided in conf/janus.plugin.audiobridge.cfg
and includes a demo room for testing.
To add more rooms or modify the existing one, you can use the following syntax:
[<unique room ID>]
description = This is my awesome room
is_private = yes|no (private rooms don't appear when you do a 'list' request)
secret = <password needed for manipulating (e.g. destroying) the room>
sampling_rate = <sampling rate> (e.g., 16000 for wideband mixing)
record = true|false (whether this room should be recorded, default=false)
record_file = /path/to/recording.wav (where to save the recording)
Audio Bridge API
The Audio Bridge API supports several requests, some of which are synchronous and some asynchronous. There are some situations, though, (invalid JSON, invalid request) which will always result in a synchronous error response even for asynchronous requests.
create
, destroy
, exists
, list
, listparticipants
and resetdecoder
are synchronous requests, which means you'll get a response directly within the context of the transaction. create
allows you to create a new audio conference bridge dynamically, as an alternative to using the configuration file; destroy
removes an audio conference bridge and destroys it, kicking all the users out as part of the process; exists
allows you to check whether a specific audio conference exists; list
lists all the available rooms, while listparticipants
lists all the participants of a specific room and their details; finally, resetdecoder
marks the Opus decoder for the participant as invalid, and forces it to be recreated (which might be needed if the audio for generated by the participant becomes garbled).
The join
, configure
, changeroom
and leave
requests instead are all asynchronous, which means you'll get a notification about their success or failure in an event. join
allows you to join a specific audio conference bridge; configure
can be used to modify some of the participation settings (e.g., mute/unmute); changeroom
can be used to leave the current room and move to a different one without having to tear down the PeerConnection and recreate it again (useful for sidebars and "waiting rooms"); finally, leave
allows you to leave an audio conference bridge for good.
create
can be used to create a new audio room, and has to be formatted as follows:
{
"request" : "create",
"room" : <unique numeric ID, optional, chosen by plugin if missing>,
"description" : "<pretty name of the room, optional>",
"secret" : "<password required to edit/destroy the room, optional>",
"is_private" : <true|false, whether the room should appear in a list request>,
"sampling" : <sampling rate of the room, optional, 16000 by default>,
"record" : <true|false, whether to record the room or not, default false>,
"record_file" : "</path/to/the/recording.wav, optional>",
}
A successful creation procedure will result in a created
response:
{
"audiobridge" : "created",
"room" : <unique numeric ID>
}
An error instead (and the same applies to all other requests, so this won't be repeated) would provide both an error code and a more verbose description of the cause of the issue:
{
"audiobridge" : "event",
"error_code" : <numeric ID, check Macros below>,
"error" : "<error description as a string>"
}
On the other hand, destroy
can be used to destroy an existing audio room, whether created dynamically or statically, and has to be formatted as follows:
{
"request" : "destroy",
"room" : <unique numeric ID of the room to destroy>,
"secret" : "<room secret, mandatory if configured>"
}
A successful destruction procedure will result in a destroyed
response:
{
"audiobridge" : "created",
"room" : <unique numeric ID>
}
You can check whether a room exists using the exists
request, which has to be formatted as follows:
{
"request" : "exists",
"room" : <unique numeric ID of the room to check>
}
A successful request will result in a success
response:
{
"audiobridge" : "success",
"room" : <unique numeric ID>,
"exists" : <true|false>
}
To get a list of the available rooms (excluded those configured or created as private rooms) you can make use of the list
request, which has to be formatted as follows:
{
"request" : "list"
}
A successful request will produce a list of rooms in a success
response:
{
"audiobridge" : "success",
"rooms" : [ // Array of room objects
{ // Room #1
"room" : <unique numeric ID>,
"description" : "<Name of the room>",
"sampling_rate" : <sampling rate of the mixer>,
"record" : <true|false, whether the room is being recorded>,
"num_participants" : <count of the participants>
},
// Other rooms
]
}
To get a list of the participants in a specific room, instead, you can make use of the listparticipants
request, which has to be formatted as follows:
{
"request" : "listparticipants",
"room" : <unique numeric ID of the room>
}
A successful request will produce a list of participants in a participants
response:
{
"audiobridge" : "participants",
"room" : <unique numeric ID of the room>,
"participants" : [ // Array of participant objects
{ // Participant #1
"id" : <unique numeric ID of the participant>,
"display" : "<display name of the participant, if any; optional>",
"muted" : <true|false, whether user is muted or not>
},
// Other participants
]
}
To mark the Opus decoder context for the current participant as invalid and force it to be recreated, use the resetdecoder
request:
{
"request" : "resetdecoder"
}
A successful request will produce a success
response:
{
"audiobridge" : "success"
}
That completes the list of synchronous requests you can send to the AudioBridge plugin. As anticipated, though, there are also several asynchronous requests you can send, specifically those related to joining and updating one's presence as a participant in an audio room.
The way you'd interact with the plugin is usually as follows:
- you use a
join
request to join an audio room, and wait for the joined
event; this event will also include a list of the other participants, if any;
- you send a
configure
request attached to an audio-only JSEP offer to start configuring your participation in the room (e.g., join unmuted or muted), and wait for a configured
event, which will be attached to a JSEP answer by the plugin to complete the setup of the WebRTC PeerConnection;
- you send other
configure
requests (without any JSEP-related attachment) to mute/unmute yourself during the audio conference;
- you intercept events originated by the plugin (
joined
, leaving
) to notify you about users joining/leaving/muting/unmuting;
- you eventually send a
leave
request to leave a room; if you leave the PeerConnection instance intact, you can subsequently join a different room without requiring a new negotiation (and so just use a join
+ JSEP-less configure
to join).
Notice that there's also a changeroom
request available: you can use this request to immediately leave the room you're in and join a different one, without requiring you to do a leave
+ join
+ configure
round. Of course remember not to pass any JSEP-related payload when doing a changeroom
as the same pre-existing PeerConnection will be re-used for the purpose.
About the syntax of all the above mentioned requests, join
has to be formatted as follows:
{
"request" : "join",
"room" : <numeric ID of the room to join>,
"id" : <unique ID to assign to the participant; optional, assigned by the plugin if missing>,
"display" : "<display name to have in the room; optional>",
"muted" : <true|false, whether to start unmuted or muted>,
"quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>
}
A successful request will produce a joined
event:
{
"audiobridge" : "joined",
"room" : <numeric ID of the room>,
"id" : <unique ID assigned to the participant>,
"display" : "<display name of the new participant>",
"participants" : [
// Array of existing participants in the room
]
}
The other participants in the room will be notified about the new participant by means of a different joined
event, which will only include the room
and the new participant as the only object in a participants
array.
At this point, the media-related settings of the participant can be modified by means of a configure
request. The configure
request has to be formatted as follows:
{
"request" : "configure",
"muted" : <true|false, whether to unmute or mute>,
"quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>,
}
muted
instructs the plugin to mute or unmute the participant; quality
changes the complexity of the Opus encoder for the participant. A successful request will result in a ok
event:
{
"audiobridge" : "event",
"room" : <numeric ID of the room>,
"result" : "ok"
}
In case the muted
property was modified, the other participants in the room will be notified about this by means of a event
notification, which will only include the room
and the updated participant as the only object in a participants
array.
As anticipated, you can leave an audio room using the leave
request, which has to be formatted as follows:
{
"request" : "leave"
}
All the participants will receive an event
notification with the ID of the participant who just left:
{
"audiobridge" : "event",
"room" : <numeric ID of the room>,
"leaving" : <numeric ID of the participant who left>
}
For what concerns the changeroom
request, instead, it's pretty much the same as a join
request and as such has to be formatted as follows:
{
"request" : "changeroom",
"room" : <numeric ID of the room to move to>,
"id" : <unique ID to assign to the participant; optional, assigned by the plugin if missing>,
"display" : "<display name to have in the room; optional>",
"muted" : <true|false, whether to start unmuted or muted>,
"quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>
}
Such a request will trigger all the above-described leaving/joined events to the other participants, as it is indeed wrapping a leave
followed by a join
and as such the other participants in both rooms need to be updated accordingly. The participant who switched room instead will be sent a roomchanged
event which is pretty similar to what joined
looks like:
A successful request will produce a joined
event:
{
"audiobridge" : "roomchanged",
"room" : <numeric ID of the new room>,
"id" : <unique ID assigned to the participant in the new room>,
"display" : "<display name of the new participant>",
"participants" : [
// Array of existing participants in the new room
]
}
Plugins