janus_client 2.4.4 copy "janus_client: ^2.4.4" to clipboard
janus_client: ^2.4.4 copied to clipboard

A feature rich Janus Webrtc package

janus_client #

januscaler

pub packageGitter

All Contributors

Table of Contents #

It is a feature rich flutter package, which offers all webrtc operations supported by Janus: the general purpose WebRTC server, it easily integrates into your flutter application and allows you to build webrtc features and functionality with clean and maintainable code.

Note

What it will do? #

  • It will help you in establishing communication with Janus server using either REST or Websocket depending on what you prefer
  • It will provide you meaningful APIs for individual plugins so you can express your app logic without worrying about internals

What it will not do? #

It will not manage every aspect of WebRTC for you by that we mean we only provide core functionalities and features when it comes to WebRTC, since this library uses flutter_webrtc for managing all of webrtc stack so you will need to refer its documentation when there's some use cases which we do not cover or does not exist in examples. This is done intentionally by design to give developers complete flexibility while also making sure library is lightweight and does not become a bloatware. A classic example of that would be changing output device on a native device for example you want to switch from speaker to headsets or bluetooth audio device you will need to use flutter_webrtc's Helper utility class:-

Helper.selectAudioOutput(deviceIdOfBluetoothDevice) 

Demo of JanusClient #

APIReference #

Wiki #

screen share example #

Developer Guide & API Reference #

Full generated docs: API reference.

Quick start #

final ws = WebSocketJanusTransport(url: 'wss://your-janus.example/janus');
final client = JanusClient(
  transport: ws,
  iceServers: [RTCIceServer(urls: 'stun:stun.l.google.com:19302')],
);
final session = await client.createSession();
final echo = await session.attach<JanusEchoTestPlugin>();
// now call initializeMediaDevices / createOffer / send on echo

Swap WebSocketJanusTransport for RestJanusTransport to use HTTP long-polling instead.

Architecture overview #

flowchart TD
  classDef tr fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
  classDef co fill:#fef3c7,stroke:#f59e0b,color:#78350f
  classDef pl fill:#ede9fe,stroke:#7c3aed,color:#2e1065
  classDef ou fill:#dcfce7,stroke:#16a34a,color:#14532d

  REST([RestJanusTransport]):::tr
  WS([WebSocketJanusTransport]):::tr
  JC[JanusClient]:::co
  JS[JanusSession]:::co

  REST -->|pass as transport| JC
  WS  -->|pass as transport| JC
  JC  -->|createSession| JS

  JS  -->|attach VideoRoom | VR[JanusVideoRoomPlugin]:::pl
  JS  -->|attach VideoCall | VC[JanusVideoCallPlugin]:::pl
  JS  -->|attach AudioBridge| AB[JanusAudioBridgePlugin]:::pl
  JS  -->|attach SIP       | SI[JanusSipPlugin]:::pl
  JS  -->|attach Streaming | ST[JanusStreamingPlugin]:::pl
  JS  -->|attach TextRoom  | TR[JanusTextRoomPlugin]:::pl
  JS  -->|attach EchoTest  | ET[JanusEchoTestPlugin]:::pl

  VR & VC & AB & SI & ST & TR & ET --> EV["typedMessages — Janus events"]:::ou
  VR & VC & AB & SI & ST & TR & ET --> MD["remoteTrack / remoteStream — incoming media"]:::ou
  VR & VC & AB & SI & ST & TR & ET --> DC["data / onData — DataChannel messages"]:::ou
  VR & VC & AB & SI & ST & TR & ET --> PC["peerConnection / mediaStats — WebRTC internals"]:::ou

Core API #

JanusClient

lib/janus_client.dart

Constructor param Default What it does
transport required RestJanusTransport or WebSocketJanusTransport
iceServers null ICE servers passed to every peer connection
isUnifiedPlan true Use unified-plan SDP (recommended)
apiSecret / token null Auth; only sent when withCredentials: true
stringIds false Coerce room ids to strings in plugin payloads
refreshInterval 50 Session keepalive interval (seconds)
Method Returns Notes
createSession() Future<JanusSession> Calls session.create() internally; errors are caught and logged — check sessionId if something fails
getInfo() Future<JanusClientInfo> Fetch Janus server info

JanusSession

lib/janus_session.dart

