metered_realtime 0.2.0 copy "metered_realtime: ^0.2.0" to clipboard
metered_realtime: ^0.2.0 copied to clipboard

Flutter WebRTC SDK for video & voice calls, screen sharing, and realtime pub/sub messaging — signaling, presence, auto-reconnect, and TURN, over flutter_webrtc.

Changelog #

0.2.0 #

Reliability release — hardens the automatic-recovery paths (network switches, dropped connections, unanswered call setup) and closes several lifecycle leaks. One API note: ErrorCode gained values, so an exhaustive switch over it needs a new case — prefer a default branch and treat the enum as open-ended from here on.

Changed #

  • ErrorCode is future-proof. Added ErrorCode.backendError and ErrorCode.internalError (retry-safe service-side failures), plus ErrorCode.unknown for codes newer than this SDK version. A service error the SDK doesn't recognize now rejects the matching subscribe / publish / send immediately with a typed SignallingServerError — previously the call sat out the full ack timeout and failed with a generic error. The on-the-wire code string is preserved on the new rawCode field of ServerErrorEvent and SignallingServerError.
  • Option validation now runs in every build mode. Out-of-range ReconnectOptions values and a non-positive tokenProviderTimeoutMs throw ArgumentError when the client is constructed. Previously these were debug-only assertions, and a bad value in a release build could produce a hot reconnect loop.
  • Delivery failures of connection-setup messages during routine recovery are now logged (via your Logger) rather than surfaced as onNegotiationError — a transport hiccup mid-reconnect is not a negotiation failure, and recovery proceeds on its own.

Fixed #

  • close() on an idle MeteredPeer now closes the underlying connection and completes the peer's event streams. join() can fail at the channel step with the connection already up; closing the peer previously left that connection running in the background and left await for loops on the peer's streams waiting forever.
  • A network drop on an unjoined peer no longer leaves a connection retrying in the background. If join() had failed with the connection still up and the network then dropped, the peer reported itself closed while the connection kept reconnecting beneath it. The connection is now released the moment the wrapper closes, and the peer's event streams complete. A drop arriving during join() itself now settles the peer cleanly closed as well — a later join() on that instance reports the closed state clearly instead of failing with an opaque internal error.
  • A stalled connection attempt fails fast and clean. If the service accepts the socket but the connection never becomes ready, connect() now rejects AND tears the socket down — so a retry can proceed immediately, and a late service handshake can't flip an already-failed connect() to connected behind your back. The same stall during automatic reconnection moves on to the next attempt in seconds instead of waiting on the inactivity watchdog (or stalling indefinitely with the watchdog disabled).
  • Recovery after a drop is watched by a deadline. If the channel roster doesn't arrive after a reconnect, the SDK re-issues the subscription a bounded number of times and then surfaces a typed ReconcileTimeoutError on onError — instead of showing reconnecting forever. Channel re-subscriptions that fail after a reconnect are also retried automatically a bounded number of times. A peer alone in its channel now completes recovery immediately on reconnect.
  • An unanswered connection-recovery offer no longer stalls the retry ladder. If the other side never answers (its connection died mid-recovery), the SDK times the attempt out and moves on, preserving the guarantee that a dead peer connection eventually surfaces the terminal negotiation error.
  • Back-to-back renegotiations are no longer dropped. An offer arriving immediately behind an answer (e.g. the remote starts a screen-share right after connecting) was misread as a collision and ignored by one side; it is now accepted per the WebRTC perfect-negotiation rules. A local offer that races an inbound remote description is abandoned cleanly instead of surfacing a spurious onNegotiationError on native platforms.
  • connect()close()connect() no longer opens two connections when a token fetch is in flight: the superseded attempt aborts (its connect() rejects) instead of racing — or disrupting — the new one.
  • Subscription bookkeeping hygiene. An invalid channel name now rejects with an ArgumentError before being remembered (it used to be re-attempted, and re-fail, on every reconnect); a subscribe attempted while disconnected rejects without leaving a surprise subscription behind for the next reconnect — while a failed re-subscribe of an already-established channel keeps the original subscription intact.
  • sendTo warns when a payload's shape collides with the SDK's connection-control messages (such payloads are consumed by the receiving SDK and never surface on onData).

0.1.0 #

Initial release — the Flutter/Dart SDK for Metered Realtime Messaging.

