Skip to content

NoSIP ​

janus.plugin.nosip is an RTP bridge without signalling. A browser talks WebRTC (ICE, DTLS-SRTP) to Janus, and a legacy endpoint talks plain RTP (or SDES-SRTP). The plugin turns a browser's SDP into a plain SDP you can put in your own signalling, and turns the peer's plain SDP into a WebRTC SDP for the browser. It does not speak SIP, XMPP or anything else: your application carries the SDP. Use it when you want to control the signalling layer yourself but still need Janus for the media. If you want Janus to do SIP for you, use the SIP plugin.

Two requests do the work, and both are asynchronous (an ack, then an event with the same transaction):

  • generate: you give it a WebRTC offer or answer (the jsep), it gives you a plain SDP and opens RTP and RTCP sockets for it.
  • process: you give it the peer's plain SDP, it returns a WebRTC SDP as jsep for the browser.

Enable and configure ​

Build with plugin-nosip. The plugin reads janus.plugin.nosip.jcfg:

text
general: {
	local_ip = "127.0.0.1"
	rtp_port_range = "20000-20100"
}

local_ip is the address the plain SDP advertises and the sockets bind to; rtp_port_range is where the RTP and RTCP port pairs come from (the generated SDP below got audio on 20020 and video on 20022, and the socket for RTCP sits next to each RTP port). sdp_ip, events and admin_key are in the NoSIP config reference.

Try it ​

Caller side: generate, then process ​

The browser offer is the canned audio and video offer of parity/sip-peer/sdp/offer-audio-video.sdp. generate answers with a plain SDP, shown here in full:

text
v=0
o=- 4611731400430051336 2 IN IP4 1.1.1.1
s=-
t=0 0
m=audio 20020 RTP/AVP 111
c=IN IP4 127.0.0.1
a=sendrecv
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
m=video 20022 RTP/AVP 96
c=IN IP4 127.0.0.1
a=sendrecv
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 nack pli

That is a plain SDP: RTP/AVP, no a=fingerprint, no a=ice-ufrag, no candidates. The o= address is always 1.1.1.1 (the SDP is anonymised, as in C); the c= lines carry local_ip and the ports are the sockets Janus opened. You send this text to the peer in your own signalling. When the peer's plain answer comes back, process turns it into the WebRTC answer for the browser.

The transcript continues with keyframe, rtp_forward (two forwarders), listforwarders, stop_rtp_forward, recording and hangup:

jsonc
// NoSIP, caller side: a browser offer becomes a plain SDP; the peer's plain answer becomes a WebRTC answer.

// The browser offer is the canned audio and video offer of parity/sip-peer/sdp (a stand-in for a real browser).

// Config: general.local_ip = 127.0.0.1, rtp_port_range = 20000-20100. SDP bodies are shortened except the generated one.


// → POST /janus
{
  "janus": "create",
  "transaction": "t-create"
}

// ← 200 OK
{
  "janus": "success",
  "transaction": "t-create",
  "data": {
    "id": 4100001234567908
  }
}

// → POST /janus/4100001234567908
{
  "janus": "attach",
  "plugin": "janus.plugin.nosip",
  "transaction": "t-attach"
}

