metered_realtime 0.1.0
metered_realtime: ^0.1.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.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.iceServersextraction with fail-closed validation, and credential/SDP scrubbing for log/error surfaces. Loggerinterface withNoopLogger/ConsoleLogger, and a UTF-8 byte-length helper for outbound size checks.SignallingClient: WebSocket pub/sub engine withconnect/close/dispose/subscribe/unsubscribe/publish/send, ack/requestId correlation, auto-reconnect (exponential backoff + jitter, terminal/slow-backoff close codes), token refresh + timeout on thetokenProviderpath, an inactivity watchdog with keepalive ping (disable withinactivityTimeoutMs <= 0), and subscription replay on reconnect. Events are exposed as idiomatic Dart broadcastStreams (onConnected,onDisconnected,stateChanges,onMessage,onDirect,onPresence,onServerError,onGoingAway,onTokenProviderError);dispose()closes them.- Public event/value types
StateChange<T>,ConnectedEvent,DisconnectedEvent,TokenProviderError, and the injectableWebSocketLikeseam (WsCloseInfo,WsReadyState,WebSocketFactory) over a defaultweb_socket_channeladapter (plus a test fake). - Typed exceptions:
SignallingConnectError,SignallingServerError,SignallingDisconnectedError. - Construction-time validation:
ReconnectOptionsandSignallingClientOptionsassert sane values; base URLs reject path/query/fragment/userinfo and non-localhost plaintextws://.
Added — WebRTC orchestration #
- WebRTC abstractions behind an injectable factory (
RtcPeerConnectionLikemirroringflutter_webrtc's surface, plusMediaStream/Track/RtpSender/DataChannel*Liketypes,RtcSessionDescription/RtcIceCandidate,RtcTrackEvent,PeerConnectionState), and aPeerConnectionengine 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-drivenonPeerJoined/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 + directonData, andonTrack/onStreamAdded/onStreamRemoved/stateChanges. Events are Stream-based;send/sendTowith client-side size + state checks; typedMeteredPeerSendError/MeteredPeerStateError/MeteredPeerOversizedError.
Added — flutter_webrtc binding #
- The package now depends on the Flutter SDK and
flutter_webrtc. A single adapter maps the plugin'sRTCPeerConnection/MediaStream/Track/RtpSender/DataChannelto the*Likeabstractions, so the orchestration core stays plugin-agnostic and fake-testable. rtcPeerConnectionFactorynow defaults to theflutter_webrtcbinding —MeteredPeer(MeteredPeerOptions(apiKey: ...))works with no factory; tests still inject a fake.- Send/receive real media with
wrapMediaStream/wrapMediaStreamTrack(orFlutterWebrtcMediaStream/FlutterWebrtcMediaStreamTrack); inbound streams expose.nativefor rendering. Seeexample/.
Added — Resilience, media metadata, data channels #
- Transient signalling drops no longer tear the call down:
MeteredPeer.state(and eachRemotePeer.state) goesreconnecting, peer references stay valid, and on reconnect each surviving peer's transport is refreshed in place before state returns tojoined. Remote streams re-announce viaonStreamAddedwith a fresh stream object (same id) — re-bind your renderer on every event.remote.pcgoes stale across the refresh; re-read it onstateChanges → connected. - Per-track/stream metadata:
addStream(s, metadata: {...})/addTrack(t, metadata: {...})deliver aStreamMetadatabag to every current and future peer ahead of the media; receivers read it on theonTrack/onStreamAddedpayloads. Sender-side lookups viagetTrackMetadata/getStreamMetadata. The bag rides thedirectchannel under the reserved__meteredTrackMetakey — don't use that key in your ownsendTopayloads (doing so logs a warning: the receiving SDK intercepts such payloads and they never surface ononData). MeteredPeer.replaceTrack(oldTrack, newTrack)— swap a sender's track across every peer without renegotiation (or pass null to silence). Partial failures throwMeteredPeerReplaceTrackErrorwith per-peersucceeded/failedoutcomes so you can retry just the failed subset.DataChannel— an opt-in wrapper over a raw channel (fromremote.pc.createDataChannel(...)orRemotePeer.onDataChannel) with typedonOpen/onClose/onError/onMessagestreams and a backpressure-awaresend()(suspends while the transport buffer is abovemaxBufferedAmount— confirmed against a fresh platform reading, with a backstop poll so a missed drain event can't hang sends — and rejects withDataChannelOverflowErrorpastmaxQueuedSends). Configure viaDataChannelOptions(non-positive values throwArgumentError);RtcDataChannelMessagecarries text or binary payloads.- Terminal close codes surfaced on
onErrornow include the code name (e.g.channel_not_authorized).