janus_client 2.4.4
janus_client: ^2.4.4 copied to clipboard
A feature rich Janus Webrtc package
janus_client #
Table of Contents #
- Package title & intro
- What it will / will not do
- Demo / API Reference / Wiki / links
- Developer Guide & API Reference
- News & Updates
- Feature Status
- WebRTC Feature Support & Testing Matrix
- Contributors
- JavaScript client (
typed_janus_js) - Donations
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 #
Take away apple specific pain for building flutter app #
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
| 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
| 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
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()callsdispose()— 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
sendif 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, nottypedMessages.
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()callsdispose()internally — create a new handle to call again.withCredentials: false(default) meansapiSecret/tokenare never sent even if set.- REST
postreturnsnullon failure, not an exception — check return values. - WebSocket
sendswallows errors when disconnected — validate signaling outcomes in your UI. - TextRoom
editRoomsends"textroom":"create"— verify against your Janus server version. - Apple screen share requires entitlements — see wiki.
flutter_webrtcfeatures (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):
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.
