@streaming-cdn/rtc-web 1.3.14 → 1.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RTC Web SDK 1.3.14
1
+ # RTC Web SDK 1.3.16
2
2
 
3
3
  The customer package contains compiled ESM/UMD JavaScript and TypeScript declarations. It intentionally excludes implementation source and source maps. Example source remains available under `examples/`.
4
4
 
@@ -169,6 +169,75 @@ the room client (`getNativeClient()`); the publisher keeps sending every
169
169
  layer. The React Native adapter has the same transport option and the same
170
170
  `setRemoteQuality` on the client.
171
171
 
172
+ ## Virtual backgrounds and effects
173
+
174
+ `createVideoEffectsPipeline(cameraTrack)` draws the camera through a canvas
175
+ and hands back a processed track for `setVideoTrack`, so both transports
176
+ publish it unchanged. Backgrounds: `blur`, animated procedural scenes
177
+ (`aurora`, `cybergrid`, `bokeh`, `sunset`), or your own `image`. Overlays:
178
+ `snow`, `confetti`, `vignette`. Person cut-out uses any `Segmenter` you plug
179
+ in (for example a self-hosted MediaPipe selfie model); without one the
180
+ pipeline degrades honestly — `blur` blurs the whole frame, scenes show the
181
+ camera as a floating card.
182
+
183
+ ```ts
184
+ const effects = createVideoEffectsPipeline(cameraTrack, { segmenter });
185
+ effects.setBackground("aurora");
186
+ effects.setOverlay("confetti");
187
+ await media.setVideoTrack(effects.track);
188
+ ```
189
+
190
+ ## Virtual avatar
191
+
192
+ `createAvatarPipeline(cameraTrack, { tracker })` analyzes the person on-device
193
+ and publishes an animated avatar instead of the camera — blink, gaze, brows,
194
+ jaw, mouth and head pose all mirror the real face. The output is an ordinary
195
+ canvas track for `setVideoTrack`, so both transports carry it unchanged and
196
+ the camera never leaves the machine.
197
+
198
+ Face perception is pluggable: supply any `FaceTracker` (for example a
199
+ self-hosted MediaPipe Face Landmarker emitting the 52 ARKit-style blendshape
200
+ coefficients). Rendering is pluggable the same way — the built-in stylized
201
+ renderer needs no assets, and a custom `AvatarRenderer` can draw a VRM
202
+ character or anything else from the same `AvatarRig`.
203
+
204
+ Two modes: `replace` (avatar on a backdrop) and `overlay` (avatar anchored to
205
+ the tracked head over the live camera). Client environments differ, so the
206
+ perception constants are an open `tuning` surface, adjustable live:
207
+
208
+ ```ts
209
+ const avatar = createAvatarPipeline(cameraTrack, {
210
+ tracker, // your FaceTracker
211
+ mode: "replace",
212
+ tuning: { mouthGain: 1.7, eyeSync: 1, smoothing: 0.4 }
213
+ });
214
+ await media.setVideoTrack(avatar.track);
215
+ avatar.setTuning({ mouthGain: 2 }); // cameras differ; correct per deployment
216
+ avatar.setMode("overlay");
217
+ ```
218
+
219
+ `AvatarTuning` covers `mouthGain`, `blinkGain`, `eyeSync` (evens the lids in
220
+ profile, where per-eye data drifts), `smoothing`, `avatarScale`, `avatarLift`
221
+ and `lostHoldMs` (losing the face holds the pose and eases the expression to
222
+ rest instead of snapping to neutral).
223
+
224
+ ## Call recording
225
+
226
+ `createCallRecorder(media.getNativeClient(), { credential })` composites every
227
+ participant's video onto a canvas, mixes all audio, records with
228
+ MediaRecorder, and on `stop()` uploads the file with the room signaling token.
229
+ The platform stores it, bills the storage, lists it under
230
+ `GET /v1/rtc/recordings` (Server API), and serves it from
231
+ `GET /v1/rtc/recordings/{id}/download`. Recording happens on this client —
232
+ what is recorded is what this participant saw and heard.
233
+
234
+ ```ts
235
+ const recorder = createCallRecorder(media.getNativeClient(), { credential });
236
+ await recorder.start();
237
+ // ... later
238
+ const { recordingId, durationSeconds } = await recorder.stop();
239
+ ```
240
+
172
241
  ## Active speaker