API Notes
create() Opens session on Janus; starts keepalive
attach<T extends JanusPlugin>({opaqueId}) Returns a typed plugin handle (JanusVideoRoomPlugin, JanusVideoCallPlugin, JanusAudioBridgePlugin, JanusSipPlugin, JanusStreamingPlugin, JanusTextRoomPlugin, JanusEchoTestPlugin)
dispose() Tears down the session and the shared transport
sessionId Populated after create()

JanusTransport

lib/janus_transport.dart

WebSocketJanusTransport — preferred for low latency.

Param Default Notes
url required wss://…
sendCompleterTimeout 20s How long send waits for an ack
autoReconnect true
heartbeatInterval 10s
maxMessageMissedRetries 3
maxReconnectAttempts 5

RestJanusTransport — works everywhere. post / get return null on errors instead of throwing.

JanusPlugin (base)

lib/janus_plugin.dart — every plugin handle inherits these.

Streams you subscribe to:

Stream What arrives
typedMessages Janus plugin events as typed Dart classes
messages Raw Janus JSON events
remoteTrack / remoteStream Incoming audio/video tracks
data / onData DataChannel messages
renegotiationNeeded SDP renegotiation trigger

Methods you call:

Method Notes
initializeWebRTCStack() Create RTCPeerConnection
initializeMediaDevices({mediaConstraints, useDisplayMediaDevices, desktopCaptureContext, screenSelectDialogBuilder, ...}) getUserMedia or getDisplayMedia; for desktop capture pass desktopCaptureContext + optionally a custom screenSelectDialogBuilder
createOffer() / createAnswer() SDP negotiation
handleRemoteJsep(jsep) setRemoteDescription
send({data, jsep}) Send a Janus message
initDataChannel() / sendData(msg) DataChannel setup and send
hangup({disposeStream}) / dispose() Teardown
getBitrate() / watchNetworkQualityLevel() Stats — see Bitrate & network quality

JanusError & typed events

lib/interfaces/typed_event.dart

plugin.typedMessages?.listen((TypedEvent e) {
  final data = e.event.plugindata?.data;
  // data is a typed event class — pattern-match per plugin below
  await plugin.handleRemoteJsep(e.jsep);
}, onError: (e) {
  if (e is JanusError) { /* handle */ }
});

Wrapper plugins #

JanusVideoRoomPlugin

lib/wrapper_plugins/janus_video_room_plugin.dart · SFU rooms, simulcast, dynamic subscriber feeds.

Key methods: joinPublisher, joinSubscriber, publishMedia, configure, subscribeToStreams, update, unsubscribe, unpublish, createRoom, destroyRoom, getRooms, getRoomParticipants, hangup

final plugin = await session.attach<JanusVideoRoomPlugin>();
plugin.typedMessages?.listen((e) async {
  if (e.event.plugindata?.data is VideoRoomJoinedEvent) {
    await plugin.configure(
      sessionDescription: await plugin.createOffer(audioRecv: false, videoRecv: false));
  }
  await plugin.handleRemoteJsep(e.jsep);
});
await plugin.joinPublisher(roomId, displayName: 'Alice');

Typed events: VideoRoomJoinedEvent, VideoRoomConfigured, VideoRoomNewPublisherEvent, VideoRoomAttachedEvent, VideoRoomLeavingEvent, VideoRoomUnPublishedEvent, …


JanusVideoCallPlugin

lib/wrapper_plugins/janus_video_call_plugin.dart · 1:1 registered calls.

Key methods: register, call, acceptCall, set, getList, hangup

await plugin.register('Alice');
await plugin.initializeMediaDevices(mediaConstraints: {'audio': true, 'video': true});
await plugin.call('Bob', offer: await plugin.createOffer(audioRecv: true, videoRecv: true));

hangup() calls dispose() — the plugin handle cannot be reused after hanging up.


JanusAudioBridgePlugin

lib/wrapper_plugins/janus_audio_bridge_plugin.dart · Audio-only conferencing.

Key methods: createRoom (permanent defaults true), joinRoom, configure, muteParticipant, kickParticipant, rtpForward, stopRtpForward, listParticipants, hangup

