The AudioBridge plugin provides a powerful API for creating and managing audio conferencing rooms. This document serves as a guide for developers using the Janus SDK to integrate audio bridging functionalities into their applications. It covers the core concepts, API usage, event handling, and provides practical examples.
The primary interaction with the AudioBridge plugin is through the AudioBridgeHandle. This handle is associated with a specific user and allows you to perform actions within the audio conferencing system, such as creating rooms, joining them, and managing participants.
Events from the plugin, such as a user joining or leaving a room, are delivered asynchronously. To receive these events, you must implement the JanusAudioBridgeListener and register it with your AudioBridgeHandle.
Before you can interact with the AudioBridge plugin, you need a Janus session and a handle attached to that session. The following example demonstrates how to create a JanusClient, establish a session, and then create an AudioBridgeHandle.
import io.github.kinsleykajiva.janus.client.JanusClient;
import io.github.kinsleykajiva.janus.client.JanusSession;
import io.github.kinsleykajiva.janus.client.handle.impl.AudioBridgeHandle;
import java.net.URI;
import java.util.concurrent.CompletableFuture;
public class AudioBridgeExample {
public static void main(String[] args) throws Exception {
// 1. Create a JanusClient
// Replace with your Janus server's WebSocket URI
URI serverUri = new URI("ws://your-janus-server:8188/");
JanusClient janusClient = new JanusClient(serverUri);
janusClient.connect();
// 2. Create a JanusSession
CompletableFuture<JanusSession> sessionFuture = janusClient.createSession();
JanusSession session = sessionFuture.get();
// 3. Create an AudioBridgeHandle
CompletableFuture<AudioBridgeHandle> handleFuture = session.attachToAudioBridge();
AudioBridgeHandle audioBridgeHandle = handleFuture.get();
System.out.println("AudioBridgeHandle created with ID: " + audioBridgeHandle.getHandleId());
// You can now use the audioBridgeHandle to interact with the plugin
// ...
// Clean up when done
janusClient.disconnect();
}
}Key Steps:
JanusClient: This is the main entry point to the Janus server. You instantiate it with the WebSocket URI of your Janus instance.JanusSession: A session represents a logical connection to the Janus server. You create it from theJanusClient.AudioBridgeHandle: Once you have a session, you can "attach" a handle to a specific plugin. Thesession.attachToAudioBridge()method is a convenient way to get a handle specifically for the AudioBridge plugin.
With the audioBridgeHandle instance, you are now ready to send commands and listen for events. The following sections will detail the available methods and events.
The AudioBridgeHandle class is your primary tool for interacting with the AudioBridge plugin. All methods that send a request to the Janus server return a CompletableFuture, allowing for non-blocking, asynchronous operations.
Creates a new audio conference room.
- Parameters:
request: ACreateRoomRequestobject. Use this to specify properties for the new room, such as its description, PIN, and whether it's private.
- Returns:
CompletableFuture<AudioBridgeRoom>- A future that completes with anAudioBridgeRoomobject, representing the newly created room. - Example:
import io.github.kinsleykajiva.janus.client.handle.impl.AudioBridgeHandle; import io.github.kinsleykajiva.janus.client.plugins.audiobridge.models.CreateRoomRequest; ......
CreateRoomRequest.Builder builder =new CreateRoomRequest.Builder().
setRoom(System.currentTimeMillis())
.setIsPrivate(false).setPermanent(false)
.setPin("1234")
.setDescription("Random Room")
;
CreateRoomRequest createRoomRequest = builder.build();
var room=audioBridgeHandle.createRoom(createRoomRequest).get();
logger.info("Created roomId with ID: {}", room.room());
```
Destroys an existing audio room.
- Parameters:
request: ADestroyRoomRequestobject specifying theroomIdof the room to destroy and, optionally, asecretif required by the room's configuration.
- Returns:
CompletableFuture<Void>- A future that completes when the room has been successfully destroyed. - Example:
long roomIdToDestroy = 123456789; // The ID of the room to destroy DestroyRoomRequest destroyRequest = new DestroyRoomRequest(roomIdToDestroy); audioBridgeHandle.destroyRoom(destroyRequest) .thenRun(() -> { System.out.println("Room " + roomIdToDestroy + " destroyed successfully."); }) .exceptionally(ex -> { System.err.println("Failed to destroy room: " + ex.getMessage()); return null; });
Retrieves a list of all available (non-private) AudioBridge rooms.
- Returns:
CompletableFuture<List<AudioBridgeRoom>>- A future that completes with a list ofAudioBridgeRoomobjects. - Example:
audioBridgeHandle.listRooms() .thenAccept(rooms -> { System.out.println("Available rooms:"); rooms.forEach(room -> { System.out.println("- " + room.getDescription() + " (ID: " + room.getRoomId() + ")"); }); });
Edits the properties of an existing room (e.g., changing the description, PIN, or secret).
- Parameters:
request: AnEditRoomRequestobject containing theroomIdand the new properties to set.
- Returns:
CompletableFuture<Void>- A future that completes when the room has been successfully edited. - Example:
long roomToEdit = 123456789; EditRoomRequest editRequest = new EditRoomRequest(roomToEdit); editRequest.setNewDescription("My updated conference room"); editRequest.setNewPin("5678"); audioBridgeHandle.editRoom(editRequest) .thenRun(() -> { System.out.println("Room " + roomToEdit + " edited successfully."); }) .exceptionally(ex -> { System.err.println("Failed to edit room: " + ex.getMessage()); return null; });
Checks if a specific room exists.
- Parameters:
request: AnExistsRequestobject containing theroomIdto check.
- Returns:
CompletableFuture<ExistsResponse>- A future that completes with anExistsResponseobject, which has aboolean exists()method. - Example:
long roomToCheck = 123456789; ExistsRequest existsRequest = new ExistsRequest(roomToCheck); audioBridgeHandle.exists(existsRequest) .thenAccept(response -> { if (response.exists()) { System.out.println("Room " + roomToCheck + " exists."); } else { System.out.println("Room " + roomToCheck + " does not exist."); } });
Sends a request for the local user to join a room. This is an asynchronous operation. A successful join is not confirmed by the return of this future, but by an onJoined event delivered to registered JanusAudioBridgeListeners.
- Parameters:
request: AJoinRoomRequestobject specifying theroomIdto join. You can also set properties likepin,displayName, and whether the user should join muted.
- Returns:
CompletableFuture<Void>- A future that completes when the join request has been acknowledged by the server. It does not signify that the join is complete. - Example:
long roomToJoin = 123456789; JoinRoomRequest joinRequest = new JoinRoomRequest(roomToJoin); joinRequest.setDisplayName("John Doe"); joinRequest.setMuted(true); // Join the room already muted audioBridgeHandle.joinRoom(joinRequest) .thenRun(() -> { System.out.println("Join request sent. Waiting for 'onJoined' event..."); }) .exceptionally(ex -> { System.err.println("Failed to send join request: " + ex.getMessage()); return null; });
Sends a request for the local user to leave the current room. A successful departure will be indicated by an onParticipantLeft event with your own ID.
- Returns:
CompletableFuture<Void>- A future that completes when the leave request is acknowledged by the server. - Example:
audioBridgeHandle.leave() .thenRun(() -> { System.out.println("Leave request sent."); });
Configures the local user's participation in the room (e.g., muting/unmuting or changing the display name).
- Parameters:
request: AConfigureRequestobject. You can specifymutedto mute/unmute ordisplayto change the display name.
- Returns:
CompletableFuture<Void>- A future that completes when the configure request is acknowledged. - Example:
// Mute the local user ConfigureRequest muteRequest = new ConfigureRequest(); muteRequest.setMuted(true); audioBridgeHandle.configure(muteRequest) .thenRun(() -> { System.out.println("Request to mute has been sent."); }); // Change display name ConfigureRequest nameChangeRequest = new ConfigureRequest(); nameChangeRequest.setDisplay("John D. Updated"); audioBridgeHandle.configure(nameChangeRequest);
Retrieves the list of participants currently in a specific room.
- Parameters:
roomId: The unique ID of the room.
- Returns:
CompletableFuture<List<AudioBridgeParticipant>>- A future that completes with a list ofAudioBridgeParticipantobjects. - Example:
long roomToList = 123456789; audioBridgeHandle.listParticipants(roomToList) .thenAccept(participants -> { System.out.println("Participants in room " + roomToList + ":"); participants.forEach(p -> { System.out.println( "- ID: " + p.getId() + ", Name: " + p.getDisplayName() + ", Muted: " + p.isMuted() ); }); });
These methods are typically used by moderators to manage participants in a room.
Kicks a specific participant from a room.
- Parameters:
request: AKickRequestobject specifying theroomIdand theparticipantIdto kick.
- Returns:
CompletableFuture<Void>- A future that completes when the kick request is acknowledged. - Example:
long roomId = 123456789; long participantToKick = 987654321; KickRequest kickRequest = new KickRequest(roomId, participantToKick); audioBridgeHandle.kick(kickRequest) .thenRun(() -> System.out.println("Kick request sent for participant " + participantToKick));
Mutes a specific participant in the room.
- Parameters:
request: AMuteParticipantRequestobject specifying theroomIdand theparticipantIdto mute.
- Returns:
CompletableFuture<Void>- A future that completes when the mute request is acknowledged. - Example:
long roomId = 123456789; long participantToMute = 987654321; MuteParticipantRequest muteRequest = new MuteParticipantRequest(roomId, participantToMute); audioBridgeHandle.mute(muteRequest) .thenRun(() -> System.out.println("Mute request sent for participant " + participantToMute));
Unmutes a specific participant in the room.
- Parameters:
request: AnUnmuteParticipantRequestobject specifying theroomIdand theparticipantIdto unmute.
- Returns:
CompletableFuture<Void>- A future that completes when the unmute request is acknowledged. - Example:
long roomId = 123456789; long participantToUnmute = 987654321; UnmuteParticipantRequest unmuteRequest = new UnmuteParticipantRequest(roomId, participantToUnmute); audioBridgeHandle.unmute(unmuteRequest) .thenRun(() -> System.out.println("Unmute request sent for participant " + participantToUnmute));
Mutes all participants in the room.
- Parameters:
request: AMuteRoomRequestobject specifying theroomId.
- Returns:
CompletableFuture<Void>- A future that completes when the mute request is acknowledged. - Example:
long roomId = 123456789; MuteRoomRequest muteRoomRequest = new MuteRoomRequest(roomId); audioBridgeHandle.muteRoom(muteRoomRequest) .thenRun(() -> System.out.println("Mute request sent for room " + roomId));
Unmutes all participants in the room.
- Parameters:
request: AnUnmuteRoomRequestobject specifying theroomId.
- Returns:
CompletableFuture<Void>- A future that completes when the unmute request is acknowledged. - Example:
long roomId = 123456789; UnmuteRoomRequest unmuteRoomRequest = new UnmuteRoomRequest(roomId); audioBridgeHandle.unmuteRoom(unmuteRoomRequest) .thenRun(() -> System.out.println("Unmute request sent for room " + roomId));
Forwards the audio stream of a participant to a remote RTP server.
- Parameters:
request: AnRtpForwardRequestobject containing details likeroomId,participantId,host,port, etc.
- Returns:
CompletableFuture<RtpForwardResponse>- A future that completes with anRtpForwardResponse, which includes thertp_streamdetails. - Example:
long roomId = 123456789; long participantId = 987654321; RtpForwardRequest rtpRequest = new RtpForwardRequest(roomId, participantId, "192.168.1.100", 5004); audioBridgeHandle.rtpForward(rtpRequest) .thenAccept(response -> { System.out.println("RTP forwarding started for stream: " + response.getRtpStream().getStreamId()); });
Stops an existing RTP forward.
- Parameters:
request: AStopRtpForwardRequestobject specifying theroomIdand thestreamIdto stop.
- Returns:
CompletableFuture<Void>- A future that completes when the forwarding is stopped. - Example:
long roomId = 123456789; long streamId = 1122334455; // The ID of the stream from RtpForwardResponse StopRtpForwardRequest stopRtpRequest = new StopRtpForwardRequest(roomId, streamId); audioBridgeHandle.stopRtpForward(stopRtpRequest) .thenRun(() -> System.out.println("RTP forwarding stopped for stream " + streamId));
Plays a pre-recorded file into the conference.
- Parameters:
request: APlayFileRequestobject specifying theroomIdand the path to the file to play.
- Returns:
CompletableFuture<PlayFileResponse>- A future that completes with aPlayFileResponse, containing theannouncement_id. - Example:
long roomId = 123456789; PlayFileRequest playRequest = new PlayFileRequest(roomId, "/path/to/your/audio.opus"); audioBridgeHandle.playFile(playRequest) .thenAccept(response -> { System.out.println("Playing file with announcement ID: " + response.getAnnouncementId()); });
Stops a file that is currently being played.
- Parameters:
request: AStopFileRequestobject with theroomIdand theannouncementIdto stop.
- Returns:
CompletableFuture<StopFileResponse>- A future that completes when the file playback is stopped. - Example:
long roomId = 123456789; long announcementId = 556677889; // from PlayFileResponse StopFileRequest stopRequest = new StopFileRequest(roomId, announcementId); audioBridgeHandle.stopFile(stopRequest) .thenRun(() -> System.out.println("Stopped playing file."));
Enables or disables recording for a specific participant.
- Parameters:
request: AnEnableRecordingRequestobject specifying theroomId,participantId, and a booleanrecordflag. You can also specify the outputfilepath.
- Returns:
CompletableFuture<Void>- A future that completes when the recording state is changed. - Example:
long roomId = 123456789; long participantId = 987654321; EnableRecordingRequest recordRequest = new EnableRecordingRequest( roomId, participantId, true, // Start recording "/path/to/recordings/user-987654321.opus" ); audioBridgeHandle.enableRecording(recordRequest) .thenRun(() -> System.out.println("Recording enabled for participant " + participantId));
To react to events happening in the audio conference, you need to implement the JanusAudioBridgeListener interface and register it with your AudioBridgeHandle. This is crucial for building an interactive application, as it's how you get notified about participants joining, leaving, being muted, and other state changes.
You can add and remove listeners using the following methods on your AudioBridgeHandle:
addAudioBridgeListener(JanusAudioBridgeListener listener)removeAudioBridgeListener(JanusAudioBridgeListener listener)
Example:
// Create an instance of your listener implementation
MyAudioBridgeListener myListener = new MyAudioBridgeListener();
// Add the listener to the handle
audioBridgeHandle.addAudioBridgeListener(myListener);
// When you're done, you can remove it
// audioBridgeHandle.removeAudioBridgeListener(myListener);Here are the methods you can implement from the JanusAudioBridgeListener interface.
Called when the local user has successfully joined a room. This is the confirmation that your joinRoom request was successful.
- Event Object:
JoinedEventgetRoomId(): The ID of the room that was joined.getDescription(): The description of the room.getParticipants(): A list ofAudioBridgeParticipantobjects for other users already in the room.
Called when a remote participant joins the room you are in.
- Event Object:
ParticipantJoinedEventgetRoomId(): The ID of the room.getParticipant(): AnAudioBridgeParticipantobject for the new participant.
Called when any participant (including the local user) leaves the room.
- Event Object:
ParticipantLeftEventgetRoomId(): The ID of the room.getLeavingParticipantId(): The unique ID of the participant who left.
Called when a participant's state is updated (e.g., they are muted, unmuted, or their display name changes).
- Event Object:
ParticipantUpdatedEventgetRoomId(): The ID of the room.getParticipant(): TheAudioBridgeParticipantobject with the updated details.
Called when the room you are in is destroyed.
- Event Object:
RoomDestroyedEventgetRoomId(): The ID of the room that was destroyed.
A generic, catch-all callback for any event from the plugin. This is useful for debugging or handling custom events not covered by the specific methods above.
- Event Object:
JSONObject- The raw JSON data of the event from Janus.
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.events.*;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.listeners.JanusAudioBridgeListener;
import org.json.JSONObject;
public class MyAudioBridgeListener implements JanusAudioBridgeListener {
@Override
public void onJoined(JoinedEvent event) {
System.out.println("Successfully joined room: " + event.getRoomId());
System.out.println("Room description: " + event.getDescription());
event.getParticipants().forEach(p -> {
System.out.println("- Existing participant: " + p.getDisplayName());
});
}
@Override
public void onParticipantJoined(ParticipantJoinedEvent event) {
System.out.println(
"Participant joined: " + event.getParticipant().getDisplayName() +
" (ID: " + event.getParticipant().getId() + ")"
);
}
@Override
public void onParticipantLeft(ParticipantLeftEvent event) {
System.out.println("Participant left: " + event.getLeavingParticipantId());
}
@Override
public void onParticipantUpdated(ParticipantUpdatedEvent event) {
System.out.println(
"Participant updated: " + event.getParticipant().getDisplayName() +
" | Muted: " + event.getParticipant().isMuted()
);
}
@Override
public void onRoomDestroyed(RoomDestroyedEvent event) {
System.out.println("The room has been destroyed: " + event.getRoomId());
}
@Override
public void onEvent(JSONObject event) {
// Optional: Log all raw events for debugging
// System.out.println("Received raw event: " + event.toString(2));
}
}The SDK uses a variety of model classes to represent requests and responses for the AudioBridge plugin. These classes, found in the io.github.kinsleykajiva.janus.client.plugins.audiobridge.models package, provide a type-safe way to construct requests and parse responses.
Below is a brief overview of some of the key model classes.
CreateRoomRequest: Used to define the properties of a new room (description, PIN, secret, etc.) when callingcreateRoom.DestroyRoomRequest: Used to specify theroomIdto be destroyed.JoinRoomRequest: Used to specify theroomIdto join, and optionally, the user's display name, PIN, and initial muted state.ConfigureRequest: Used to change the local user's state in the room, such as muting/unmuting or changing the display name.KickRequest: Used to kick a participant from a room, requiring aroomIdandparticipantId.MuteParticipantRequest: Used to mute a specific participant, requiring aroomIdandparticipantId.RtpForwardRequest: Used to provide all the necessary details for starting an RTP forward, including host, port, and stream details.PlayFileRequest: Used to specify theroomIdand thefileto be played as an announcement.
AudioBridgeRoom: Represents an audio conference room, containing its ID, description, and other properties. Returned bycreateRoomandlistRooms.AudioBridgeParticipant: Represents a participant in a room, containing their ID, display name, and muted status.ExistsResponse: A simple response object from theexistsrequest, containing a booleanexists()method.RtpForwardResponse: Contains the details of a newly created RTP stream after a successfulrtpForwardrequest.
By using these models, you can avoid constructing JSON objects manually and benefit from the compile-time checks and auto-completion provided by your IDE.
This example demonstrates a simple "bot" that connects to Janus, creates a room, joins it, waits for a short period, and then leaves and destroys the room. It showcases how to use the AudioBridgeHandle and JanusAudioBridgeListener together.
import io.github.kinsleykajiva.janus.client.JanusClient;
import io.github.kinsleykajiva.janus.client.JanusSession;
io.github.kinsleykajiva.janus.client.handle.impl.AudioBridgeHandle;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.events.*;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.listeners.JanusAudioBridgeListener;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.models.CreateRoomRequest;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.models.DestroyRoomRequest;
import io.github.kinsleykajiva.janus.client.plugins.audiobridge.models.JoinRoomRequest;
import org.json.JSONObject;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ConferenceBotExample {
// Use a latch to keep the application running until the bot is done
private static CountDownLatch latch = new CountDownLatch(1);
private static long myRoomId;
public static void main(String[] args) throws Exception {
URI serverUri = new URI("ws://your-janus-server:8188/");
io.github.kinsleykajiva.janus.client.JanusClient janusClient = new JanusClient(serverUri);
janusClient.connect();
JanusSession session = janusClient.createSession().get();
AudioBridgeHandle audioBridgeHandle = session.attachToAudioBridge().get();
// Add a listener to react to events
audioBridgeHandle.addAudioBridgeListener(new ConferenceListener());
// --- Workflow ---
// 1. Create a room
CreateRoomRequest createRequest = new CreateRoomRequest("Bot's Conference Room");
audioBridgeHandle.createRoom(createRequest)
.thenCompose(room -> {
myRoomId = room.getRoomId();
System.out.println("Room created: " + myRoomId);
// 2. Join the room
JoinRoomRequest joinRequest = new JoinRoomRequest(myRoomId);
joinRequest.setDisplayName("ConferenceBot");
return audioBridgeHandle.joinRoom(joinRequest);
})
.thenRun(() -> {
System.out.println("Join request sent. The bot will leave in 15 seconds.");
})
.exceptionally(ex -> {
System.err.println("Error in setup: " + ex.getMessage());
latch.countDown(); // Release latch on error
return null;
});
// Keep the main thread alive
latch.await(30, TimeUnit.SECONDS);
// --- Cleanup ---
System.out.println("Cleaning up...");
// 4. Leave the room (optional, as destroying the room kicks everyone out)
audioBridgeHandle.leave().get();
// 5. Destroy the room
DestroyRoomRequest destroyRequest = new DestroyRoomRequest(myRoomId);
audioBridgeHandle.destroyRoom(destroyRequest).get();
System.out.println("Room destroyed.");
janusClient.disconnect();
System.out.println("Disconnected.");
}
// A simple listener implementation for our bot
static class ConferenceListener implements JanusAudioBridgeListener {
@Override
public void onJoined(JoinedEvent event) {
System.out.println("Bot successfully joined room " + event.getRoomId());
// After joining, let's wait for a bit before leaving
new Thread(() -> {
try {
Thread.sleep(15000); // Wait for 15 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
latch.countDown(); // Signal the main thread to proceed with cleanup
}).start();
}
@Override
public void onParticipantJoined(ParticipantJoinedEvent event) {
System.out.println(
"Someone joined the conference: " + event.getParticipant().getDisplayName()
);
}
@Override
public void onParticipantLeft(ParticipantLeftEvent event) {
System.out.println(
"Someone left the conference: participant ID " + event.getLeavingParticipantId()
);
}
@Override
public void onParticipantUpdated(ParticipantUpdatedEvent event) {
// Not used in this simple example
}
@Override
public void onRoomDestroyed(RoomDestroyedEvent event) {
System.out.println("Room was destroyed remotely.");
latch.countDown(); // Release latch if room is destroyed
}
@Override
public void onEvent(JSONObject event) {
}
}
}