Event types
Every event that an event handler receives is a JSON object with the same outer shape and a body that depends on the type. The examples on this page are real events: a headless Chrome made an EchoTest call (audio and video) against the Rust binary, and a sample HTTP handler wrote down what it received. Session and handle ids change from run to run.
The envelope
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 1,
"timestamp": 1793807320859272,
"session_id": 7619045123486259,
"handle_id": 3190114712345680,
"opaque_id": "docs-demo",
"event": { }
}| Field | Type | When | Meaning |
|---|---|---|---|
emitter | string | when the server has a name (server_name in janus.jcfg; the default is MyJanusInstance) | The server that raised the event. GELF uses it as host. |
type | integer | always | The type number from the table below. |
subtype | integer | only when above 0 | Narrows core, webrtc and media events. |
timestamp | integer | always | Microseconds since the Unix epoch, wall clock. |
session_id | integer | all types except core and external; plugin and transport events leave it out when they have no session | The session the event is about. |
handle_id | integer | handle, jsep, webrtc, media and plugin events | The handle. |
opaque_id | string | when the client gave one at attach | Lets you tie a handle to your own id. |
event | object | always | The body, described per type below. |
The handlers add to this: MQTT adds eventtype (the readable type name), GELF wraps the whole object in full_message. See the handler descriptions.
The types
The number is one bit, so the numbers add up in a mask. events in a handler file takes the label. all is every bit (4294967295).
| Type | Number | Label in events | Name (GELF short_message) | Subtypes | Raised when |
|---|---|---|---|---|---|
| session | 1 | sessions | Sessions | none | A session is created, destroyed or times out. |
| handle | 2 | handles | Handles | none | A handle attaches to a plugin or detaches. |
| external | 4 | external | External | none | An Admin API custom_event request. |
| jsep | 8 | jsep | Jsep | none | An SDP arrives from a client or goes out to one. |
| webrtc | 16 | webrtc | WebRTC | 1 ICE, 2 local candidate, 3 remote candidate, 4 selected pair, 5 DTLS, 6 PeerConnection state | ICE, DTLS or the PeerConnection changes. |
| media | 32 | media | Media | 1 state, 2 slow link, 3 statistics | Media starts or stops flowing; every second per medium. |
| plugin | 64 | plugins | Plugins | none | A plugin raises its own event. |
| transport | 128 | transports | Transports | none | A transport reports a connection. |
| core | 256 | core | Core | 1 startup, 2 shutdown | Janus starts, every 15 s after, and stops. |
none (mask 0) selects no events.
Session (type 1)
event.name is created, destroyed or timeout (the session expired with no keepalive). created adds a transport object when the session was made over HTTP: transport is the package name of the transport and id is a number that stands for the connection.
// Type 1 (sessions). The `transport` object says which transport created the session.
// ⇠ type 1: created over HTTP
{
"emitter": "MyJanusInstance",
"type": 1,
"timestamp": 1793808494458194,
"session_id": 1724494162477229,
"event": {
"name": "created"
}
}
// ⇠ type 1: destroyed
{
"emitter": "MyJanusInstance",
"type": 1,
"timestamp": 1793808494859758,
"session_id": 1724494162477229,
"event": {
"name": "destroyed"
}
}The transport object is missing for other transports
A session created over WebSockets, a Unix socket or nanomsg sends {"name": "created"} with no transport. The C server always includes it. Code that reads event.transport.transport must tolerate a missing object.
Handle (type 2)
event.name is attached or detached; event.plugin is the plugin package name. If the client gave opaque_id at attach it is in the envelope and, as the C server does for older consumers, repeated inside event. If a token was used to attach, it is in event.token. The top-level handle_id is the handle.
// Type 2 (handles). `opaque_id` is the one the client gave at attach.
// ⇠ type 2: attached
{
"emitter": "MyJanusInstance",
"type": 2,
"timestamp": 1793808496260761,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"name": "attached",
"plugin": "janus.plugin.echotest",
"opaque_id": "docs-demo"
}
}
// ⇠ type 2: detached
{
"emitter": "MyJanusInstance",
"type": 2,
"timestamp": 1793808505829415,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"name": "detached",
"plugin": "janus.plugin.echotest",
"opaque_id": "docs-demo"
}
}External (type 4)
Sent when someone calls the Admin API request custom_event: schema names your format and data is any JSON. The event has no session_id.
// Type 4 (external): sent with the Admin API request custom_event. It has no session_id.
// ⇠ type 4
{
"emitter": "MyJanusInstance",
"type": 4,
"timestamp": 1793808495262621,
"event": {
"schema": "docs.example",
"data": {
"hello": "world",
"count": 3
}
}
}The request that made it was {"janus": "custom_event", "schema": "docs.example", "data": {"hello": "world", "count": 3}, "admin_secret": "janusoverlord", "transaction": "t"}.
Jsep (type 8)
One event for each SDP that passes the core. event.owner is remote for what the client sent and local for what Janus sent back. event.jsep has type (offer, answer) and the full sdp. The SDPs below are cut after the third line; the real events carry all of it.
// Type 8 (jsep): every SDP that crosses the core. `owner` is who made it. The SDP is shortened here.
// ⇠ type 8: remote offer
{
"emitter": "MyJanusInstance",
"type": 8,
"timestamp": 1793808496659900,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"owner": "remote",
"jsep": {
"type": "offer",
"sdp": "v=0\r\no=- 8687266155835230716 2 IN IP4 127.0.0.1\r\ns=-\r\n…"
}
}
}
// ⇠ type 8: local answer
{
"emitter": "MyJanusInstance",
"type": 8,
"timestamp": 1793808496661746,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"owner": "local",
"jsep": {
"type": "answer",
"sdp": "v=0\r\no=- 8687266155835230716 2 IN IP4 192.0.2.10\r\ns=-\r\n…"
}
}
}WebRTC (type 16)
The subtype says what changed. All of them have stream_id and component_id (1 and 1 with BUNDLE and rtcp-mux, which is what browsers use).
| Subtype | Body | Values |
|---|---|---|
| 1 | ice | disconnected, gathering, connecting, connected, ready, failed |
| 2 | local-candidate | the candidate line Janus sent |
| 3 | remote-candidate | the candidate line the client sent |
| 4 | selected-pair, candidates | a text form of the pair, and local and remote objects with address, port, type, transport and family |
| 5 | dtls, retransmissions | created, trying, connected, failed |
| 6 | connection | webrtcup, or hangup with an optional reason |
The order in the run was: ICE gathering, the local candidates, the remote candidates, ICE connecting, connected, the selected pair, ICE ready, DTLS trying, DTLS connected, webrtcup. A hangup sends hangup with "reason": "Janus API" when the client asked for it.
// Type 16 (webrtc), with a subtype for each kind of update: 1 ICE state, 2 local candidate, 3 remote candidate, 4 selected pair, 5 DTLS state, 6 PeerConnection state (webrtcup and hangup).
// ⇠ type 16, subtype 1: ICE state
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 1,
"timestamp": 1793808497031211,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"ice": "connected",
"stream_id": 1,
"component_id": 1
}
}
// ⇠ type 16, subtype 2: local candidate
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 2,
"timestamp": 1793808496661790,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"local-candidate": "1 1 udp 2015363327 192.0.2.10 57734 typ host",
"stream_id": 1,
"component_id": 1
}
}
// ⇠ type 16, subtype 3: remote candidate
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 3,
"timestamp": 1793808496661801,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"remote-candidate": "800151196 1 udp 2122260223 192.0.2.11 64725 typ host",
"stream_id": 1,
"component_id": 1
}
}
// ⇠ type 16, subtype 4: selected pair
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 4,
"timestamp": 1793808497364895,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"selected-pair": "192.0.2.10:57734 [host,udp] <-> 192.0.2.10:55114 [host,udp]",
"candidates": {
"local": {
"address": "192.0.2.10",
"port": 57734,
"type": "host",
"transport": "udp",
"family": 4
},
"remote": {
"address": "192.0.2.10",
"port": 55114,
"type": "host",
"transport": "udp",
"family": 4
}
},
"stream_id": 1,
"component_id": 1
}
}
// ⇠ type 16, subtype 5: DTLS state
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 5,
"timestamp": 1793808497967814,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"dtls": "connected",
"stream_id": 1,
"component_id": 1,
"retransmissions": 0
}
}
// ⇠ type 16, subtype 6: webrtcup
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 6,
"timestamp": 1793808497967879,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"event": {
"connection": "webrtcup"
},
"opaque_id": "docs-demo"
}
// ⇠ type 16, subtype 6: hangup
{
"emitter": "MyJanusInstance",
"type": 16,
"subtype": 6,
"timestamp": 1793808504826819,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"event": {
"connection": "hangup",
"reason": "Janus API"
},
"opaque_id": "docs-demo"
}Media (type 32)
| Subtype | What | Body |
|---|---|---|
| 1 | media started or stopped flowing | media (audio, video), mid, receiving (true or false), substream for simulcast, seconds how long it was silent when receiving is false |
| 2 | slow link | media, mid, slow_link (uplink or downlink), lost_lastsec |
| 3 | statistics | one event per medium every stats_period seconds (default 1): see below. With combine_media_stats there is one event per handle with an array |
The statistics body: mid, mindex, media, codec, base (clock rate), rtt, rtt-values (once a receiver report has arrived), lost, lost-by-remote, jitter-local, jitter-remote, the four link-quality percentages, packets-received, packets-sent, bytes-received, bytes-sent, bytes-received-lastsec, bytes-sent-lastsec, nacks-received, nacks-sent, retransmissions-received, and remb-bitrate when the browser sent one. With simulcast, the second and third layer are reported as their own events with media set to video-sim1 and video-sim2, and a data channel has an event with media set to data.
A media statistics event is one of the most frequent events: with stats on and a handle with audio and video, expect two events per second per handle. Turn stats_period up, or off with 0, if the volume matters.
// Type 32 (media): subtype 1 media state, 2 slow link, 3 statistics. The slow link event is not shown: it needs packet loss, which this run did not have.
// ⇠ type 32, subtype 1: media is flowing
{
"emitter": "MyJanusInstance",
"type": 32,
"subtype": 1,
"timestamp": 1793808497980440,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"media": "video",
"mid": "1",
"receiving": true
}
}
// ⇠ type 32, subtype 3: statistics (one per medium, every second)
{
"emitter": "MyJanusInstance",
"type": 32,
"subtype": 3,
"timestamp": 1793808498971965,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"mid": "0",
"mindex": 0,
"media": "audio",
"codec": "opus",
"base": 48000,
"rtt": 0,
"lost": 0,
"lost-by-remote": 0,
"jitter-local": 0,
"jitter-remote": 0,
"in-link-quality": 100,
"in-media-link-quality": 100,
"out-link-quality": 100,
"out-media-link-quality": 100,
"packets-received": 50,
"packets-sent": 50,
"bytes-received": 3861,
"bytes-sent": 3461,
"bytes-received-lastsec": 0,
"bytes-sent-lastsec": 0,
"nacks-received": 0,
"nacks-sent": 0,
"retransmissions-received": 0
}
}Check the numbers before you alert on them
In this run, once the first receiver reports arrived, rtt was 45542573 (a "millisecond" value of 12 hours), with rtt-values.ntp smaller than lsr. And lost for the video stream was -8318. Both came from a clean loopback call with Chrome and fake devices. The C server gives an rtt of a few milliseconds there. I did not compare the numbers with the C server, but these two are wrong enough that you should not build alerts on rtt or lost until they are fixed.
Plugin (type 64)
event.plugin is the package name; event.data is whatever the plugin passes to notify_event. The shape of data is the plugin's: see the plugin's own page. EchoTest sends its settings when you change them. Plugin events may have no session_id or handle_id when the plugin raises them outside a handle.
// Type 64 (plugin): what a plugin passes to notify_event. The shape of `data` is the plugin's.
// ⇠ type 64, from EchoTest
{
"emitter": "MyJanusInstance",
"type": 64,
"timestamp": 1793808496661617,
"session_id": 8229749208234940,
"handle_id": 8977468686213072,
"opaque_id": "docs-demo",
"event": {
"plugin": "janus.plugin.echotest",
"data": {
"audio_active": true,
"video_active": true,
"bitrate": 0
}
}
}Transport (type 128)
event.transport is the transport package, event.id stands for the connection, and event.data is the transport's own payload with an event name.
| Transport | data.event | Other keys |
|---|---|---|
janus.transport.http | request | admin_api, ip, port of the client. One per HTTP request. |
janus.transport.websockets | connected, disconnected | admin_api, ip (connected) |
janus.transport.pfunix | connected, disconnected | admin_api, fd, and type for SOCK_DGRAM |
janus.transport.mqtt | connected, disconnected, reconnecting, failed | |
janus.transport.rabbitmq | connected | |
janus.transport.nanomsg | created | admin_api, socket |
Each transport has a general.events switch that turns its transport events off.
// Type 128 (transport). `transport` is the package, `data` is the transport's own payload. HTTP reports each request, WebSockets each connection.
// ⇠ type 128: HTTP request
{
"emitter": "MyJanusInstance",
"type": 128,
"timestamp": 1793808494453987,
"event": {
"transport": "janus.transport.http",
"id": "1",
"data": {
"event": "request",
"admin_api": false,
"ip": "127.0.0.1",
"port": 49650
}
}
}
// ⇠ type 128: WebSocket connected
{
"emitter": "MyJanusInstance",
"type": 128,
"timestamp": 1793808494457685,
"event": {
"transport": "janus.transport.websockets",
"id": "6224993832",
"data": {
"event": "connected",
"admin_api": false,
"ip": "127.0.0.1"
}
}
}
// ⇠ type 128: WebSocket disconnected
{
"emitter": "MyJanusInstance",
"type": 128,
"timestamp": 1793808494859703,
"event": {
"transport": "janus.transport.websockets",
"id": "53703894096",
"data": {
"event": "disconnected"
}
}
}The WebSocket ids do not match
In the run above, the connected event of a WebSocket connection has the id 6204218920 and its disconnected event a different one. The id is meant to let you pair the two events. With the Rust port, pair them by order and ip instead, or by the session ids the connection owned.
Core (type 256)
Subtype 1 (startup) is sent when Janus starts, with the same body as GET /janus/info in event.info, and again every 15 seconds with "status": "update" and the counts. Subtype 2 (shutdown) is sent when Janus gets a stop signal, with signum.
// Type 256 (core). Subtype 1 is startup, 2 is shutdown. The core also repeats the startup subtype every 15 s with the session, handle and PeerConnection counts.
// ⇠ type 256, subtype 1: startup
{
"emitter": "MyJanusInstance",
"type": 256,
"subtype": 1,
"timestamp": 1793808494306124,
"event": {
"status": "started",
"info": {
"janus": "server_info",
"…": "the same body as GET /janus/info"
}
}
}
// ⇠ type 256, subtype 1: periodic status update
{
"emitter": "MyJanusInstance",
"type": 256,
"subtype": 1,
"timestamp": 1793808509312575,
"event": {
"status": "update",
"info": {
"sessions": 0,
"handles": 0,
"peerconnections": 0,
"stats-period": 1
}
}
}
// ⇠ type 256, subtype 2: shutdown (caught by a GELF handler: see the note on the sample handler in the events page)
{
"emitter": "MyJanusInstance",
"type": 256,
"subtype": 2,
"timestamp": 1793808511145471,
"event": {
"status": "shutdown",
"signum": 15
}
}The info bodies are cut in the transcript; the real startup event carries the full server info (about 150 lines). The shutdown event was received by a GELF handler: the sample HTTP handler of the same run did not get it.
Known gaps
- gap
session createdevents lack thetransportobject unless the session came over HTTP. - gap The
idof a WebSocket transport event changes betweenconnectedanddisconnected. - gap The media statistics
rttandlostvalues in the recorded run are not plausible. - gap The slow link event (media subtype 2) was not produced here: it needs packet loss. Its shape is from the C source.
- gap The
timeoutsession event, the MQTTreconnectingandfailedtransport events, and simulcast statistics were not produced here either; their shapes are from the C source. - gap
infodoes not report event handlers (event_handlersis always false).