// ← 200 OK
{
  "janus": "success",
  "session_id": 4100001234567908,
  "transaction": "t-attach",
  "data": {
    "id": 4100002469135799
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-1",
  "body": {
    "request": "generate",
    "info": "docs"
  },
  "jsep": {
    "type": "offer",
    "sdp": "v=0\r\no=- 4611731400430051336 2 IN IP4 127.0.0.1\r\ns=-\r\n…"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-1"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-1",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "generated",
        "type": "offer",
        "sdp": "v=0\r\no=- 4611731400430051336 2 IN IP4 1.1.1.1\r\ns=-\r\nt=0 0\r\nm=audio 20020 RTP/AVP 111\r\nc=IN IP4 127.0.0.1\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\nm=video 20022 RTP/AVP 96\r\nc=IN IP4 127.0.0.1\r\na=sendrecv\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 nack pli\r\n",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-2",
  "body": {
    "request": "process",
    "type": "answer",
    "sdp": "v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Docs peer\r\n…",
    "info": "docs"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-2"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-2",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "processed",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  },
  "jsep": {
    "type": "answer",
    "sdp": "v=0\r\no=- 1 1 IN IP4 192.0.2.10\r\ns=Docs peer\r\n…"
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-3",
  "body": {
    "request": "keyframe",
    "user": true,
    "peer": true
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-3"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-3",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "keyframesent"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-4",
  "body": {
    "request": "rtp_forward",
    "streams": [
      {
        "type": "audio",
        "host": "127.0.0.1",
        "port": 5004,
        "ssrc": 1111,
        "pt": 111
      },
      {
        "type": "peer_video",
        "host": "127.0.0.1",
        "port": 5006
      }
    ]
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-4"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-4",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "forwarders": [
          {
            "stream_id": 2007654332,
            "type": "audio",
            "host": "127.0.0.1",
            "port": 5004,
            "media": "audio",
            "pt": 111,
            "ssrc": 1111
          },
          {
            "stream_id": 2015308653,
            "type": "peer_video",
            "host": "127.0.0.1",
            "port": 5006,
            "media": "video"
          }
        ],
        "event": "rtp_forward"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-5",
  "body": {
    "request": "listforwarders"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-5"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-5",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "forwarders",
        "rtp_forwarders": [
          {
            "stream_id": 2015308653,
            "type": "peer_video",
            "host": "127.0.0.1",
            "port": 5006,
            "media": "video"
          },
          {
            "stream_id": 2007654332,
            "type": "audio",
            "host": "127.0.0.1",
            "port": 5004,
            "media": "audio",
            "pt": 111,
            "ssrc": 1111
          }
        ]
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-6",
  "body": {
    "request": "stop_rtp_forward",
    "streams": [
      2007654332,
      2015308653
    ]
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-6"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-6",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "stop_rtp_forward",
        "streams": [
          2007654332,
          2015308653
        ]
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-7",
  "body": {
    "request": "recording",
    "action": "start",
    "audio": true,
    "video": true,
    "peer_audio": true,
    "peer_video": true,
    "filename": "docs-call"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-7"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-7",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "recordingupdated"
      }
    }
  }
}

// Files in the server's working directory now: docs-call-peer-audio.mjr, docs-call-peer-video.mjr, docs-call-user-audio.mjr, docs-call-user-video.mjr


// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-8",
  "body": {
    "request": "recording",
    "action": "stop",
    "audio": true,
    "video": true,
    "peer_audio": true,
    "peer_video": true
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-8"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-8",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "recordingupdated"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-9",
  "body": {
    "request": "hangup"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-9"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-9",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "hangingup"
      }
    }
  }
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "hangup",
  "session_id": 4100001234567908,
  "sender": 4100002469135799,
  "reason": "Close PC"
}

Recording start created four files in the server's working directory (the filename you pass is a prefix): <filename>-user-audio.mjr, -user-video.mjr, -peer-audio.mjr, -peer-video.mjr. The note in the transcript lists them.

Callee side: process, then generate ​

When the peer calls you, the plain SDP is an offer. process with type: "offer" returns a JSEP offer for the browser; the browser answers; generate with that answer returns the plain answer for the peer.

jsonc
// NoSIP, callee side: the peer's plain offer becomes a WebRTC offer for the browser;

// the browser's answer becomes a plain answer for the peer. Same config as nosip-caller.


// → POST /janus
{
  "janus": "create",
  "transaction": "t-create"
}

// ← 200 OK
{
  "janus": "success",
  "transaction": "t-create",
  "data": {
    "id": 4100001234567908
  }
}

// → POST /janus/4100001234567908
{
  "janus": "attach",
  "plugin": "janus.plugin.nosip",
  "transaction": "t-attach"
}

// ← 200 OK
{
  "janus": "success",
  "session_id": 4100001234567908,
  "transaction": "t-attach",
  "data": {
    "id": 4100002469135799
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-1",
  "body": {
    "request": "process",
    "type": "offer",
    "sdp": "v=0\r\no=- 1234567890 1 IN IP4 203.0.113.9\r\ns=Legacy peer\r\n…"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-1"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-1",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "processed",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  },
  "jsep": {
    "type": "offer",
    "sdp": "v=0\r\no=- 1234567890 1 IN IP4 192.0.2.10\r\ns=Legacy peer\r\n…"
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-2",
  "body": {
    "request": "generate"
  },
  "jsep": {
    "type": "answer",
    "sdp": "v=0\r\no=- 6100000000001000000 2 IN IP4 127.0.0.1\r\ns=-\r\n…"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-2"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-2",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "generated",
        "type": "answer",
        "sdp": "v=0\r\no=- 6100000000001000000 2 IN IP4 1.1.1.1\r\ns=-\r\nt=0 0\r\nm=audio 20020 RTP/AVP 111\r\nc=IN IP4 127.0.0.1\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\nm=video 20022 RTP/AVP 96\r\nc=IN IP4 127.0.0.1\r\na=sendrecv\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 nack pli\r\n",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-3",
  "body": {
    "request": "hangup"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-3"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-3",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "hangingup"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-4",
  "body": {
    "request": "bogus"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-4"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-4",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "error_code": 442,
      "error": "Unknown request (bogus)"
    }
  }
}

// → POST /janus/4100001234567908
{
  "janus": "attach",
  "plugin": "janus.plugin.nosip",
  "transaction": "t-attach"
}

// ← 200 OK
{
  "janus": "success",
  "session_id": 4100001234567908,
  "transaction": "t-attach",
  "data": {
    "id": 4100003703703690
  }
}

// → POST /janus/4100001234567908/4100003703703690
{
  "janus": "message",
  "transaction": "t-1",
  "body": {
    "request": "generate"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-1"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-1",
  "sender": 4100003703703690,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "error_code": 446,
      "error": "Missing SDP"
    }
  }
}

// → POST /janus/4100001234567908/4100003703703690
{
  "janus": "message",
  "transaction": "t-2",
  "body": {
    "request": "process",
    "type": "answer"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-2"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-2",
  "sender": 4100003703703690,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "error_code": 443,
      "error": "Missing mandatory element (sdp)"
    }
  }
}

// → POST /janus/4100001234567908/4100003703703690
{
  "janus": "message",
  "transaction": "t-3",
  "body": {
    "request": "process",
    "type": "answer",
    "sdp": "not an sdp"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-3"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-3",
  "sender": 4100003703703690,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "error_code": 446,
      "error": "Error parsing SDP: Invalid SDP (doesn't start with v=)"
    }
  }
}

// → POST /janus/4100001234567908/4100003703703690
{
  "janus": "message",
  "transaction": "t-4",
  "body": {
    "request": "hangup"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-4"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-4",
  "sender": 4100003703703690,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "hangingup"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100003703703690
{
  "janus": "message",
  "transaction": "t-5",
  "body": {
    "request": "keyframe",
    "user": true
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-5"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-5",
  "sender": 4100003703703690,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "keyframesent"
      }
    }
  }
}

The second half of that transcript shows the errors: a generate with no JSEP (446 Missing SDP), a process with no sdp (443), and a process with text that is not an SDP (446).

With media ​

Here a real browser (headless Chrome with a fake microphone) sits on one side and a UDP socket on this machine plays the legacy peer. The plain RTP the browser's audio becomes is counted at the socket, and 50 RTP packets sent from the socket to Janus' RTP port are counted at the browser.

jsonc
// NoSIP with media: a real browser (headless Chrome, fake microphone) on one side and a UDP socket on 127.0.0.1:40000 as the legacy peer.

// The browser offer is audio only. SDP bodies are shortened except the generated one.


// → POST /janus
{
  "janus": "create",
  "transaction": "t-create"
}

// ← 200 OK
{
  "janus": "success",
  "transaction": "t-create",
  "data": {
    "id": 4100001234567908
  }
}

// → POST /janus/4100001234567908
{
  "janus": "attach",
  "plugin": "janus.plugin.nosip",
  "transaction": "t-attach"
}

// ← 200 OK
{
  "janus": "success",
  "session_id": 4100001234567908,
  "transaction": "t-attach",
  "data": {
    "id": 4100002469135799
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-1",
  "body": {
    "request": "generate"
  },
  "jsep": {
    "type": "offer",
    "sdp": "v=0\r\no=- 6100000000001000000 2 IN IP4 127.0.0.1\r\ns=-\r\n…"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-1"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-1",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "generated",
        "type": "offer",
        "sdp": "v=0\r\no=- 6100000000001000000 2 IN IP4 1.1.1.1\r\ns=-\r\nt=0 0\r\nm=audio 20020 RTP/AVP 111 63 9 0 8 13 110 126\r\nc=IN IP4 127.0.0.1\r\na=sendrecv\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=rtcp-xr:rcvr-rtt=all\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\n",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  }
}

// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-2",
  "body": {
    "request": "process",
    "type": "answer",
    "sdp": "v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Legacy peer\r\n…"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-2"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-2",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "processed",
        "unique_id": "00a13bcf-3c1f-4d2a-9b5e-000000a13bcf"
      }
    }
  },
  "jsep": {
    "type": "answer",
    "sdp": "v=0\r\no=- 1 1 IN IP4 192.0.2.10\r\ns=Legacy peer\r\n…"
  }
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "webrtcup",
  "session_id": 4100001234567908,
  "sender": 4100002469135799
}


// Plain RTP from Janus to the legacy peer (browser audio): 127 packets arrived in a few seconds.

// First packet: RTP version 2, payload type 111, 44 bytes.

// Browser getStats: outbound-rtp packetsSent = 134; inbound-rtp packetsReceived = 0 (the legacy peer sent 50 packets).


// → POST /janus/4100001234567908/4100002469135799
{
  "janus": "message",
  "transaction": "t-3",
  "body": {
    "request": "hangup"
  }
}

// ← 200 OK
{
  "janus": "ack",
  "session_id": 4100001234567908,
  "transaction": "t-3"
}

// ⇠ event, long poll GET /janus/<session>
{
  "janus": "event",
  "session_id": 4100001234567908,
  "transaction": "t-3",
  "sender": 4100002469135799,
  "plugindata": {
    "plugin": "janus.plugin.nosip",
    "data": {
      "nosip": "event",
      "result": {
        "event": "hangingup"
      }
    }
  }
}

Reference ​

All requests have "request": "<name>". The reply key is nosip, and results are in result.

RequestParametersEvent
generateJSEP offer or answer (required), info, srtp (sdes_optional or sdes_mandatory), srtp_profile, update{"result": {"event": "generated", "type": "offer" or "answer", "sdp": "…", "unique_id": "<uuid>"}}
processtype (offer or answer, required), sdp (required), info, srtp, srtp_profile{"result": {"event": "processed", "srtp", "unique_id"}} with the WebRTC jsep
hangupnone{"result": {"event": "hangingup"}}, then Janus pushes its own hangup event for the handle
keyframeuser (boolean: PLI to the browser), peer (boolean: PLI to the peer){"result": {"event": "keyframesent"}}
recordingaction (start, stop), audio, video, peer_audio, peer_video, filename{"result": {"event": "recordingupdated"}}
rtp_forwardstreams (array of {type: audio, video, peer_audio or peer_video; host; port; host_family; ssrc; pt; srtp_suite; srtp_crypto}), unique_id, admin_key{"result": {"event": "rtp_forward", "forwarders": [{stream_id, type, host, port, media, pt, ssrc}]}}
stop_rtp_forwardstreams (array of stream_id), unique_id, admin_key{"result": {"event": "stop_rtp_forward", "streams": […]}}
listforwardersunique_id, admin_key{"result": {"event": "forwarders", "rtp_forwarders": […]}}

generate and process may be repeated on a handle to renegotiate. In the C trace parity/traces/nosip-requests a second generate on the same handle returns the same SDP and ports with "update": true in the event. That was not run against this binary here.

With srtp the generated SDP uses RTP/SAVP (mandatory) or RTP/AVP with a=crypto lines (optional): written from the C docs and the C trace, not run here.

Error codes ​

CodeMeaning
440 to 444No message, invalid JSON, unknown request, missing element, invalid element
445Wrong state
446Missing or unparseable SDP (Missing SDP, Error parsing SDP: …)
447Invalid SDP
448 to 452I/O error, recording error, too strict, no such stream, no such unique_id
490Unauthorized (wrong admin_key)
499Other

Behaviour you may not expect ​

  • generate does not need a prior process, and a handle can be the caller or the callee. The role depends on the type of the JSEP you give.
  • The SDP is for an RTP session only: data channels are refused (an SDP with m=application fails, as in C).
  • keyframe on a handle with no call still answers keyframesent.
  • The plugin does not bind to the ports of the SDP you give it; it learns the peer's address from the plain SDP you pass to process.

Known gaps ​

  • fails Media from the legacy peer to the browser is not delivered. Verified in the nosip-media transcript: the browser's audio reaches the legacy peer as plain RTP, but RTP packets sent to Janus' RTP port never reach the browser (out packets 0 in the handle's media stats, inbound-rtp 0 in Chrome). The cause is in the source: net::run_peer_relay, the task that reads the peer's sockets, is tested but nothing in the binary starts it, so the RTP and RTCP sockets are bound and never read.
  • RTCP from the peer (and so the PLI the peer's keyframe requests would produce) goes through the same unstarted task.
  • Data channels are not bridged (same as C).
  • rtp_forward creates the forwarders and lists them (the transcript), but forwarded packets were not counted here.
  • SRTP (srtp) and admin_key were not run for this page.

Test it in Janus Bench ​

Open the NoSIP page, or run nosip.generate-process. It generates a plain SDP from a browser offer, checks that it is RTP/AVP without WebRTC attributes, processes a plain answer, and connects the browser. Result today: verified (generate and process). The check does not send RTP from a peer, so it cannot see the gap above.

GPL-3.0-or-later, like the Janus gateway it ports.