What is janus?

As described by its creator meetecho, janus is a general purpose WebRTC endpoint that browsers can interact with, and different modules can determine what should be done with the media. You can do, text messaging, video conferencing, streaming and more other things with janus. Also if there is no such module to do stuff what you are looking, you can expand janus by yourself.

What is janus VideoRoom plugin?

Janus videoroom plugin implement virtual conferencing room that peers can join and leave at anytime. Janus using here publish/subscribe method to initiate video conference. When some one join to the virtual room they can publish their own video/audio streams into room. Then others who already joined to the room receive notification about that publish. Then they can subscribe that stream.

Create or join to room

First of all you need to create Janus object in your application by using janus.js. After that you can create attach videoroom plugin to that object and create a handler. That handler gives the opportunity to use the features in videoroom plugin. So after creating that handler we can create new room or join existing room. In this example we use already created demo room to create our video conference. You can refer more about videoroom plugin by here.

Implement demo video conference

First you need to initiate Janus and attach videoroom plugin.

Janus.init({debug: "all", callback: function() {
      // Make sure the browser supports WebRTC
      // Create session
        janusRoom = new Janus(
          {
              server: JANUS_SERVER_URL,
              iceServers: ICE_SERVERS,
    
              success: function() {
                  // Attach to VideoRoom plugin
                  janusRoom.attach(
                      {
                          plugin: "janus.plugin.videoroom",

You can use above code for it. After attached the videoroom plugin you need to join the room as a publisher or subscriber. Here we first look at how to join room as a publisher.

{
        "request" : "join",
        "ptype" : "publisher",
        "room" : <unique ID of the room to join>,
        "id" : <unique ID to register for the publisher; optional, will be chosen by the plugin if missing>,
        "display" : "<display name for the publisher; optional>",
        "token" : "<invitation token, in case the room has an ACL; optional>"
}

you can use above code block to join existing room as a publisher. after join you will receive "joined" event. After receiving "joined" event you can publish your own feed to the room. You will get below information with joined event.

{
        "videoroom" : "joined",
        "room" : <room ID>,
        "description" : <description of the room, if available>,
        "id" : <unique ID of the participant>,
        "private_id" : <a different unique ID associated to the participant; meant to be private>,
        "publishers" : [
                {
                        "id" : <unique ID of active publisher #1>,
                        "display" : "<display name of active publisher #1, if any>",
                        "streams" : [
                                {
                                        "type" : "<type of published stream #1 (audio|video|data)">,
                                        "mindex" : "<unique mindex of published stream #1>",
                                        "mid" : "<unique mid of of published stream #1>",
                                        "disabled" : <if true, it means this stream is currently inactive/disabled (and so codec, description, etc. will be missing)>,
                                        "codec" : "<codec used for published stream #1>",
                                        "description" : "<text description of published stream #1, if any>",
                                        "moderated" : <true if this stream audio has been moderated for this participant>,
                                        "simulcast" : "<true if published stream #1 uses simulcast (VP8 and H.264 only)>",
                                        "svc" : "<true if published stream #1 uses SVC (VP9 only)>",
                                        "talking" : <true|false, whether the publisher stream has audio activity or not (only if audio levels are used)>,
                                },
                                // Other streams, if any
                        ],
                        "talking" : <true|false, whether the publisher is talking or not (only if audio levels are used); deprecated, use the stream specific ones>,
                },
                // Other active publishers
        ],
        "attendees" : [         // Only present when notify_joining is set to TRUE for rooms
                {
                        "id" : <unique ID of attendee #1>,
                        "display" : "<display name of attendee #1, if any>"
                },
                // Other attendees
        ]
}

To publish your own feed you need to createOffer to establish peer connection.

videoroomHandle.createOffer(
        {
            media: { audioRecv: false, videoRecv: false, audioSend: useAudio, videoSend: true },	// Publishers are sendonly
            success: function(jsep) {
                Janus.debug("Got publisher SDP!");
                Janus.debug(jsep);

                const publish = { "request": "publish", "audio": useAudio, "video": true };
                videoroomHandle.send({"message": publish, "jsep": jsep});
            },
            error: function(error) {
                Janus.error("WebRTC error:", error);
                if (useAudio) {
                    publishOwnFeed(false);
                }
            }
        });

use this code block to publish your own feed. when you publish your own feed, you will receive your own stream via janus on event called onlocalstream.

onlocalstream: function(stream) {
   Janus.attachMediaStream(document.getElementById(`#video-local`), stream);
},

You feel free to attach that stream into your DOM as your wish.

After you publishing your feed, others in the room will notify about your publication. So that they have to create another pluginHandler as a subscriber and subscribe your feed.

function newRemoteFeed(id, display, audio, video) {
    // A new feed has been published, create a new plugin handle and attach to it as a subscriber
    let remoteFeed = null;
    janusRoom.attach(
        {
            plugin: "janus.plugin.videoroom",
            opaqueId: opaqueId,
            success: function(pluginHandle) {
                remoteFeed = pluginHandle;
                console.log("Plugin attached! (" + remoteFeed.getPlugin() + ", id=" + remoteFeed.getId() + ")");
                console.log("  -- This is a subscriber");
                // We wait for the plugin to send us an offer
                let subscribe = {
                    request: "join",
                    room: myroom,
                    ptype: "subscriber",
                    feed: id,
                    private_id: mypvtid
                };
                remoteFeed.videoCodec = video;
                remoteFeed.send({ message: subscribe });
            },

In here join as a subscriber to the room. After joining as a subscriber, you will receive event called as "attached" with following information.

{
        "videoroom" : "attached",
        "room" : <room ID>,
        "streams" : [
                {
                        "mindex" : <unique m-index of this stream>,
                        "mid" : "<unique mid of this stream>",
                        "type" : "<type of this stream's media (audio|video|data)>",
                        "feed_id" : <unique ID of the publisher originating this stream>,
                        "feed_mid" : "<unique mid of this publisher's stream>",
                        "feed_display" : "<display name of this publisher, if any>",
                        "send" : <true|false; whether we configured the stream to relay media>,
                        "ready" : <true|false; whether this stream is ready to start sending media (will be false at the beginning)>
                },
                // Other streams in the subscription, if any
        ]
}

in there you need to negotiate sdp with remote peer by createAnswer.

 onmessage: function(msg, jsep) {
    console.log('got message from subscripber--------')
    Janus.debug(" ::: Got a message (subscriber) :::", msg);
    let event = msg["videoroom"];
    console.log("Event: " + event);
    if(event) {
        if(event === "attached") {
            console.log(`subscriber created and attached!`);
            feeds[msg['id']] = remoteFeed;
            remoteFeed.rfid = msg["id"];
            remoteFeed.rfdisplay = msg["display"];
        }
    }
    if(jsep) {
        Janus.debug("Handling SDP as well...", jsep);
        // Answer and attach
        remoteFeed.createAnswer(
            {
                jsep: jsep,
                // Add data:true here if you want to subscribe to datachannels as well
                // (obviously only works if the publisher offered them in the first place)
                media: { audioSend: false, videoSend: false },	// We want recvonly audio/video
                success: function(jsep) {
                    console.log("Got SDP!", jsep);
                    let body = { request: "start", room: myroom };
                    remoteFeed.send({ message: body, jsep: jsep });
                },
                error: function(error) {
                    console.error("WebRTC error:", error);
                }
            });

When the peer connection cycle complete you will receive remote stream in the event onremotestream.

onremotestream: function(stream) {      
    Janus.attachMediaStream(document.getElementById(`participant-video-${remoteFeed.rfid}`), stream);
},

You feel free to attach that stream into your DOM as your wish.
Congratulations ! You make your first Janus video conference.