173
242
 
174
243
  The client samples WebRTC audio levels once a second on both transports and
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Face blendshapes → avatar rig.
3
+ *
4
+ * MediaPipe's Face Landmarker emits 52 ARKit-style blendshape coefficients
5
+ * and a facial transformation matrix. This module reduces them to the small
6
+ * rig a stylized avatar needs, as pure functions — the experimental Avatar
7
+ * Lab draws from this rig, and a future VRM renderer can consume the same
8
+ * values. Keeping it pure keeps it testable without a camera.
9
+ */
10
+ export interface BlendshapeCategory {
11
+ categoryName: string;
12
+ score: number;
13
+ }
14
+ export interface AvatarRig {
15
+ /** 0 open .. 1 closed */
16
+ blinkLeft: number;
17
+ blinkRight: number;
18
+ /** 0 shut .. 1 wide open */
19
+ jawOpen: number;
20
+ /** -1 frown .. 1 smile */
21
+ smile: number;
22
+ /** 0 rest .. 1 raised */
23
+ browUp: number;
24
+ /** -1 left .. 1 right (viewer's perspective) */
25
+ pupilX: number;
26
+ /** -1 up .. 1 down */
27
+ pupilY: number;
28
+ /** radians */
29
+ headYaw: number;
30
+ headPitch: number;
31
+ headRoll: number;
32
+ /** whether a face was tracked at all */
33
+ tracked: boolean;
34
+ }
35
+ export declare function neutralRig(): AvatarRig;
36
+ /**
37
+ * The 4x4 facial transformation matrix is column-major. Yaw/pitch/roll are
38
+ * extracted for a Y-up, viewer-facing convention; angles are what an avatar
39
+ * head joint wants directly.
40
+ */
41
+ export declare function headPoseFromMatrix(matrix: ArrayLike<number> | null | undefined): {
42
+ yaw: number;
43
+ pitch: number;
44
+ roll: number;
45
+ };
46
+ export declare function rigFromBlendshapes(categories: BlendshapeCategory[] | null | undefined, matrix?: ArrayLike<number> | null, eyeSyncStrength?: number): AvatarRig;
47
+ /**
48
+ * Exponential smoothing between detector ticks so the avatar renders
49
+ * smoothly even when detection runs slower than the paint loop.
50
+ */
51
+ export declare function smoothRig(previous: AvatarRig, next: AvatarRig, alpha?: number): AvatarRig;
52
+ /** Where to draw the head when compositing over a real frame. */
53
+ export interface AvatarAnchor {
54
+ x: number;
55
+ y: number;
56
+ radius: number;
57
+ }
58
+ /**
59
+ * Draws the built-in stylized avatar. Pure canvas drawing, no assets. With an
60
+ * anchor the head is drawn over whatever is already on the canvas — the
61
+ * keep-real-background mode — otherwise it paints its own backdrop.
62
+ */
63
+ export declare function drawAvatar(ctx: CanvasRenderingContext2D, rig: AvatarRig, width: number, height: number, anchor?: AvatarAnchor): void;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Virtual avatar pipeline.
3
+ *
4
+ * Same shape as the video-effects pipeline: the camera is analyzed on-device,
5
+ * an avatar canvas is captured as a MediaStreamTrack, and that track feeds the
6
+ * existing `setVideoTrack` — every transport (mesh or SFU) publishes the
7
+ * avatar without knowing it exists, and the network sees an ordinary video
8
+ * track.
9
+ *
10
+ * Face perception is an application asset, not an SDK constant: the pipeline
11
+ * takes any `FaceTracker` (the platform meeting room supplies a self-hosted
12
+ * MediaPipe Face Landmarker). Without a tracker the avatar idles untracked
13
+ * rather than pretending. Rendering is pluggable the same way: the built-in
14
+ * stylized renderer needs no assets, and a custom `AvatarRenderer` can draw
15
+ * anything driven by the same rig (the platform's VRM character renderer is
16
+ * exactly that).
17
+ *
18
+ * Client environments differ — cameras, lighting, faces — so every perception
19
+ * constant that needed hand-tuning during development is exposed in
20
+ * `AvatarTuning` and adjustable live via `setTuning`.
21
+ */
22
+ import { type AvatarAnchor, type AvatarRig, type BlendshapeCategory } from "./avatar-rig";
23
+ export interface FaceTrackerResult {
24
+ /** MediaPipe-style blendshape categories; null/empty means no face. */
25
+ categories: BlendshapeCategory[] | null;
26
+ /** Column-major 4x4 facial transformation matrix, when available. */
27
+ matrix?: ArrayLike<number> | null;
28
+ /** Normalized (0..1) face landmarks, used to anchor overlay mode. */
29
+ landmarks?: Array<{
30
+ x: number;
31
+ y: number;
32
+ }> | null;
33
+ }
34
+ export interface FaceTracker {
35
+ detect(frame: CanvasImageSource, timestampMs: number): FaceTrackerResult | null;
36
+ close?(): void;
37
+ }
38
+ export interface AvatarRenderer {
39
+ /** Draws the rig. `anchor` is set in overlay mode, null in replace mode. */
40
+ render(ctx: CanvasRenderingContext2D, rig: AvatarRig, width: number, height: number, anchor: AvatarAnchor | null): void;
41
+ dispose?(): void;
42
+ }
43
+ /** `replace`: avatar on a backdrop. `overlay`: avatar over the live camera, anchored to the tracked head. */
44
+ export type AvatarMode = "replace" | "overlay";
45
+ export interface AvatarTuning {
46
+ /** jawOpen multiplier — detectors top out well below 1 for a wide-open mouth. */
47
+ mouthGain: number;
48
+ /** Blink coefficient multiplier. */
49
+ blinkGain: number;
50
+ /** 0 off .. 1 full evening of the lids in profile (per-eye data drifts at high yaw). */
51
+ eyeSync: number;
52
+ /** Detector→rig easing per frame; lower is smoother but laggier. */
53
+ smoothing: number;
54
+ /** Avatar size relative to the tracked head (overlay mode). */
55
+ avatarScale: number;
56
+ /** Vertical offset in head radii, positive is down (overlay mode). */
57
+ avatarLift: number;
58
+ /** After this many ms without a face, the expression eases to rest (pose is held). */
59
+ lostHoldMs: number;
60
+ }
61
+ export declare const AVATAR_TUNING_DEFAULTS: AvatarTuning;
62
+ export interface AvatarPipelineOptions {
63
+ width?: number;
64
+ height?: number;
65
+ frameRate?: number;
66
+ tracker?: FaceTracker | null;
67
+ renderer?: AvatarRenderer | null;
68
+ mode?: AvatarMode;
69
+ tuning?: Partial<AvatarTuning>;
70
+ /** Backdrop color for replace mode. */
71
+ backdrop?: string;
72
+ document?: Document;
73
+ }
74
+ export interface AvatarPipeline {
75
+ readonly track: MediaStreamTrack;
76
+ readonly stream: MediaStream;
77
+ /** The current smoothed rig — read-only, useful for meters and debugging. */
78
+ getRig(): AvatarRig;
79
+ setMode(mode: AvatarMode): void;
80
+ setTracker(tracker: FaceTracker | null): void;
81
+ setRenderer(renderer: AvatarRenderer | null): void;
82
+ setTuning(partial: Partial<AvatarTuning>): void;
83
+ getTuning(): AvatarTuning;
84
+ dispose(): void;
85
+ }
86
+ /** The built-in stylized renderer: pure canvas, no assets, no licensing. */
87
+ export declare function stylizedAvatarRenderer(): AvatarRenderer;
88
+ export declare function createAvatarPipeline(source: MediaStreamTrack | MediaStream, options?: AvatarPipelineOptions): AvatarPipeline;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Client-side call recording.
3
+ *
4
+ * The mesh has no server with access to the media, so the first recorder that
5
+ * can exist lives where the media already is: this client. It composites every
6
+ * video tile onto a canvas, mixes every audio track, records the result with
7
+ * MediaRecorder, and uploads the finished file with the room signaling token.
8
+ * The platform bills and purges the stored file through the same
9
+ * `rtc_recordings` rows a future server-side recorder will use.
10
+ */
11
+ export interface RecordableRoomClient {
12
+ getRemoteStreams(): Array<{
13
+ participant: {
14
+ participantId: string;
15
+ displayName: string;
16
+ };
17
+ stream: MediaStream;
18
+ }>;
19
+ getLocalStream(): MediaStream | null;
20
+ }
21
+ export interface CallRecorderOptions {
22
+ credential: {
23
+ signalingToken: string;
24
+ signalingUrl: string;
25
+ };
26
+ /** Canvas output height; width follows 16:9. Default 720. */
27
+ height?: number;
28
+ mimeType?: string;
29
+ /** Test seams. */
30
+ document?: Document;
31
+ mediaRecorderFactory?: (stream: MediaStream, options: {
32
+ mimeType: string;
33
+ }) => MediaRecorder;
34
+ fetchImplementation?: typeof fetch;
35
+ }
36
+ export interface CallRecorderResult {
37
+ recordingId: string;
38
+ sizeBytes: number;
39
+ durationSeconds: number;
40
+ }
41
+ export interface CallRecorder {
42
+ readonly recording: boolean;
43
+ start(): Promise<void>;
44
+ /** Stops, uploads, and marks the recording ready. */
45
+ stop(): Promise<CallRecorderResult>;
46
+ dispose(): void;
47
+ }
48
+ export declare function createCallRecorder(client: RecordableRoomClient, options: CallRecorderOptions): CallRecorder;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,14 @@ export { createPictureInPictureController } from "./picture-in-picture";
2
2
  export type { PictureInPictureController, PictureInPictureOptions } from "./picture-in-picture";
