@streaming-cdn/rtc-web 1.3.24 → 1.3.25
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 +11 -1
- package/dist/avatar-library.d.ts +82 -0
- package/dist/avatar-vrm.d.ts +30 -0
- package/dist/index.d.ts +3 -1
- package/dist/peer-quality.d.ts +14 -2
- package/dist/rtc-web-avatar-vrm.es.js +61 -0
- package/dist/rtc-web-avatar-vrm.umd.js +1 -0
- package/dist/rtc-web.es.js +541 -312
- package/dist/rtc-web.umd.js +1 -1
- package/dist/zip-reader.d.ts +31 -0
- package/package.json +19 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# RTC Web SDK 1.3.
|
|
1
|
+
# RTC Web SDK 1.3.25
|
|
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
|
|
|
@@ -357,6 +357,16 @@ operational diagnostics. Tokens, SDP, media, and chat content are never part of
|
|
|
357
357
|
that report. Set `reportSignalingDiagnostics: false` only when replacing this
|
|
358
358
|
with an application-owned diagnostics pipeline.
|
|
359
359
|
|
|
360
|
+
## 1.3.25 notes
|
|
361
|
+
|
|
362
|
+
- **B21 — per-peer quality no longer ratchets down on bandwidth-pinned
|
|
363
|
+
links.** The step-up gate required `qualityLimitationReason` to clear; on
|
|
364
|
+
links that pin it at `bandwidth`, upgrades were mathematically impossible.
|
|
365
|
+
Both directions now share `bandwidthSustainRatio` (default 0.7): down
|
|
366
|
+
judges the current tier's target, up judges the next tier's, so promotions
|
|
367
|
+
cannot flap. `bandwidthHeadroomRatio` is retired and ignored, and
|
|
368
|
+
`goodSamplesToStepUp` is effective again in this state.
|
|
369
|
+
|
|
360
370
|
## 1.3.24 notes
|
|
361
371
|
|
|
362
372
|
- `callupdated` now carries `actorExternalId`, matching the React Native SDK.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type ZipEntry } from "./zip-reader";
|
|
2
|
+
/**
|
|
3
|
+
* Custom avatar model library.
|
|
4
|
+
*
|
|
5
|
+
* A browser cannot look inside the user's Downloads folder — no web API
|
|
6
|
+
* enumerates the file system. So a model obtained from a third-party site is
|
|
7
|
+
* handed over once, explicitly (file picker or drag-and-drop), and kept here so
|
|
8
|
+
* every later call finds it already imported.
|
|
9
|
+
*
|
|
10
|
+
* Models never leave the device: the avatar is rendered locally into the
|
|
11
|
+
* outgoing video track, so remote participants receive video, never the model
|
|
12
|
+
* file. Nothing here uploads, and nothing here depends on three.js — reading a
|
|
13
|
+
* VRM's own metadata only needs its glTF JSON chunk.
|
|
14
|
+
*/
|
|
15
|
+
export interface AvatarModelLicense {
|
|
16
|
+
/** Character name declared by the model, when present. */
|
|
17
|
+
title: string | null;
|
|
18
|
+
author: string | null;
|
|
19
|
+
/** Who the model may be used by, verbatim from the file. */
|
|
20
|
+
allowedUser: string | null;
|
|
21
|
+
/** Whether the author permits commercial use, verbatim from the file. */
|
|
22
|
+
commercialUse: string | null;
|
|
23
|
+
licenseName: string | null;
|
|
24
|
+
licenseUrl: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface AvatarModelEntry {
|
|
27
|
+
id: string;
|
|
28
|
+
/** Display name: the model's own title when it declares one, else the file name. */
|
|
29
|
+
name: string;
|
|
30
|
+
bytes: number;
|
|
31
|
+
importedAt: string;
|
|
32
|
+
/** The author's terms as declared inside the file; null when it declares none. */
|
|
33
|
+
license: AvatarModelLicense | null;
|
|
34
|
+
}
|
|
35
|
+
export interface AvatarModelImportOptions {
|
|
36
|
+
/** Overrides the name taken from the model metadata or file name. */
|
|
37
|
+
name?: string;
|
|
38
|
+
/** Rejects models above this size. Defaults to AVATAR_MODEL_MAX_BYTES. */
|
|
39
|
+
maxBytes?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Which character to take when the source is an archive holding several.
|
|
42
|
+
* Comes from AvatarModelError.choices on a previous "archive_many_models".
|
|
43
|
+
*/
|
|
44
|
+
entryPath?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Lists the characters inside a downloaded pack, so a caller can offer a
|
|
48
|
+
* choice before importing. Returns an empty list for a bare .vrm.
|
|
49
|
+
*/
|
|
50
|
+
export declare function listArchiveModels(source: File | ArrayBuffer): Promise<string[]>;
|
|
51
|
+
/** Well past any sensible VRM character, far below anything that would wedge a tab. */
|
|
52
|
+
export declare const AVATAR_MODEL_MAX_BYTES: number;
|
|
53
|
+
export type AvatarModelErrorCode = "model_too_large" | "model_not_glb" | "model_not_vrm" | "model_unreadable" | "storage_unavailable" | "archive_no_model" | "archive_many_models" | "archive_unreadable" | "archive_encrypted" | "archive_unsupported";
|
|
54
|
+
export declare class AvatarModelError extends Error {
|
|
55
|
+
readonly code: AvatarModelErrorCode;
|
|
56
|
+
/**
|
|
57
|
+
* The characters found inside an archive. Set only for
|
|
58
|
+
* "archive_many_models", so the caller can ask which one to import and pass
|
|
59
|
+
* it back as importAvatarModel's entryPath.
|
|
60
|
+
*/
|
|
61
|
+
readonly choices: string[];
|
|
62
|
+
constructor(code: AvatarModelErrorCode, message: string, choices?: string[]);
|
|
63
|
+
}
|
|
64
|
+
/** Entries a character pack holds that are actually characters. */
|
|
65
|
+
export declare function findArchiveModels(entries: ZipEntry[]): ZipEntry[];
|
|
66
|
+
/**
|
|
67
|
+
* Reads the VRM metadata a model declares about itself. Supports VRM 0.x
|
|
68
|
+
* (extensions.VRM.meta) and VRM 1.0 (extensions.VRMC_vrm.meta), whose field
|
|
69
|
+
* names differ. Returns null for a glTF that carries neither.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readAvatarModelMetadata(buffer: ArrayBuffer): AvatarModelLicense | null;
|
|
72
|
+
/**
|
|
73
|
+
* Validates a model the user supplied and keeps it for later calls. The bytes
|
|
74
|
+
* are checked before anything renders them: an oversized or non-VRM file is
|
|
75
|
+
* rejected with a code the UI can explain, never handed to the renderer.
|
|
76
|
+
*/
|
|
77
|
+
export declare function importAvatarModel(source: File | ArrayBuffer, options?: AvatarModelImportOptions): Promise<AvatarModelEntry>;
|
|
78
|
+
/** Every model this device has imported, newest first. */
|
|
79
|
+
export declare function listAvatarModels(): Promise<AvatarModelEntry[]>;
|
|
80
|
+
/** The stored bytes for a model, or null when it is no longer present. */
|
|
81
|
+
export declare function readAvatarModel(id: string): Promise<ArrayBuffer | null>;
|
|
82
|
+
export declare function removeAvatarModel(id: string): Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AvatarRig } from "./avatar-rig";
|
|
2
|
+
import type { AvatarRenderer } from "./avatar";
|
|
3
|
+
export interface VrmRenderOptions {
|
|
4
|
+
/** Multiplier on head joint rotation; 1 mirrors the person exactly. */
|
|
5
|
+
headGain?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface VrmStage {
|
|
8
|
+
canvas: HTMLCanvasElement;
|
|
9
|
+
applyAndRender(rig: AvatarRig, options?: VrmRenderOptions): void;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Adapts a VrmStage to the SDK's pluggable `AvatarRenderer`, so
|
|
14
|
+
* `createAvatarPipeline` can publish a VRM character instead of the built-in
|
|
15
|
+
* stylized avatar. In overlay mode the bust is framed around the head, so it
|
|
16
|
+
* spans a few head radii over the tracked anchor.
|
|
17
|
+
*/
|
|
18
|
+
export declare function vrmAvatarRenderer(stage: VrmStage, options?: {
|
|
19
|
+
headGain?: number;
|
|
20
|
+
}): AvatarRenderer;
|
|
21
|
+
export declare function createVrmStage(buffer: ArrayBuffer, size?: number): Promise<VrmStage>;
|
|
22
|
+
/**
|
|
23
|
+
* One-call helper: bytes in, pipeline renderer out. Pair it with
|
|
24
|
+
* readAvatarModel() from the model library to render a character the user
|
|
25
|
+
* imported from a third-party site.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createVrmAvatarRenderer(buffer: ArrayBuffer, options?: {
|
|
28
|
+
size?: number;
|
|
29
|
+
headGain?: number;
|
|
30
|
+
}): Promise<AvatarRenderer>;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type { BackgroundPreset, OverlayEffect, Segmenter, VideoEffectsOptions, V
|
|
|
9
9
|
export { AVATAR_TUNING_DEFAULTS, createAvatarPipeline, stylizedAvatarRenderer } from "./avatar";
|
|
10
10
|
export type { AvatarMode, AvatarPipeline, AvatarPipelineOptions, AvatarRenderer, AvatarTuning, FaceTracker, FaceTrackerResult } from "./avatar";
|
|
11
11
|
export { drawAvatar, headPoseFromMatrix, neutralRig, rigFromBlendshapes, smoothRig } from "./avatar-rig";
|
|
12
|
+
export { AVATAR_MODEL_MAX_BYTES, AvatarModelError, importAvatarModel, listArchiveModels, listAvatarModels, readAvatarModel, readAvatarModelMetadata, removeAvatarModel } from "./avatar-library";
|
|
13
|
+
export type { AvatarModelEntry, AvatarModelErrorCode, AvatarModelImportOptions, AvatarModelLicense } from "./avatar-library";
|
|
12
14
|
export type { AvatarAnchor, AvatarRig, BlendshapeCategory } from "./avatar-rig";
|
|
13
15
|
export type RtcMode = "voice" | "video" | "meeting";
|
|
14
16
|
/**
|
|
@@ -393,4 +395,4 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
393
395
|
private emit;
|
|
394
396
|
}
|
|
395
397
|
export declare function createRtcIncomingClient(options: RtcIncomingClientOptions): RtcIncomingClient;
|
|
396
|
-
export declare const version = "1.3.
|
|
398
|
+
export declare const version = "1.3.25";
|
package/dist/peer-quality.d.ts
CHANGED
|
@@ -37,8 +37,20 @@ export interface PeerQualityThresholds {
|
|
|
37
37
|
goodLossPct: number;
|
|
38
38
|
goodRttMs: number;
|
|
39
39
|
sustainRatio: number;
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* One ratio governs both directions of the bandwidth-limited path (B21).
|
|
42
|
+
* Down: "bandwidth" counts as congestion only below this fraction of the
|
|
43
|
+
* CURRENT tier's target. Up: a bandwidth-limited but otherwise clean sample
|
|
44
|
+
* counts as good once the uplink covers this fraction of the NEXT tier's
|
|
45
|
+
* target — a capped encoder never lets estimation probe much past its send
|
|
46
|
+
* rate, so demanding the full next-tier target keeps the gate shut exactly
|
|
47
|
+
* when the cap is what throttles discovery. The default (0.7) exceeds every
|
|
48
|
+
* adjacent-tier cap ratio, so a promotion never yields fewer usable bits
|
|
49
|
+
* than the tier being left was allowed at full cap.
|
|
50
|
+
*/
|
|
51
|
+
bandwidthSustainRatio: number;
|
|
52
|
+
/** @deprecated No longer used; superseded by bandwidthSustainRatio (B21). */
|
|
53
|
+
bandwidthHeadroomRatio?: number;
|
|
42
54
|
}
|
|
43
55
|
export declare const defaultPeerQualityThresholds: PeerQualityThresholds;
|
|
44
56
|
export interface PeerQualitySample {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as l from "three";
|
|
2
|
+
import { GLTFLoader as L } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
|
+
import { VRMLoaderPlugin as A, VRMUtils as k } from "@pixiv/three-vrm";
|
|
4
|
+
function M(t, c) {
|
|
5
|
+
return {
|
|
6
|
+
render(n, p, s, m, o) {
|
|
7
|
+
if (t.applyAndRender(p, c), o) {
|
|
8
|
+
const a = o.radius * 5.2;
|
|
9
|
+
n.drawImage(t.canvas, o.x - a / 2, o.y - a * 0.42, a, a);
|
|
10
|
+
} else {
|
|
11
|
+
const a = Math.min(s, m);
|
|
12
|
+
n.drawImage(t.canvas, (s - a) / 2, (m - a) / 2, a, a);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
dispose() {
|
|
16
|
+
t.dispose();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async function g(t, c = 512) {
|
|
21
|
+
const n = document.createElement("canvas");
|
|
22
|
+
n.width = c, n.height = c;
|
|
23
|
+
const p = new l.WebGLRenderer({ canvas: n, alpha: !0, antialias: !0 });
|
|
24
|
+
p.setClearColor(0, 0);
|
|
25
|
+
const s = new l.Scene();
|
|
26
|
+
s.add(new l.AmbientLight(16777215, 1.1));
|
|
27
|
+
const m = new l.DirectionalLight(16777215, 1.4);
|
|
28
|
+
m.position.set(0.5, 1.2, 1.5), s.add(m);
|
|
29
|
+
const o = new l.PerspectiveCamera(28, 1, 0.1, 20), a = new L();
|
|
30
|
+
a.register((e) => new A(e));
|
|
31
|
+
const w = await a.parseAsync(t, ""), i = w.userData.vrm;
|
|
32
|
+
k.removeUnnecessaryVertices(w.scene), k.combineSkeletons(w.scene), k.rotateVRM0(i), s.add(i.scene);
|
|
33
|
+
const u = i.humanoid?.getNormalizedBoneNode("head"), h = new l.Vector3(0, 1.35, 0);
|
|
34
|
+
u && u.getWorldPosition(h), o.position.set(0, h.y + 0.03, 0.85), o.lookAt(0, h.y + 0.01, 0);
|
|
35
|
+
const f = new l.Object3D();
|
|
36
|
+
f.position.set(0, h.y, 2), s.add(f), i.lookAt && (i.lookAt.target = f);
|
|
37
|
+
const b = new l.Clock();
|
|
38
|
+
return {
|
|
39
|
+
canvas: n,
|
|
40
|
+
applyAndRender(e, R) {
|
|
41
|
+
const y = R?.headGain ?? 0.9, r = i.expressionManager;
|
|
42
|
+
if (r) {
|
|
43
|
+
const V = r.expressionMap ?? {}, d = (v) => !!V[v];
|
|
44
|
+
d("blinkLeft") || d("blinkRight") ? (r.setValue("blinkLeft", e.blinkLeft), r.setValue("blinkRight", e.blinkRight)) : d("blink") && r.setValue("blink", (e.blinkLeft + e.blinkRight) / 2), d("aa") && r.setValue("aa", Math.max(0, e.jawOpen)), d("happy") && r.setValue("happy", Math.max(0, e.smile)), d("angry") && r.setValue("angry", Math.max(0, -e.smile) * 0.6);
|
|
45
|
+
}
|
|
46
|
+
u && u.rotation.set(-e.headPitch * y, e.headYaw * y, -e.headRoll * y), f.position.set(e.pupilX * 1.2, h.y - e.pupilY * 0.8, 2), i.update(b.getDelta()), p.render(s, o);
|
|
47
|
+
},
|
|
48
|
+
dispose() {
|
|
49
|
+
k.deepDispose(i.scene), p.dispose();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function D(t, c = {}) {
|
|
54
|
+
const n = await g(t, c.size ?? 640);
|
|
55
|
+
return M(n, { headGain: c.headGain });
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
D as createVrmAvatarRenderer,
|
|
59
|
+
g as createVrmStage,
|
|
60
|
+
M as vrmAvatarRenderer
|
|
61
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(s,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("three"),require("three/examples/jsm/loaders/GLTFLoader.js"),require("@pixiv/three-vrm")):typeof define=="function"&&define.amd?define(["exports","three","three/examples/jsm/loaders/GLTFLoader.js","@pixiv/three-vrm"],f):(s=typeof globalThis<"u"?globalThis:s||self,f(s.StreamingCdnRtcAvatarVrm={},s.THREE,s.THREE,s.THREE_VRM))})(this,(function(s,f,M,m){"use strict";function L(e){const i=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(i,t,r.get?r:{enumerable:!0,get:()=>e[t]})}}return i.default=e,Object.freeze(i)}const o=L(f);function w(e,i){return{render(t,r,d,h,c){if(e.applyAndRender(r,i),c){const a=c.radius*5.2;t.drawImage(e.canvas,c.x-a/2,c.y-a*.42,a,a)}else{const a=Math.min(d,h);t.drawImage(e.canvas,(d-a)/2,(h-a)/2,a,a)}},dispose(){e.dispose()}}}async function V(e,i=512){const t=document.createElement("canvas");t.width=i,t.height=i;const r=new o.WebGLRenderer({canvas:t,alpha:!0,antialias:!0});r.setClearColor(0,0);const d=new o.Scene;d.add(new o.AmbientLight(16777215,1.1));const h=new o.DirectionalLight(16777215,1.4);h.position.set(.5,1.2,1.5),d.add(h);const c=new o.PerspectiveCamera(28,1,.1,20),a=new M.GLTFLoader;a.register(n=>new m.VRMLoaderPlugin(n));const b=await a.parseAsync(e,""),l=b.userData.vrm;m.VRMUtils.removeUnnecessaryVertices(b.scene),m.VRMUtils.combineSkeletons(b.scene),m.VRMUtils.rotateVRM0(l),d.add(l.scene);const y=l.humanoid?.getNormalizedBoneNode("head"),R=new o.Vector3(0,1.35,0);y&&y.getWorldPosition(R),c.position.set(0,R.y+.03,.85),c.lookAt(0,R.y+.01,0);const v=new o.Object3D;v.position.set(0,R.y,2),d.add(v),l.lookAt&&(l.lookAt.target=v);const j=new o.Clock;return{canvas:t,applyAndRender(n,A){const k=A?.headGain??.9,p=l.expressionManager;if(p){const T=p.expressionMap??{},u=x=>!!T[x];u("blinkLeft")||u("blinkRight")?(p.setValue("blinkLeft",n.blinkLeft),p.setValue("blinkRight",n.blinkRight)):u("blink")&&p.setValue("blink",(n.blinkLeft+n.blinkRight)/2),u("aa")&&p.setValue("aa",Math.max(0,n.jawOpen)),u("happy")&&p.setValue("happy",Math.max(0,n.smile)),u("angry")&&p.setValue("angry",Math.max(0,-n.smile)*.6)}y&&y.rotation.set(-n.headPitch*k,n.headYaw*k,-n.headRoll*k),v.position.set(n.pupilX*1.2,R.y-n.pupilY*.8,2),l.update(j.getDelta()),r.render(d,c)},dispose(){m.VRMUtils.deepDispose(l.scene),r.dispose()}}}async function g(e,i={}){const t=await V(e,i.size??640);return w(t,{headGain:i.headGain})}s.createVrmAvatarRenderer=g,s.createVrmStage=V,s.vrmAvatarRenderer=w,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|