realtime-avatar 0.7.3 → 0.8.0
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/browser.d.ts +42 -1
- package/dist/browser.js +76 -4
- package/dist/{chunk-2V333KRZ.js → chunk-JNRRH2G7.js} +1 -1
- package/dist/{chunk-OMOEUVO5.js → chunk-TOXLWFQY.js} +1 -1
- package/dist/express.d.ts +2 -2
- package/dist/express.js +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/nextjs.d.ts +2 -2
- package/dist/nextjs.js +2 -2
- package/dist/playout-delay-DWOc8iCl.d.ts +65 -0
- package/dist/{proxy-client-BzblQMvg.d.ts → proxy-client-DDqYRz-_.d.ts} +10 -26
- package/dist/react-native.d.ts +3 -2
- package/dist/react-native.js +18 -10
- package/dist/react.d.ts +3 -2
- package/dist/react.js +39 -11
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tanstack-start.d.ts +2 -2
- package/dist/tanstack-start.js +2 -2
- package/dist/{types-ZfY6oxQY.d.ts → types-D_aIKv4P.d.ts} +16 -4
- package/dist/{types-NKCoqM8E.d.ts → types-QoMHpR1h.d.ts} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ Browser — these never can:
|
|
|
140
140
|
| --- | --- |
|
|
141
141
|
| `realtime-avatar/react` | `AvatarCall`, `useAvatarCall`, `useRealtimeSession`, `useSessionLifecycle` |
|
|
142
142
|
| `realtime-avatar/react-native` | The same surface for Expo / React Native |
|
|
143
|
-
| `realtime-avatar/browser` | `enableMicrophone`, `attachRemoteAudio` — no React |
|
|
143
|
+
| `realtime-avatar/browser` | `enableMicrophone`, `attachRemoteAudio`, `prepareAvatarRoom` — no React |
|
|
144
144
|
| `realtime-avatar/tools` | `attachAvatarTools` — the browser tool plane |
|
|
145
145
|
|
|
146
146
|
Every adapter takes the same two hooks: `authorize` gates the request, `session` decides the
|
|
@@ -169,6 +169,16 @@ start" is one sentence covering six causes with different fixes — and one of t
|
|
|
169
169
|
system denial, cannot be fixed from the address bar and needs the browser restarted.
|
|
170
170
|
`attachRemoteAudio` attaches into the DOM *before* `connect`, which is what stops a track
|
|
171
171
|
arriving mid-connect from being lost on a fast connection.
|
|
172
|
+
`prepareAvatarRoom(room)` — one call after `new Room()` — gives every track the room subscribes
|
|
173
|
+
the same 0.5s receiver cushion the React surface applies on its own: the avatar crosses the public
|
|
174
|
+
internet, a shallow buffer freezes on every lost packet, and a deeper one recovers it before
|
|
175
|
+
playout (measured at ~5% loss: ~11fps with multi-second freezes → a steady 25fps). It covers audio
|
|
176
|
+
and video alike so the pair stays lip-locked, and `attachRemoteAudio` calls it for you, so a page
|
|
177
|
+
that follows the pattern above never has to know the knob exists. `applyPlayoutDelay(track)` is the
|
|
178
|
+
per-track primitive underneath. Until these lived here the cushion was only reachable through
|
|
179
|
+
`/react`, which is why every plain-DOM page — this repo's demos included — froze that way; and in
|
|
180
|
+
React it was applied only by `AvatarVideoSurface`, so `RealtimeAvatarLiveKitRoom` now applies it
|
|
181
|
+
room-wide to whatever you render inside it (`playoutDelaySeconds={false}` opts out).
|
|
172
182
|
|
|
173
183
|
`attachAvatarTools` runs your functions in the page. Nothing is executed on the platform, and
|
|
174
184
|
a tool has **2.5 seconds** to answer before the call gives up on it and tells her it failed.
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, P as PlayoutDelayTarget, a as applyAvatarPlayoutDelay, b as applyPlayoutDelay } from './playout-delay-DWOc8iCl.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Turning the microphone on, and saying what went wrong when it does not.
|
|
3
5
|
*
|
|
@@ -118,6 +120,14 @@ interface AudioCapableRoom {
|
|
|
118
120
|
off(event: string, listener: (...args: never[]) => void): unknown;
|
|
119
121
|
}
|
|
120
122
|
interface AttachRemoteAudioOptions {
|
|
123
|
+
/**
|
|
124
|
+
* Receiver playout cushion applied to EVERY remote track the room subscribes (video
|
|
125
|
+
* included, so the pair stays lip-locked) via `prepareAvatarRoom`. Default 0.5s — the
|
|
126
|
+
* measured freeze-free depth. `false` leaves the room untouched, for a page that owns
|
|
127
|
+
* its own buffering. Not undone by `detach()`: a shallower buffer mid-call only brings
|
|
128
|
+
* the freezes back.
|
|
129
|
+
*/
|
|
130
|
+
playoutDelaySeconds?: number | false;
|
|
121
131
|
/**
|
|
122
132
|
* Where to park the audio elements. Defaults to `document.body`.
|
|
123
133
|
*
|
|
@@ -162,6 +172,37 @@ interface RemoteAudioAttachment {
|
|
|
162
172
|
*/
|
|
163
173
|
declare function attachRemoteAudio(room: AudioCapableRoom, options?: AttachRemoteAudioOptions): RemoteAudioAttachment;
|
|
164
174
|
|
|
175
|
+
/** The sliver of a livekit-client `Room` this needs. */
|
|
176
|
+
interface PreparableRoom {
|
|
177
|
+
on(event: string, listener: (...args: never[]) => void): unknown;
|
|
178
|
+
off(event: string, listener: (...args: never[]) => void): unknown;
|
|
179
|
+
/** Read once, so tracks subscribed BEFORE this call are covered too. Optional on purpose. */
|
|
180
|
+
remoteParticipants?: Iterable<[unknown, PreparableParticipant]> | {
|
|
181
|
+
values(): Iterable<PreparableParticipant>;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** The sliver of a livekit-client `RemoteParticipant` this needs. */
|
|
185
|
+
interface PreparableParticipant {
|
|
186
|
+
trackPublications?: Iterable<[unknown, {
|
|
187
|
+
track?: unknown;
|
|
188
|
+
}]> | {
|
|
189
|
+
values(): Iterable<{
|
|
190
|
+
track?: unknown;
|
|
191
|
+
}>;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
interface PrepareAvatarRoomOptions {
|
|
195
|
+
/** Receiver cushion in seconds for every remote track. Default 0.5 — see `./playout-delay.ts`. */
|
|
196
|
+
playoutDelaySeconds?: number;
|
|
197
|
+
}
|
|
198
|
+
interface PreparedAvatarRoom {
|
|
199
|
+
/** Stop applying the cushion to tracks subscribed from now on. Idempotent. */
|
|
200
|
+
detach(): void;
|
|
201
|
+
/** The delay currently applied to new tracks. */
|
|
202
|
+
readonly playoutDelaySeconds: number;
|
|
203
|
+
}
|
|
204
|
+
declare function prepareAvatarRoom(room: PreparableRoom, options?: PrepareAvatarRoomOptions): PreparedAvatarRoom;
|
|
205
|
+
|
|
165
206
|
/**
|
|
166
207
|
* Ping-pong (boomerang) playback for idle/ambient clips.
|
|
167
208
|
*
|
|
@@ -200,4 +241,4 @@ type BoomerangPlayback = {
|
|
|
200
241
|
*/
|
|
201
242
|
declare function attachBoomerangPlayback(video: HTMLVideoElement, options?: BoomerangPlaybackOptions): BoomerangPlayback;
|
|
202
243
|
|
|
203
|
-
export { type AttachRemoteAudioOptions, type AttachableTrack, type AudioCapableRoom, type BoomerangPlayback, type BoomerangPlaybackOptions, type EnableMicrophoneOptions, type MicrophoneCapableRoom, type MicrophoneFailureReason, type MicrophoneResult, type RemoteAudioAttachment, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone };
|
|
244
|
+
export { type AttachRemoteAudioOptions, type AttachableTrack, type AudioCapableRoom, type BoomerangPlayback, type BoomerangPlaybackOptions, type EnableMicrophoneOptions, type MicrophoneCapableRoom, type MicrophoneFailureReason, type MicrophoneResult, type PreparableParticipant, type PreparableRoom, type PrepareAvatarRoomOptions, type PreparedAvatarRoom, type RemoteAudioAttachment, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone, prepareAvatarRoom };
|
package/dist/browser.js
CHANGED
|
@@ -75,14 +75,86 @@ async function enableMicrophone(room, options = {}) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// ../browser/src/playout-delay.ts
|
|
79
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
80
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
81
|
+
const target = playoutDelayTarget(track);
|
|
82
|
+
if (!target) return false;
|
|
83
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
87
|
+
const delay = Math.max(0, delaySeconds);
|
|
88
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
89
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
90
|
+
}
|
|
91
|
+
function playoutDelayTarget(track) {
|
|
92
|
+
const candidate = track;
|
|
93
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ../browser/src/prepare-room.ts
|
|
97
|
+
var TRACK_SUBSCRIBED = "trackSubscribed";
|
|
98
|
+
var prepared = /* @__PURE__ */ new WeakMap();
|
|
99
|
+
function prepareAvatarRoom(room, options = {}) {
|
|
100
|
+
const requested = options.playoutDelaySeconds;
|
|
101
|
+
const existing = prepared.get(room);
|
|
102
|
+
if (existing) {
|
|
103
|
+
if (requested !== void 0 && requested !== existing.delay) {
|
|
104
|
+
existing.delay = requested;
|
|
105
|
+
applyToExisting(room, requested);
|
|
106
|
+
}
|
|
107
|
+
return existing.handle;
|
|
108
|
+
}
|
|
109
|
+
const state = {
|
|
110
|
+
delay: requested ?? DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS,
|
|
111
|
+
listener: ((track) => {
|
|
112
|
+
const current = prepared.get(room);
|
|
113
|
+
applyPlayoutDelay(track, current ? current.delay : DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS);
|
|
114
|
+
}),
|
|
115
|
+
handle: {
|
|
116
|
+
get playoutDelaySeconds() {
|
|
117
|
+
return prepared.get(room)?.delay ?? state.delay;
|
|
118
|
+
},
|
|
119
|
+
detach() {
|
|
120
|
+
const live = prepared.get(room);
|
|
121
|
+
if (!live) return;
|
|
122
|
+
room.off(TRACK_SUBSCRIBED, live.listener);
|
|
123
|
+
prepared.delete(room);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
prepared.set(room, state);
|
|
128
|
+
room.on(TRACK_SUBSCRIBED, state.listener);
|
|
129
|
+
applyToExisting(room, state.delay);
|
|
130
|
+
return state.handle;
|
|
131
|
+
}
|
|
132
|
+
function applyToExisting(room, delay) {
|
|
133
|
+
for (const participant of values(room.remoteParticipants)) {
|
|
134
|
+
for (const publication of values(participant?.trackPublications)) {
|
|
135
|
+
if (publication?.track) applyPlayoutDelay(publication.track, delay);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function values(source) {
|
|
140
|
+
if (!source) return [];
|
|
141
|
+
if (Symbol.iterator in source) {
|
|
142
|
+
return Array.from(source, ([, value]) => value);
|
|
143
|
+
}
|
|
144
|
+
return source.values();
|
|
145
|
+
}
|
|
146
|
+
|
|
78
147
|
// ../browser/src/remote-audio.ts
|
|
79
148
|
var AUDIO_PLAYBACK_CHANGED = "audioPlaybackChanged";
|
|
80
|
-
var
|
|
149
|
+
var TRACK_SUBSCRIBED2 = "trackSubscribed";
|
|
81
150
|
var AUDIO_KIND = "audio";
|
|
82
151
|
function attachRemoteAudio(room, options = {}) {
|
|
83
152
|
const container = options.container ?? document.body;
|
|
84
153
|
const elements = /* @__PURE__ */ new Set();
|
|
85
154
|
let detached = false;
|
|
155
|
+
if (options.playoutDelaySeconds !== false) {
|
|
156
|
+
prepareAvatarRoom(room, { playoutDelaySeconds: options.playoutDelaySeconds });
|
|
157
|
+
}
|
|
86
158
|
const publishPlaybackState = () => {
|
|
87
159
|
if (detached) return;
|
|
88
160
|
options.onPlaybackBlocked?.(room.canPlaybackAudio ? null : () => room.startAudio());
|
|
@@ -95,14 +167,14 @@ function attachRemoteAudio(room, options = {}) {
|
|
|
95
167
|
elements.add(element);
|
|
96
168
|
publishPlaybackState();
|
|
97
169
|
};
|
|
98
|
-
room.on(
|
|
170
|
+
room.on(TRACK_SUBSCRIBED2, onTrackSubscribed);
|
|
99
171
|
room.on(AUDIO_PLAYBACK_CHANGED, publishPlaybackState);
|
|
100
172
|
publishPlaybackState();
|
|
101
173
|
return {
|
|
102
174
|
detach() {
|
|
103
175
|
if (detached) return;
|
|
104
176
|
detached = true;
|
|
105
|
-
room.off(
|
|
177
|
+
room.off(TRACK_SUBSCRIBED2, onTrackSubscribed);
|
|
106
178
|
room.off(AUDIO_PLAYBACK_CHANGED, publishPlaybackState);
|
|
107
179
|
for (const element of elements) element.remove();
|
|
108
180
|
elements.clear();
|
|
@@ -169,4 +241,4 @@ function now() {
|
|
|
169
241
|
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
170
242
|
}
|
|
171
243
|
|
|
172
|
-
export { attachBoomerangPlayback, attachRemoteAudio, enableMicrophone };
|
|
244
|
+
export { DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, applyAvatarPlayoutDelay, applyPlayoutDelay, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone, prepareAvatarRoom };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-
|
|
1
|
+
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-JNRRH2G7.js';
|
|
2
2
|
|
|
3
3
|
// ../proxy/src/config.ts
|
|
4
4
|
var json = (body, status = 200) => new Response(JSON.stringify(body), {
|
package/dist/express.d.ts
CHANGED
package/dist/express.js
CHANGED
package/dist/hono.d.ts
CHANGED
package/dist/hono.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as ClipSyncResult, h as AssetKind, i as Asset, j as ListSessionsOptions, U as UsageSessionPage, k as UsageSession, l as CreditBalance, T as TranscriptPayload } from './types-
|
|
2
|
-
export { m as AvatarClip, n as CallConnection, o as CallQueued, p as ClipLibraryPlan, q as ClipSource, r as ContextMessage, s as EndCallReason, V as VideoPolicy, t as VideoState, u as isQueued } from './types-
|
|
1
|
+
import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as ClipSyncResult, h as AssetKind, i as Asset, j as ListSessionsOptions, U as UsageSessionPage, k as UsageSession, l as CreditBalance, T as TranscriptPayload } from './types-D_aIKv4P.js';
|
|
2
|
+
export { m as AvatarClip, n as CallConnection, o as CallQueued, p as ClipLibraryPlan, q as ClipSource, r as ContextMessage, s as EndCallReason, V as VideoPolicy, t as VideoState, u as isQueued } from './types-D_aIKv4P.js';
|
|
3
3
|
|
|
4
4
|
interface RealtimeAvatarOptions {
|
|
5
5
|
/** `tic_live_…` or `tic_test_…`. Server-side only — never ship this to a browser. */
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-JNRRH2G7.js';
|
package/dist/nextjs.d.ts
CHANGED
package/dist/nextjs.js
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receiver-side playout delay (SECONDS) for the avatar media tracks.
|
|
3
|
+
*
|
|
4
|
+
* The avatar is published from a GPU datacenter and reaches viewers over the
|
|
5
|
+
* public internet, where a few percent of packet loss is normal. A shallow
|
|
6
|
+
* receiver buffer stalls (freezes) on every loss while it waits for a
|
|
7
|
+
* retransmit; a deeper buffer recovers the lost packets BEFORE playout, so the
|
|
8
|
+
* viewer sees smooth 25fps instead of freezing. Measured at ~5% loss this took
|
|
9
|
+
* the stream from ~11fps with multi-second freezes to a steady 25fps with zero
|
|
10
|
+
* freezes.
|
|
11
|
+
*
|
|
12
|
+
* 0.5s is applied via the native `RemoteTrack.setPlayoutDelay`, which sets the
|
|
13
|
+
* receiver's `playoutDelayHint` — the same Chromium jitter-buffer knob the old
|
|
14
|
+
* hand-rolled `jitterBufferTarget` reached into (the spec renamed
|
|
15
|
+
* playoutDelayHint→jitterBufferTarget; both influence the same buffer depth),
|
|
16
|
+
* but typed and SDK-owned so there is no cast or feature-probe. The hint is NOT
|
|
17
|
+
* free at the open: the receiver holds the FIRST video frame toward the target
|
|
18
|
+
* (~250-450ms of measured TTFF), a deliberate cost paid once for the freeze-free
|
|
19
|
+
* steady state above. The open-small-then-ramp variant that tried to dodge that
|
|
20
|
+
* cost was shipped, failed twice (the rVFC ramp trigger is throttled on the
|
|
21
|
+
* opacity:0-mounted element, stranding the shallow buffer), and was deliberately
|
|
22
|
+
* removed in 2262e4c7b — do not re-propose it. No-op on browsers that don't
|
|
23
|
+
* support the hint (the SDK warns and moves on).
|
|
24
|
+
*
|
|
25
|
+
* This lives in the vanilla browser package, not the React one, because the
|
|
26
|
+
* React surface applies it for you and a plain-DOM page has to be given it.
|
|
27
|
+
* Until this file existed the helper was only reachable through
|
|
28
|
+
* `realtime-avatar/react`, so every plain-DOM adopter — every one of this repo's
|
|
29
|
+
* own demos included — ran with the browser's default shallow buffer and froze on
|
|
30
|
+
* every lost packet. Nobody should have to know this knob exists: `prepareAvatarRoom`
|
|
31
|
+
* (and `attachRemoteAudio`, which calls it) applies it to every track the room
|
|
32
|
+
* subscribes, and `RealtimeAvatarLiveKitRoom` does the same for React. This file is
|
|
33
|
+
* the one place the number and its reasoning live.
|
|
34
|
+
* Structural like its siblings: no `livekit-client` import, no version pin.
|
|
35
|
+
*/
|
|
36
|
+
declare const DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
37
|
+
/**
|
|
38
|
+
* A track that can take a native playout-delay hint — livekit-client's subscribed
|
|
39
|
+
* `RemoteTrack`. Duck-typed on the method rather than `instanceof`, so a
|
|
40
|
+
* placeholder or local track is skipped and the helper is testable with a fake.
|
|
41
|
+
*/
|
|
42
|
+
interface PlayoutDelayTarget {
|
|
43
|
+
setPlayoutDelay(delayInSeconds: number): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Apply the receiver playout delay to ONE subscribed track.
|
|
47
|
+
*
|
|
48
|
+
* Call it for every track `RoomEvent.TrackSubscribed` hands you, audio and video
|
|
49
|
+
* alike, before or after `attach()` — WebRTC syncs the pair to the LARGER hint,
|
|
50
|
+
* so applying it to one stream and not the other lets the lips lag the voice.
|
|
51
|
+
* Returns `true` when the hint was applied and `false` when the object cannot take
|
|
52
|
+
* one (a local track, a placeholder, a not-yet-subscribed publication), so a
|
|
53
|
+
* caller can log the miss without a try/catch. The delay is floored at 0 so a
|
|
54
|
+
* negative input can never throw.
|
|
55
|
+
*/
|
|
56
|
+
declare function applyPlayoutDelay(track: unknown, delaySeconds?: number): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Apply the SAME native playout delay to the avatar's audio + video tracks
|
|
59
|
+
* (kept equal so the a/v sync stays lip-locked). Pure and exported so the clamp
|
|
60
|
+
* and the both-tracks-equal invariant are unit-testable without a DOM; the React
|
|
61
|
+
* hook `useAvatarPlayoutDelay` is a thin effect wrapper around this.
|
|
62
|
+
*/
|
|
63
|
+
declare function applyAvatarPlayoutDelay(videoTrack: unknown, audioTrack: unknown, delaySeconds?: number): void;
|
|
64
|
+
|
|
65
|
+
export { DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS as D, type PlayoutDelayTarget as P, applyAvatarPlayoutDelay as a, applyPlayoutDelay as b };
|
|
@@ -740,31 +740,7 @@ declare function useCallTranscript(): CallTranscript;
|
|
|
740
740
|
* (needs `useLocalParticipant`), e.g. under {@link RealtimeAvatarLiveKitRoom}.
|
|
741
741
|
*/
|
|
742
742
|
declare function useReleaseMicLeaseOnTrackEnded(token: symbol): void;
|
|
743
|
-
|
|
744
|
-
* Receiver-side playout delay (SECONDS) for the avatar media tracks.
|
|
745
|
-
*
|
|
746
|
-
* The avatar is published from a GPU datacenter and reaches viewers over the
|
|
747
|
-
* public internet, where a few percent of packet loss is normal. A shallow
|
|
748
|
-
* receiver buffer stalls (freezes) on every loss while it waits for a
|
|
749
|
-
* retransmit; a deeper buffer recovers the lost packets BEFORE playout, so the
|
|
750
|
-
* viewer sees smooth 25fps instead of freezing. Measured at ~5% loss this took
|
|
751
|
-
* the stream from ~11fps with multi-second freezes to a steady 25fps with zero
|
|
752
|
-
* freezes.
|
|
753
|
-
*
|
|
754
|
-
* 0.5s is applied via the native {@link RemoteTrack.setPlayoutDelay}, which sets
|
|
755
|
-
* the receiver's `playoutDelayHint` — the same Chromium jitter-buffer knob the
|
|
756
|
-
* old hand-rolled `jitterBufferTarget` reached into (the spec renamed
|
|
757
|
-
* playoutDelayHint→jitterBufferTarget; both influence the same buffer depth),
|
|
758
|
-
* but typed and SDK-owned so there is no cast or feature-probe. The hint is NOT
|
|
759
|
-
* free at the open: the receiver holds the FIRST video frame toward the target
|
|
760
|
-
* (~250-450ms of measured TTFF), a deliberate cost paid once for the freeze-free
|
|
761
|
-
* steady state above. The open-small-then-ramp variant that tried to dodge that
|
|
762
|
-
* cost was shipped, failed twice (the rVFC ramp trigger is throttled on the
|
|
763
|
-
* opacity:0-mounted element, stranding the shallow buffer), and was deliberately
|
|
764
|
-
* removed in 2262e4c7b — do not re-propose it. No-op on browsers that don't
|
|
765
|
-
* support the hint (the SDK warns and moves on).
|
|
766
|
-
*/
|
|
767
|
-
declare const DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
743
|
+
|
|
768
744
|
/**
|
|
769
745
|
* Sets the avatar AUDIO and VIDEO tracks' native playout delay so the stream has
|
|
770
746
|
* a real de-jitter / loss-recovery cushion for smooth, freeze-free playback.
|
|
@@ -861,6 +837,14 @@ type RealtimeAvatarLiveKitRoomProps = Omit<LiveKitRoomProps, "serverUrl" | "toke
|
|
|
861
837
|
/** Passed directly to LiveKitRoom. */
|
|
862
838
|
options?: LiveKitRoomProps["options"];
|
|
863
839
|
renderRoomAudio?: boolean;
|
|
840
|
+
/**
|
|
841
|
+
* Receiver playout cushion applied to EVERY remote track this room subscribes — so
|
|
842
|
+
* whatever you render inside (a bare `<VideoTrack>`, your own `<video>`, or
|
|
843
|
+
* {@link AvatarVideoSurface}) gets the freeze-free buffer, not only the SDK's own
|
|
844
|
+
* surface. Default {@link DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS}. `false` opts out
|
|
845
|
+
* for a consumer that owns its own buffering.
|
|
846
|
+
*/
|
|
847
|
+
playoutDelaySeconds?: number | false;
|
|
864
848
|
children?: ReactNode;
|
|
865
849
|
};
|
|
866
850
|
/** Thin typed bridge from a Realtime Avatar grant into LiveKitRoom. */
|
|
@@ -1633,4 +1617,4 @@ interface ProxyClientOptions {
|
|
|
1633
1617
|
}
|
|
1634
1618
|
declare function createProxyClient(options: ProxyClientOptions): AvatarSessionClient;
|
|
1635
1619
|
|
|
1636
|
-
export {
|
|
1620
|
+
export { RECONNECT_BACKOFF_MS as $, type AvatarVideoFit as A, type BehaviorSnapshot as B, type CallTranscript as C, DEFAULT_APPROACHING_END_LEAD_SECONDS as D, type EndReason as E, type FishTtsModel as F, type Governor as G, type GraceWindowClosedEvent as H, type GraceWindowOpenEvent as I, type GraceWindowState as J, type IdleWarningEvent as K, type LiveKitSessionGrant as L, type InboundRtpCursor as M, type InboundRtpReading as N, type KnownBehaviorState as O, type LLMProvider as P, type LLMSelection as Q, type LiveKitAvatarGrantState as R, type LiveKitAvatarGrantStatus as S, type LiveKitCapacityState as T, type LiveKitConnectionStatus as U, type LiveKitSessionRequest as V, type LiveKitSessionStartResult as W, MAX_RECONNECT_ATTEMPTS as X, MAX_SESSION_INSTRUCTIONS_CHARS as Y, type ProxyClientOptions as Z, type QualityCap as _, AdaptivePlayoutController as a, type RealtimeAvatarRequestOptions as a0, type RealtimeSessionApi as a1, type RealtimeSessionMedia as a2, type RealtimeSessionRoomSinks as a3, type ReconnectPolicy as a4, type ReconnectingEvent as a5, type RecoveryState as a6, type RetryStep as a7, type SessionBehavior as a8, type SessionClip as a9, useAvatarAdaptivePlayoutDelay as aA, useAvatarPlayoutDelay as aB, useAvatarQualityGovernor as aC, useCallTranscript as aD, useLiveKitAvatarGrant as aE, useLiveTrackProducing as aF, useMicLease as aG, useRealtimeSession as aH, useReleaseMicLeaseOnTrackEnded as aI, useSessionLifecycle as aJ, AvatarVideoSurface as aK, type AvatarVideoSurfaceProps as aL, type LivePlaybackKeeper as aM, MIC_LEASE_ENDED_TIMEOUT_MS as aN, type MicLease as aO, type PlayableVideoElement as aP, RealtimeAvatarCapacityError as aQ, RealtimeAvatarLiveKitRoom as aR, type RealtimeAvatarLiveKitRoomProps as aS, shouldReplayPendingTurn as aT, type SessionClocks as aa, type SessionEndReason as ab, type SessionLifecycleApi as ac, type SessionLifecyclePhase as ad, type SessionLifecyclePhaseKind as ae, SessionLifecycleRoomBridge as af, type SessionLifecycleRoomBridgeProps as ag, type SurfaceLayers as ah, type TurnState as ai, type TurnTimeoutEvent as aj, type UseAvatarQualityGovernorInput as ak, type UseLiveKitAvatarGrantInput as al, type UseRealtimeSessionInput as am, type UseSessionLifecycleInput as an, type VoiceSpec as ao, type VoiceSpecInput as ap, capacityErrorFromBusy as aq, capacityStateFromGrant as ar, createProxyClient as as, isNativeLiveTrackSubscribed as at, knownBehaviorStates as au, mapTurnState as av, readInboundRtp as aw, sessionBehaviorSchema as ax, sessionClipSchema as ay, splitCallTranscript as az, type AdaptivePlayoutDecision as b, type AdaptivePlayoutOptions as c, type AdaptivePlayoutSample as d, type ApproachingEndEvent as e, type ApproachingEndReason as f, type AvatarSessionClient as g, type CallTranscriptSegment as h, type CartesiaTtsModel as i, type ClipResult as j, type ClosingTurnResult as k, type CreditsLowEvent as l, DEFAULT_CREDITS_LOW_LEAD_SECONDS as m, DEFAULT_GOVERNOR_CONFIG as n, DEFAULT_GRACE_CEILING_SECONDS as o, DEFAULT_GRACE_WINDOW_LEAD_SECONDS as p, DEFAULT_IDLE_SECONDS as q, DEFAULT_IDLE_WARN_LEAD_SECONDS as r, DEFAULT_TURN_TIMEOUT_SECONDS as s, type EndedEvent as t, type ExtendResult as u, type FreezeReadingFn as v, type GovernorAction as w, type GovernorConfig as x, type GovernorSignal as y, type GovernorState as z };
|
package/dist/react-native.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { LiveKitRoomProps } from '@livekit/react-native';
|
|
2
2
|
export { AudioSession, VideoTrack, VideoTrackProps, registerGlobals } from '@livekit/react-native';
|
|
3
3
|
import { ReactNode, ReactElement } from 'react';
|
|
4
|
-
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-
|
|
5
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as
|
|
4
|
+
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-DDqYRz-_.js';
|
|
5
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, at as isNativeLiveTrackSubscribed, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aF as useLiveTrackProducing, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-DDqYRz-_.js';
|
|
6
6
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
7
|
export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
|
|
8
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
|
8
9
|
export { DisconnectReason, RemoteAudioTrack, RemoteTrack, RemoteVideoTrack, Room, RoomEvent, SendTextOptions, Track } from 'livekit-client';
|
|
9
10
|
import 'zod';
|
|
10
11
|
|
package/dist/react-native.js
CHANGED
|
@@ -130,6 +130,24 @@ function timeoutSetter(fn, ms) {
|
|
|
130
130
|
return () => clearTimeout(id);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// ../browser/src/playout-delay.ts
|
|
134
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
135
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
136
|
+
const target = playoutDelayTarget(track);
|
|
137
|
+
if (!target) return false;
|
|
138
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
142
|
+
const delay = Math.max(0, delaySeconds);
|
|
143
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
144
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
145
|
+
}
|
|
146
|
+
function playoutDelayTarget(track) {
|
|
147
|
+
const candidate = track;
|
|
148
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
// ../client/src/errors.ts
|
|
134
152
|
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
135
153
|
constructor(message, status, body, response, metadata = {}) {
|
|
@@ -327,7 +345,6 @@ function useReleaseMicLeaseOnTrackEnded(token) {
|
|
|
327
345
|
};
|
|
328
346
|
}, [localParticipant, token, localParticipant?.audioTrackPublications.size]);
|
|
329
347
|
}
|
|
330
|
-
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
331
348
|
function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
332
349
|
const videoMediaTrack = videoTrack?.publication?.track;
|
|
333
350
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
@@ -335,15 +352,6 @@ function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AV
|
|
|
335
352
|
applyAvatarPlayoutDelay(videoMediaTrack, audioMediaTrack, delaySeconds);
|
|
336
353
|
}, [videoMediaTrack, audioMediaTrack, delaySeconds]);
|
|
337
354
|
}
|
|
338
|
-
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
339
|
-
const delay = Math.max(0, delaySeconds);
|
|
340
|
-
playoutDelayTarget(videoTrack)?.setPlayoutDelay(delay);
|
|
341
|
-
playoutDelayTarget(audioTrack)?.setPlayoutDelay(delay);
|
|
342
|
-
}
|
|
343
|
-
function playoutDelayTarget(track) {
|
|
344
|
-
const candidate = track;
|
|
345
|
-
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
346
|
-
}
|
|
347
355
|
function resolveReleaseTarget(heldSessionId, queueTicketId) {
|
|
348
356
|
if (heldSessionId) return { kind: "session", sessionId: heldSessionId };
|
|
349
357
|
if (queueTicketId) return { kind: "ticket", queueTicketId };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-
|
|
3
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason,
|
|
2
|
+
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-DDqYRz-_.js';
|
|
3
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, aK as AvatarVideoSurface, aL as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, aM as LivePlaybackKeeper, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, aN as MIC_LEASE_ENDED_TIMEOUT_MS, aO as MicLease, aP as PlayableVideoElement, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, aQ as RealtimeAvatarCapacityError, aR as RealtimeAvatarLiveKitRoom, aS as RealtimeAvatarLiveKitRoomProps, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, aT as shouldReplayPendingTurn, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-DDqYRz-_.js';
|
|
4
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
|
4
5
|
import '@livekit/components-react';
|
|
5
6
|
import 'livekit-client';
|
|
6
7
|
import 'zod';
|
package/dist/react.js
CHANGED
|
@@ -89,6 +89,24 @@ function timeoutSetter(fn, ms) {
|
|
|
89
89
|
return () => clearTimeout(id);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// ../browser/src/playout-delay.ts
|
|
93
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
94
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
95
|
+
const target = playoutDelayTarget(track);
|
|
96
|
+
if (!target) return false;
|
|
97
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
101
|
+
const delay = Math.max(0, delaySeconds);
|
|
102
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
103
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
104
|
+
}
|
|
105
|
+
function playoutDelayTarget(track) {
|
|
106
|
+
const candidate = track;
|
|
107
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
// ../client/src/errors.ts
|
|
93
111
|
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
94
112
|
constructor(message, status, body, response, metadata = {}) {
|
|
@@ -286,7 +304,6 @@ function useReleaseMicLeaseOnTrackEnded(token) {
|
|
|
286
304
|
};
|
|
287
305
|
}, [localParticipant, token, localParticipant?.audioTrackPublications.size]);
|
|
288
306
|
}
|
|
289
|
-
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
290
307
|
function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
291
308
|
const videoMediaTrack = videoTrack?.publication?.track;
|
|
292
309
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
@@ -294,15 +311,6 @@ function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AV
|
|
|
294
311
|
applyAvatarPlayoutDelay(videoMediaTrack, audioMediaTrack, delaySeconds);
|
|
295
312
|
}, [videoMediaTrack, audioMediaTrack, delaySeconds]);
|
|
296
313
|
}
|
|
297
|
-
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
298
|
-
const delay = Math.max(0, delaySeconds);
|
|
299
|
-
playoutDelayTarget(videoTrack)?.setPlayoutDelay(delay);
|
|
300
|
-
playoutDelayTarget(audioTrack)?.setPlayoutDelay(delay);
|
|
301
|
-
}
|
|
302
|
-
function playoutDelayTarget(track) {
|
|
303
|
-
const candidate = track;
|
|
304
|
-
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
305
|
-
}
|
|
306
314
|
function resolveReleaseTarget(heldSessionId, queueTicketId) {
|
|
307
315
|
if (heldSessionId) return { kind: "session", sessionId: heldSessionId };
|
|
308
316
|
if (queueTicketId) return { kind: "ticket", queueTicketId };
|
|
@@ -540,6 +548,24 @@ function sameCapacityState(a, b) {
|
|
|
540
548
|
return true;
|
|
541
549
|
}
|
|
542
550
|
}
|
|
551
|
+
function AvatarPlayoutCushion({ seconds }) {
|
|
552
|
+
const room = useRoomContext();
|
|
553
|
+
useEffect(() => {
|
|
554
|
+
const onSubscribed = (track) => {
|
|
555
|
+
applyPlayoutDelay(track, seconds);
|
|
556
|
+
};
|
|
557
|
+
for (const participant of room.remoteParticipants.values()) {
|
|
558
|
+
for (const publication of participant.trackPublications.values()) {
|
|
559
|
+
applyPlayoutDelay(publication.track, seconds);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
room.on(RoomEvent.TrackSubscribed, onSubscribed);
|
|
563
|
+
return () => {
|
|
564
|
+
room.off(RoomEvent.TrackSubscribed, onSubscribed);
|
|
565
|
+
};
|
|
566
|
+
}, [room, seconds]);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
543
569
|
function RealtimeAvatarLiveKitRoom(props) {
|
|
544
570
|
const {
|
|
545
571
|
grant,
|
|
@@ -548,11 +574,13 @@ function RealtimeAvatarLiveKitRoom(props) {
|
|
|
548
574
|
video = false,
|
|
549
575
|
options,
|
|
550
576
|
renderRoomAudio = true,
|
|
577
|
+
playoutDelaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS,
|
|
551
578
|
children,
|
|
552
579
|
...roomProps
|
|
553
580
|
} = props;
|
|
554
581
|
const shouldConnect = Boolean(grant && connect);
|
|
555
582
|
const roomAudio = renderRoomAudio ? createElement(RoomAudioRenderer, { key: "room-audio" }) : null;
|
|
583
|
+
const cushion = playoutDelaySeconds === false ? null : createElement(AvatarPlayoutCushion, { key: "playout-cushion", seconds: playoutDelaySeconds });
|
|
556
584
|
const roomOptions = { adaptiveStream: false, dynacast: true, ...options };
|
|
557
585
|
return createElement(
|
|
558
586
|
LiveKitRoom,
|
|
@@ -565,7 +593,7 @@ function RealtimeAvatarLiveKitRoom(props) {
|
|
|
565
593
|
video,
|
|
566
594
|
options: roomOptions
|
|
567
595
|
},
|
|
568
|
-
createElement(Fragment, null, children, roomAudio)
|
|
596
|
+
createElement(Fragment, null, cushion, children, roomAudio)
|
|
569
597
|
);
|
|
570
598
|
}
|
|
571
599
|
var capacityErrorFromBusy = (busy) => {
|
package/dist/server.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, RealtimeAvatarOptions, StartCallOptions, verifyTranscript } from './index.js';
|
|
2
|
-
export { i as Asset, h as AssetKind, A as Avatar, m as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, n as CallConnection, C as CallMode, a as CallPolicy, o as CallQueued, d as ClipDeclaration, f as ClipLibrary, p as ClipLibraryPlan, e as ClipLibraryUpdate, q as ClipSource, g as ClipSyncResult, r as ContextMessage, l as CreditBalance, E as EndCallOptions, s as EndCallReason, j as ListSessionsOptions, L as LoopRedirect, S as StartCallResult, T as TranscriptPayload, k as UsageSession, U as UsageSessionPage, V as VideoPolicy, t as VideoState, u as isQueued } from './types-
|
|
2
|
+
export { i as Asset, h as AssetKind, A as Avatar, m as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, n as CallConnection, C as CallMode, a as CallPolicy, o as CallQueued, d as ClipDeclaration, f as ClipLibrary, p as ClipLibraryPlan, e as ClipLibraryUpdate, q as ClipSource, g as ClipSyncResult, r as ContextMessage, l as CreditBalance, E as EndCallOptions, s as EndCallReason, j as ListSessionsOptions, L as LoopRedirect, S as StartCallResult, T as TranscriptPayload, k as UsageSession, U as UsageSessionPage, V as VideoPolicy, t as VideoState, u as isQueued } from './types-D_aIKv4P.js';
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-JNRRH2G7.js';
|
package/dist/tanstack-start.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as ProxyConfig } from './types-
|
|
2
|
-
import './types-
|
|
1
|
+
import { P as ProxyConfig } from './types-QoMHpR1h.js';
|
|
2
|
+
import './types-D_aIKv4P.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* TanStack Start. Mount at `routes/api/realtime-avatar/$.ts` — the trailing `$` is Start's
|
package/dist/tanstack-start.js
CHANGED
|
@@ -585,12 +585,24 @@ interface components {
|
|
|
585
585
|
Error: {
|
|
586
586
|
/** @description Human-readable message. Not a stable contract — switch on `code` or `status`. */
|
|
587
587
|
error: string;
|
|
588
|
-
/** @description Repeats the HTTP status in the body.
|
|
589
|
-
status
|
|
590
|
-
/** @description Machine-readable discriminator
|
|
591
|
-
code
|
|
588
|
+
/** @description Repeats the HTTP status in the body. Always present. */
|
|
589
|
+
status: number;
|
|
590
|
+
/** @description Machine-readable discriminator. ALWAYS present: a failure that names its own — billing, clip-library and loop refusals, `insufficient_scope` — carries that, and everything else carries the status's default (`unauthorized`, `not_found`, `rate_limited`, …). Switch on this rather than on `error`, whose wording is not a contract. The set is open and grows with the API; the codes a given endpoint can return are documented with that endpoint at https://realtimeavatar.ai/docs/api-reference. */
|
|
591
|
+
code: string;
|
|
592
|
+
/**
|
|
593
|
+
* Format: uri
|
|
594
|
+
* @description Where to read how to resolve a failure of this class — the authentication guide for 401/403, the API reference otherwise. Always present.
|
|
595
|
+
*/
|
|
596
|
+
documentation: string;
|
|
592
597
|
/** @description Set when the failure is worth retrying as-is. */
|
|
593
598
|
retryable?: boolean;
|
|
599
|
+
/**
|
|
600
|
+
* @description Present on `403 insufficient_scope` — the one scope this operation needed and the key did not carry. Repeated in the `WWW-Authenticate` challenge as `scope=`.
|
|
601
|
+
* @enum {string}
|
|
602
|
+
*/
|
|
603
|
+
requiredScope?: "*" | "api_keys:write" | "credits:read" | "avatars:read" | "avatars:write" | "realtime:write" | "usage:read" | "usage:write";
|
|
604
|
+
/** @description Present alongside `requiredScope` — the scopes the presented key does carry. */
|
|
605
|
+
grantedScopes?: ("*" | "api_keys:write" | "credits:read" | "avatars:read" | "avatars:write" | "realtime:write" | "usage:read" | "usage:write")[];
|
|
594
606
|
/** @description Present on 402 — where to send the user to top up. */
|
|
595
607
|
billingUrl?: string;
|
|
596
608
|
} & {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CallMode, a as CallPolicy } from './types-
|
|
1
|
+
import { C as CallMode, a as CallPolicy } from './types-D_aIKv4P.js';
|
|
2
2
|
|
|
3
3
|
/** The three things a caller can ask the proxy to do. Gate on these, not on URLs. */
|
|
4
4
|
type ProxyOperation = "connect" | "end" | "avatars" | "credits";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtime-avatar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Realtime Avatar SDK. `realtime-avatar` and the route adapters hold your API key and are server-only; `realtime-avatar/react`, `/browser` and `/tools` never can — importing a server entry into a browser build throws at bundle time.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "The Influence Company",
|