3
3
  export { pickActiveSpeaker, SPEAKER_NOISE_FLOOR, SPEAKER_TAKEOVER_RATIO } from "./active-speaker";
4
4
  export type { SpeakerLevel } from "./active-speaker";
5
+ export { createCallRecorder } from "./call-recorder";
6
+ export type { CallRecorder, CallRecorderOptions, CallRecorderResult } from "./call-recorder";
7
+ export { BACKGROUND_PRESETS, createVideoEffectsPipeline, OVERLAY_EFFECTS, scenePainters } from "./video-effects";
8
+ export type { BackgroundPreset, OverlayEffect, Segmenter, VideoEffectsOptions, VideoEffectsPipeline } from "./video-effects";
9
+ export { AVATAR_TUNING_DEFAULTS, createAvatarPipeline, stylizedAvatarRenderer } from "./avatar";
10
+ export type { AvatarMode, AvatarPipeline, AvatarPipelineOptions, AvatarRenderer, AvatarTuning, FaceTracker, FaceTrackerResult } from "./avatar";
11
+ export { drawAvatar, headPoseFromMatrix, neutralRig, rigFromBlendshapes, smoothRig } from "./avatar-rig";
12
+ export type { AvatarAnchor, AvatarRig, BlendshapeCategory } from "./avatar-rig";
5
13
  export type RtcMode = "voice" | "video" | "meeting";
6
14
  /**
7
15
  * How media travels. `mesh` (the default) opens one connection per pair and
@@ -338,4 +346,4 @@ export declare class RtcIncomingClient extends EventTarget {
338
346
  private emit;
339
347
  }
340
348
  export declare function createRtcIncomingClient(options: RtcIncomingClientOptions): RtcIncomingClient;
341
- export declare const version = "1.3.14";
349
+ export declare const version = "1.3.16";