Added — Signalling core #

  • Wire-protocol types mirroring the Metered Realtime Messaging server (WelcomePayload, ChannelMessage, DirectMessageEvent, PresenceEvent, PresencePeer, ServerErrorEvent, GoingAwayEvent, IceServerConfig, ErrorCode, WsCloseCode).
  • Strict inbound frame parser (malformed frames are dropped, never thrown) and outbound frame builders with client-side validation + generateRequestId.
  • Internal helpers: base-URL validation + connect-URL builder, credential isolation (CredentialStore), welcome.metadata.iceServers extraction with fail-closed validation, and credential/SDP scrubbing for log/error surfaces.
  • Logger interface with NoopLogger / ConsoleLogger, and a UTF-8 byte-length helper for outbound size checks.
  • SignallingClient: WebSocket pub/sub engine with connect/close/dispose/ subscribe/unsubscribe/publish/send, ack/requestId correlation, auto-reconnect (exponential backoff + jitter, terminal/slow-backoff close codes), token refresh + timeout on the tokenProvider path, an inactivity watchdog with keepalive ping (disable with inactivityTimeoutMs <= 0), and subscription replay on reconnect. Events are exposed as idiomatic Dart broadcast Streams (onConnected, onDisconnected, stateChanges, onMessage, onDirect, onPresence, onServerError, onGoingAway, onTokenProviderError); dispose() closes them.
  • Public event/value types StateChange<T>, ConnectedEvent, DisconnectedEvent, TokenProviderError, and the injectable WebSocketLike seam (WsCloseInfo, WsReadyState, WebSocketFactory) over a default web_socket_channel adapter (plus a test fake).
  • Typed exceptions: SignallingConnectError, SignallingServerError, SignallingDisconnectedError.
  • Construction-time validation: ReconnectOptions and SignallingClientOptions assert sane values; base URLs reject path/query/fragment/userinfo and non-localhost plaintext ws://.

Added — WebRTC orchestration #

  • WebRTC abstractions behind an injectable factory (RtcPeerConnectionLike mirroring flutter_webrtc's surface, plus MediaStream/Track/RtpSender/ DataChannel *Like types, RtcSessionDescription/RtcIceCandidate, RtcTrackEvent, PeerConnectionState), and a PeerConnection engine with perfect-negotiation (polite/impolite collision + rollback) and an ICE-restart ladder (9 attempts, give-up-once, reset on connected) + inbound ICE flood caps.
  • MeteredPeer / RemotePeer — the basic 1:N call: join/close, presence-driven onPeerJoined/onPeerLeft, one connection per remote with lexical-politeness perfect-negotiation, local-track fan-out (addStream/addTrack/removeStream/removeTrack), inbound RTC-signal routing (never surfaced as data), broadcast + direct onData, and onTrack/onStreamAdded/onStreamRemoved/stateChanges. Events are Stream-based; send/sendTo with client-side size + state checks; typed MeteredPeerSendError/MeteredPeerStateError/MeteredPeerOversizedError.

Added — flutter_webrtc binding #

  • The package now depends on the Flutter SDK and flutter_webrtc. A single adapter maps the plugin's RTCPeerConnection / MediaStream / Track / RtpSender / DataChannel to the *Like abstractions, so the orchestration core stays plugin-agnostic and fake-testable.
  • rtcPeerConnectionFactory now defaults to the flutter_webrtc binding — MeteredPeer(MeteredPeerOptions(apiKey: ...)) works with no factory; tests still inject a fake.
  • Send/receive real media with wrapMediaStream / wrapMediaStreamTrack (or FlutterWebrtcMediaStream / FlutterWebrtcMediaStreamTrack); inbound streams expose .native for rendering. See example/.

Added — Resilience, media metadata, data channels #

  • Transient signalling drops no longer tear the call down: MeteredPeer.state (and each RemotePeer.state) goes reconnecting, peer references stay valid, and on reconnect each surviving peer's transport is refreshed in place before state returns to joined. Remote streams re-announce via onStreamAdded with a fresh stream object (same id) — re-bind your renderer on every event. remote.pc goes stale across the refresh; re-read it on stateChanges → connected.
  • Per-track/stream metadata: addStream(s, metadata: {...}) / addTrack(t, metadata: {...}) deliver a StreamMetadata bag to every current and future peer ahead of the media; receivers read it on the onTrack / onStreamAdded payloads. Sender-side lookups via getTrackMetadata / getStreamMetadata. The bag rides the direct channel under the reserved __meteredTrackMeta key — don't use that key in your own sendTo payloads (doing so logs a warning: the receiving SDK intercepts such payloads and they never surface on onData).
  • MeteredPeer.replaceTrack(oldTrack, newTrack) — swap a sender's track across every peer without renegotiation (or pass null to silence). Partial failures throw MeteredPeerReplaceTrackError with per-peer succeeded/failed outcomes so you can retry just the failed subset.
  • DataChannel — an opt-in wrapper over a raw channel (from remote.pc.createDataChannel(...) or RemotePeer.onDataChannel) with typed onOpen/onClose/onError/onMessage streams and a backpressure-aware send() (suspends while the transport buffer is above maxBufferedAmount — confirmed against a fresh platform reading, with a backstop poll so a missed drain event can't hang sends — and rejects with DataChannelOverflowError past maxQueuedSends). Configure via DataChannelOptions (non-positive values throw ArgumentError); RtcDataChannelMessage carries text or binary payloads.
  • Terminal close codes surfaced on onError now include the code name (e.g. channel_not_authorized).
0
likes
160
points
108
downloads

Documentation

API reference

Publisher

verified publishermetered.ca

Weekly Downloads

Flutter WebRTC SDK for video & voice calls, screen sharing, and realtime pub/sub messaging — signaling, presence, auto-reconnect, and TURN, over flutter_webrtc.

Homepage

Topics

#webrtc #video-call #signaling #realtime #turn

License

MIT (license)

Dependencies

flutter, flutter_webrtc, web_socket_channel

More

Packages that depend on metered_realtime