Welcome to the Java implementation of the Janus WebRTC server APIs. This SDK is designed to facilitate interaction with the Janus WebRTC server using both REST API and WebSockets. It is suitable for use in Java applications that require communication with the Janus WebRTC server.
The APIs methods are documented very much so it's easy to use you would understand how the code works as much no surprises there in behaviour also with respect to the Janus WebRTC server Documentation.
The SDK is divided into two parts:
- API Part: This can be integrated into the back-ends of web frameworks. It is ready for use with Spring Boot, Micronaut, Quakus, Helidon, and more.
- Platform Dependent Part: This is designed for desktop applications like JavaFX, Swing, AWT, etc. Please note that this part is still under development.
This SDK is continually evolving, and while comprehensive, it may not cover all scenarios. I recognize room for improvement and encourage contributions from the community. If you have suggestions, feel free to create a pull request.
There are two way to use this you can :
- copy all the files from the sdk package and paste into your project folder structure.
- import the
.jar
fromrelease/
folder,so the target files isrelease/janus-server-sdk-{version}.jar
The Janus Streaming Plugin API is a Java class designed to interact with the Janus WebRTC server's streaming plugin. It provides methods for creating, deleting, checking the existence, and retrieving a list of streaming sessions.
- Java Development Kit (JDK) 11+ installed
- Janus WebRTC server running and accessible
static final String JANUS_URL = "https://some-address-to-janus-server/janus";
static String JANUS_ADMIN_SECRET= "some-secret-if-janus-server-has-authentication";
Janus janus = new Janus(true,new JanusConfiguration(JANUS_URL,"","",JANUS_ADMIN_SECRET));
JSONObject result = janus.janusRestApiClient.janusStreamingPlugInAPI.createMountingPoint("rtp", "MountingPoint1", "Description", "metadata", "secret", "1234", true, true, "[{\"type\": \"audio\"}, {\"type\": \"video\"}]");
JSONObject result = janus.janusRestApiClient.janusStreamingPlugInAPI.editMountingPoint("mountingPointId", true, "New Description", "new_metadata", "new_secret", "5678", true, false);
JSONObject result = janus.janusRestApiClient.janusStreamingPlugInAPI.deleteMountingPoint("mountingPointId", "secret", true);
JSONObject result = janus.janusRestApiClient.janusStreamingPlugInAPI.enableMountingPoint("mountingPointId", "secret");
JSONObject result = janus.janusRestApiClient.janusStreamingPlugInAPI.infoMountingPoint("mountingPointId", "secret");
In case of an error, the returned JSONObject may contain an "error" field with additional details.
if (result.has("error")) {
String errorMessage = result.getString("error");
// Handle the error
}
The Janus Video Room Plugin API is a Java class designed to interact with the Janus WebRTC server's video room plugin. It provides methods for creating, deleting, checking the existence, and retrieving a list of video rooms.
Clone the repository and include the JanusVideoRoomPlugInAPI class in your Java project.
Please do note that roomId
parameter can be a number or a string.
static final String JANUS_URL = "https://some-address-to-janus-server/janus";
static String JANUS_ADMIN_SECRET= "some-secret-if-janus-server-has-authentication";
Janus janus = new Janus(true,new JanusConfiguration(JANUS_URL,"","",JANUS_ADMIN_SECRET));
var createJanusRoom = janus.janusRestApiClient.janusVideoRoomPlugInAPI.createJanusRoom("91","room","1234","1234",5,true,true,"/tmp");
var deleteRoom = janus.janusRestApiClient.janusVideoRoomPlugInAPI.deleteRoom("91","1234");
var response = janus.janusRestApiClient.janusVideoRoomPlugInAPI.checkIfVideoRoomExistsBoolCheck("1234");
// or
var result = janus.janusRestApiClient.janusVideoRoomPlugInAPI.checkIfVideoRoomExists("1234");
var response = janus.janusRestApiClient.janusVideoRoomPlugInAPI.getRooms();
There is support to consume events from the video room plugin.This is done by implementing the JanusEventsEmissions
interface.I would recommend to implement this interface in the end point class that will be
receiving the events from Janus server.For Example:
//Helidon SE Endpoint Class
public class JanusEventsService implements HttpService, JanusEventsEmissions {
//... other code
private void janusEventsHandler(ServerRequest req, ServerResponse res) {
String bodyText = req.content().as(String.class);
//System.out.println("Event bodyText : " + bodyText);
consumeEventAsync(bodyText);// this is the function to pass the json string for processing
res.status(Status.OK_200).send("");
}
//... other code
}
//Spring boot Endpoint Class
@RestController
public class JanusEventsService implements JanusEventsEmissions {
//... other code
@PostMapping("/janus-events-handler")
public ResponseEntity<String> janusEventsHandler(@RequestBody String bodyText) {
//System.out.println("Event bodyText : " + bodyText);
consumeEventAsync(bodyText);// this is the function to pass the json string for processing
return ResponseEntity.ok().body("");
}
//... other code
}
// Micronaut Endpoint Class
@Controller("/janus-events-handler")
public class JanusEventsService implements JanusEventsEmissions {
//... other code
@Post
public HttpResponse<String> janusEventsHandler(@Body String bodyText) {
//System.out.println("Event bodyText : " + bodyText);
consumeEventAsync(bodyText);// this is the function to pass the json string for processing
return HttpResponse.ok("");
}
//... other code
}
// Quarkus Endpoint Class
@Path("/janus-events-handler")
public class JanusEventsService implements JanusEventsEmissions {
//... other code
@POST
@Consumes(MediaType.TEXT_PLAIN)
public Response janusEventsHandler(String bodyText) {
//System.out.println("Event bodyText : " + bodyText);
consumeEventAsync(bodyText);// this is the function to pass the json string for processing
return Response.ok().build();
}
//... other code
}
Here are the methods that will be called when an event is received from Janus server.
@Override
public void onParticipantJoined( long participantId, String participantDisplay, String roomId ) {
System.out.println("onParticipantJoined event = " + participantId + " " + participantDisplay + " " + roomId);
}
@Override
public void onParticipantLeft( long participantId, String participantDisplay, String roomId ) {
System.out.println("onParticipantLeft event = " + participantId + " " + participantDisplay + " " + roomId);
}
@Override
public void onRoomSessionStarted( String roomId, long firstParticipantId, String firstParticipantDisplay ) {
System.out.println("onRoomSessionStarted event = " + roomId + " " + firstParticipantId + " " + firstParticipantDisplay);
}
@Override
public void onRoomSessionEnded( String roomId ) {
System.out.println("onRoomSessionEnded event = " + roomId);
}
This SDK provides a way to cache the events received from Janus server in a database.Currently, MySQL & Mongo are supported but other databases can be added easily(PRs are welcome). To save the events to an existing database consider the following example :
Make sure the user provided in all the databases or cases have enough privileges to create tables and insert data.
DBAccess dbAccess = DBAccess.getInstance();
DatabaseConfig mySqlConfig = new MySqlConfiguration("localhost", 3306, "mydatabase", "username", "password");
dbAccess.addDatabaseConnection("mysql", mySqlConfig);
DBAccess dbAccess = DBAccess.getInstance();
DatabaseConfig mySqlConfig = new MongoConfiguration("localhost", 27017, "janus_database", "root", "rootuser");
dbAccess.addDatabaseConnection("mongodb", mySqlConfig);
This SDK will start to cache the events in the database if io.github.kinsleykajiva.cache.DBAccess
is not null.If the details to access the database are incorrect ,the app wil throw an exception and stop.
So far this SDK cache supports the following Janus Events :
- Type 1 Session related event
- Type 2 -Handle related event
- Type 4 External event (injected via Admin API)
- Type 8 JSEP event (SDP offer/answer)
- Type 16 WebRTC state event (ICE/DTLS states, candidates, etc.)
- Type 32 Media event (media state, reports, etc.)
- Type 64 Plugin-originated event (e.g., event coming from VideoRoom)
- Type 128 Transport-originated event (e.g., WebSocket connection state)
- Type 256 Core event (server startup/shutdown)
Please note that in fields like session id or handle id are stored as they are but if they are not found the value is set to 0 in the database .
Timestamp are saved as DateTime
in the database, this aims at making searching easy as well based on dates.
In the event you want to search, try to consider session , handle the timestamp as well .
All events are saved on a background thread ,This is done to avoid blocking the parent thread.
This tool converts mjr
files from video rooms into a single video file that includes all participants in the room. Currently, it supports a maximum of 6 participants' streamed media being combined.
It operates via the system bash or command line, utilizing Java to execute commands through the java.lang.ProcessBuilder
class. Since the java.lang.ProcessBuilder
class is not synchronized, it is the user's responsibility to manage multi-threaded scenarios. It is advisable to establish a pipeline that processes each room sequentially.
This solution heavily relies on Janus being installed on the same machine, as it necessitates access to the command line tools janus-pp-rec
and FFmpeg for transcoding.
The process initially converts MJR files to Opus and WebM file formats for audio and video respectively, before proceeding with transcoding using FFmpeg.
The process can be initiated using the MediaFactory.class
object. You can provide a callback interface PostProcessing
to receive the results of the transcoding process.
Here is an example of how this will the run
public static void main( String[] args ) {
MediaFactory mediaFactory = new MediaFactory(
MediaOutputTarget.VIDEO_ROOM_PLUGIN ,
"1234",
"/var/www/janus/recording-folder",
"/var/www/janus/recording-folder/processed",
new PostProcessing(){
@Override
public void onProcessingStarted( String roomId, long millisecondsTimeStamp, List<FileInfoMJR> fileInfoMJRs, Thread thread ) {
System.out.println("Processing started " + roomId + " " + millisecondsTimeStamp + " " + fileInfoMJRs + " " + thread);
}
@Override
public void onProcessingEnded( String roomId, long millisecondsTimeStamp, List<File> outputs, Thread thread ) {
System.out.println("Processing ended " + roomId + " " + millisecondsTimeStamp + " " + outputs + " " + thread);
}
@Override
public void onProcessingFailed( String roomId, long millisecondsTimeStamp, String error ) {
System.out.println("Processing failed " + roomId + " " + millisecondsTimeStamp + " " + error);
}
@Override
public void onCleanUpStarted( String roomId, long millisecondsTimeStamp, Set<String> filesToCleanup ) {
System.out.println("Clean up started " + roomId + " " + millisecondsTimeStamp + " " + filesToCleanup);
}
@Override
public void onCleanUpFailed( String roomId, long millisecondsTimeStamp, String error ) {
System.out.println("Clean up failed " + roomId + " " + millisecondsTimeStamp + " " + error);
}
@Override
public void onCleanUpEnded( String roomId, long millisecondsTimeStamp ) {
System.out.println("Clean up ended " + roomId + " " + millisecondsTimeStamp);
}
});
}
Internally there are checks on the call SdkUtils.isJanusInstalled()
if this fails this wil throw an exception and stop the app.
I welcome contributions! If you find a bug, have a feature request, or want to improve the documentation, feel free to open an issue or submit a pull request.
This project is licensed under the MIT License.
git push origin +refs/heads/master:refs/heads/master