await plugin.initializeMediaDevices(mediaConstraints: {'audio': true, 'video': false});
await plugin.joinRoom(roomId, display: 'Alice');
plugin.typedMessages?.listen((e) async {
  if (e.event.plugindata?.data is AudioBridgeJoinedEvent) await plugin.configure();
  await plugin.handleRemoteJsep(e.jsep);
});

JanusSipPlugin

lib/wrapper_plugins/janus_sip_plugin.dart · WebRTC ↔ SIP gateway.

Key methods: register, unregister, call, accept, decline, hangup, hold(SipHoldState), unhold, transfer, recording

No built-in DTMF wrapper — use raw send if your Janus build supports it.


JanusStreamingPlugin

lib/wrapper_plugins/janus_streaming_plugin.dart · Consume RTSP/RTP live streams.

Key methods: listStreams, watchStream, startStream, pauseStream, stopStream, switchStream, getStreamInfo, createStream, editStream, destroyStream

await plugin.watchStream(mountId);
plugin.typedMessages?.listen((e) async {
  if (e.event.plugindata?.data is StreamingPluginPreparingEvent) {
    await plugin.handleRemoteJsep(e.jsep);
    await plugin.startStream();
  }
});

JanusTextRoomPlugin

lib/wrapper_plugins/janus_text_room_plugin.dart · Text chat over DataChannel.

Key methods: setup, joinRoom, sendMessage, leaveRoom, listRooms, listParticipants, createRoom, destroyRoom

await plugin.setup();
plugin.data?.listen((msg) { /* parse TextRoom JSON */ });
await plugin.joinRoom(roomId, username, display: username);
await plugin.sendMessage(roomId, 'hello');

Chat events arrive on data, not typedMessages.


JanusEchoTestPlugin

lib/wrapper_plugins/janus_echo_test_plugin.dart · Loopback connectivity test. No extra methods — use the base JanusPlugin API directly.


Plugin quick-reference

Plugin Key entrypoints Events arrive on
VideoRoom joinPublisher · joinSubscriber · publishMedia · subscribeToStreams typedMessages
VideoCall register · call · acceptCall · hangup typedMessages
AudioBridge joinRoom · configure · muteParticipant typedMessages
SIP register · call · accept · hold / unhold typedMessages
Streaming watchStream · startStream · stopStream typedMessages + SDP via messages
TextRoom setup · joinRoom · sendMessage data channel
EchoTest send + manual SDP messages

Desktop screen capture UI #

lib/widgets/screen_select.dart · screen_select_compact.dart · screen_select_sidebar.dart

Widget Layout
ScreenSelectDialog Default Material grid picker
CompactScreenSelectDialog Segmented tabs + badge thumbnails
SidebarScreenSelectDialog Sidebar list + large preview

Pass via initializeMediaDevices:

await plugin.initializeMediaDevices(
  useDisplayMediaDevices: true,
  desktopCaptureContext: context,
  screenSelectDialogBuilder: (_) => const CompactScreenSelectDialog(),
);

Subclass ScreenSelectDialogState and override buildHeader, buildTabs, buildBody, buildSourceGrid, buildThumbnail, buildEmpty, buildLoading, or buildActions for full custom layouts.


Bitrate & network quality #

Each plugin handle exposes mediaStats (JanusStreamStats).

API Returns
getBitrate([mid]) Human-readable label (empty until first measurement)
getBitrateMeasurement(query) Structured BitrateMeasurement
getNetworkQuality(query) / watchNetworkQuality(query) One-shot / stream of double quality score
watchNetworkQualityLevel(thresholds) Stream of NetworkQualityLevel enum

Scenarios cookbook #

Goal Start here
1:1 video call JanusVideoCallPlugin · video_call.dart
Multi-party SFU + simulcast JanusVideoRoomPlugin · google_meet.dart
Audio-only conference JanusAudioBridgePlugin · audio_bridge.dart
SIP ↔ WebRTC JanusSipPlugin · sip.dart
Live RTSP/RTP stream JanusStreamingPlugin · streaming.dart
Text chat JanusTextRoomPlugin · text_room.dart
Connectivity / echo test JanusEchoTestPlugin
Desktop screen share + custom picker initializeMediaDevices(useDisplayMediaDevices, desktopCaptureContext, screenSelectDialogBuilder)
Bitrate / signal-strength HUD watchNetworkQualityLevel + getBitrate — see google_meet.dart overlays

Gotchas #

  • JanusSession.dispose() also disposes the transport — all sessions sharing that transport are affected.
  • JanusVideoCallPlugin.hangup() calls dispose() internally — create a new handle to call again.
  • withCredentials: false (default) means apiSecret / token are never sent even if set.
  • REST post returns null on failure, not an exception — check return values.
  • WebSocket send swallows errors when disconnected — validate signaling outcomes in your UI.
  • TextRoom editRoom sends "textroom":"create" — verify against your Janus server version.
  • Apple screen share requires entitlements — see wiki.
  • flutter_webrtc features (e.g. Helper.selectAudioOutput) are out of scope — see the callout at the top of this README.

Version & migration notes #

Current: 2.4.5 (pubspec.yaml) — see full CHANGELOG.md.

Version Change
2.4.0 Desktop display capture in initializeMediaDevices; sendCompleterTimeout on WS transport
2.4.1 maxMessageMissedRetries on WebSocketJanusTransport
2.4.2 JanusStreamStats, getNetworkQuality, watchNetworkQuality
2.4.3 disposeStream param on hangup across plugins
2.4.4 REST polling URL fix; JanusPlugin.buildPollingUri
2.4.5 ScreenSelectDialog family + screenSelectDialogBuilder on initializeMediaDevices

News & Updates #

  • Introduced support for simulcast
  • videoroom and screenshare improvements (screenshare tested on android and chrome)
  • sip plugin wrapper added with sip calling example
  • Added errorHandler for typedMessage Stream for better development flow
  • Just like new flutter version comes With desktop support out of the box
  • All major plugins fully support unified plan
  • Typed examples updated with null safety and latest dart constraints
  • Introduced plugin specific wrapper classes with respective operation methods for rich development experience
  • Introduced typed events (Class Based Events) for brilliant auto completion support for IDE
  • Supports null-safety

Feature Status #

Feature Support Well Tested Unified Plan Example
WebSocket Yes Yes - Yes
Rest/Http API Yes Yes - Yes
Video Room Plugin Yes No Yes Yes
Video Call Plugin Yes No Yes Yes
Streaming Plugin Yes No Yes Yes
Audio Room Plugin Yes No Yes Yes
Sip Plugin Yes No Yes Yes
Text Room Plugin Yes No - Yes
ScreenSharing using VideoRoom plugin Yes No Yes Yes

WebRTC Feature Support & Testing Matrix #

Feature Android iOS Windows Linux macOS Browser
VideoRoom ✅ (🟢) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟢) ✅ (🟢)
VideoCall ✅ (🟢) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟢)
SIP ✅ (🟢) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟢)
AudioRoom ✅ (🟢) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟢)
Streaming ✅ (🟢) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟡) ✅ (🟢)

Legend: #

  • ✅ = Supported
  • ❌ = Supported
  • 🟢 = Tested
  • 🟡 = Not fully Tested

Contributors ✨ #

Thanks goes to these wonderful people (emoji key):

Shivansh Talwar
Shivansh Talwar

💻 📖
Kelvin Zawadi
Kelvin Zawadi

💻
Eugene
Eugene

💻
Igal Avraham
Igal Avraham

💻
Vigikaran
Vigikaran

💻
UserSense
UserSense

💻
baihua666
baihua666

🐛
ngoluuduythai
ngoluuduythai

💻
Saksham Gupta
Saksham Gupta

💻
chu06
chu06

💻
Musagil Musabayli
Musagil Musabayli

💻
Mazen Amr
Mazen Amr

💻
Patrick Schmidt
Patrick Schmidt

💻
Ivan Saprykin
Ivan Saprykin

💻
Lukas Hronec
Lukas Hronec

💻
Satoshi
Satoshi

💻
Chukwuemeka Ihedoro
Chukwuemeka Ihedoro

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

Wait there's more... The Javascript Client! #

If you loved the api style and architecture of flutter_janus_client and you wishing to have something similar for your next javascript project involving webrtc features. then worry not because we have got you covered. we have written wrapper on top of our good old janus.js, you might ask why? well the answer to that question is it does not support type bindings hence no rich ide support, so we proudly presents typed_janus_js(feature rich promisified and reactive wrapper on top of janus.js) or you can straight away use it by installing from npm npm i typed_janus_js.

Donations #

ko-fi

Buy Me A Coffee