@spatialwalk/avatarkit-rtc 1.0.0-beta.1
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 +417 -0
- package/dist/assets/animation-worker-CUXZycUw.js.map +1 -0
- package/dist/core/AnimationHandler.d.ts +17 -0
- package/dist/core/AnimationHandler.d.ts.map +1 -0
- package/dist/core/AvatarPlayer.d.ts +119 -0
- package/dist/core/AvatarPlayer.d.ts.map +1 -0
- package/dist/core/RTCProvider.d.ts +84 -0
- package/dist/core/RTCProvider.d.ts.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/index10.js +67 -0
- package/dist/index10.js.map +1 -0
- package/dist/index11.js +390 -0
- package/dist/index11.js.map +1 -0
- package/dist/index12.js +108 -0
- package/dist/index12.js.map +1 -0
- package/dist/index13.js +18 -0
- package/dist/index13.js.map +1 -0
- package/dist/index14.js +48 -0
- package/dist/index14.js.map +1 -0
- package/dist/index15.js +29 -0
- package/dist/index15.js.map +1 -0
- package/dist/index16.js +144 -0
- package/dist/index16.js.map +1 -0
- package/dist/index17.js +106 -0
- package/dist/index17.js.map +1 -0
- package/dist/index18.js +28 -0
- package/dist/index18.js.map +1 -0
- package/dist/index2.js +220 -0
- package/dist/index2.js.map +1 -0
- package/dist/index3.js +586 -0
- package/dist/index3.js.map +1 -0
- package/dist/index4.js +410 -0
- package/dist/index4.js.map +1 -0
- package/dist/index5.js +20 -0
- package/dist/index5.js.map +1 -0
- package/dist/index6.js +282 -0
- package/dist/index6.js.map +1 -0
- package/dist/index7.js +53 -0
- package/dist/index7.js.map +1 -0
- package/dist/index8.js +189 -0
- package/dist/index8.js.map +1 -0
- package/dist/index9.js +178 -0
- package/dist/index9.js.map +1 -0
- package/dist/proto/animation.d.ts +12 -0
- package/dist/proto/animation.d.ts.map +1 -0
- package/dist/providers/agora/AgoraProvider.d.ts +71 -0
- package/dist/providers/agora/AgoraProvider.d.ts.map +1 -0
- package/dist/providers/agora/SEIExtractor.d.ts +29 -0
- package/dist/providers/agora/SEIExtractor.d.ts.map +1 -0
- package/dist/providers/agora/index.d.ts +11 -0
- package/dist/providers/agora/index.d.ts.map +1 -0
- package/dist/providers/agora/types.d.ts +14 -0
- package/dist/providers/agora/types.d.ts.map +1 -0
- package/dist/providers/base/BaseProvider.d.ts +11 -0
- package/dist/providers/base/BaseProvider.d.ts.map +1 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/livekit/LiveKitProvider.d.ts +64 -0
- package/dist/providers/livekit/LiveKitProvider.d.ts.map +1 -0
- package/dist/providers/livekit/VP8Extractor.d.ts +10 -0
- package/dist/providers/livekit/VP8Extractor.d.ts.map +1 -0
- package/dist/providers/livekit/animation-transform.d.ts +11 -0
- package/dist/providers/livekit/animation-transform.d.ts.map +1 -0
- package/dist/providers/livekit/animation-worker.d.ts +14 -0
- package/dist/providers/livekit/animation-worker.d.ts.map +1 -0
- package/dist/providers/livekit/index.d.ts +11 -0
- package/dist/providers/livekit/index.d.ts.map +1 -0
- package/dist/providers/livekit/types.d.ts +11 -0
- package/dist/providers/livekit/types.d.ts.map +1 -0
- package/dist/providers/livekit/utils.d.ts +11 -0
- package/dist/providers/livekit/utils.d.ts.map +1 -0
- package/dist/types/index.d.ts +77 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/package.json +61 -0
package/dist/index6.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { decodeAnimationKeyframes } from "./index8.js";
|
|
5
|
+
import { logger } from "./index7.js";
|
|
6
|
+
class AnimationHandler {
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
constructor(renderer, config = {}) {
|
|
11
|
+
/** @internal */
|
|
12
|
+
__publicField(this, "renderer");
|
|
13
|
+
/** @internal */
|
|
14
|
+
__publicField(this, "decoder");
|
|
15
|
+
/** @internal */
|
|
16
|
+
__publicField(this, "config");
|
|
17
|
+
// Frame tracking
|
|
18
|
+
/** @internal */
|
|
19
|
+
__publicField(this, "animationFrameCount", 0);
|
|
20
|
+
/** @internal */
|
|
21
|
+
__publicField(this, "lastRenderedFrameSeq", -1);
|
|
22
|
+
/** @internal */
|
|
23
|
+
__publicField(this, "renderedFrameCount", 0);
|
|
24
|
+
// Transition state
|
|
25
|
+
/** @internal */
|
|
26
|
+
__publicField(this, "isPlayingTransition", false);
|
|
27
|
+
/** @internal */
|
|
28
|
+
__publicField(this, "isTransitioningToIdle", false);
|
|
29
|
+
/** @internal */
|
|
30
|
+
__publicField(this, "transitionFrames", []);
|
|
31
|
+
/** @internal */
|
|
32
|
+
__publicField(this, "transitionFrameIndex", 0);
|
|
33
|
+
/** @internal */
|
|
34
|
+
__publicField(this, "transitionTimeoutId", null);
|
|
35
|
+
// Guards against race conditions during async transition generation
|
|
36
|
+
/** @internal */
|
|
37
|
+
__publicField(this, "isGeneratingStartTransition", false);
|
|
38
|
+
/** @internal */
|
|
39
|
+
__publicField(this, "isGeneratingEndTransition", false);
|
|
40
|
+
// Session-level flags to prevent duplicate handling
|
|
41
|
+
// These are reset in resetTracking() for new sessions
|
|
42
|
+
/** @internal */
|
|
43
|
+
__publicField(this, "hasHandledTransitionStart", false);
|
|
44
|
+
/** @internal */
|
|
45
|
+
__publicField(this, "hasHandledTransitionEnd", false);
|
|
46
|
+
this.renderer = renderer;
|
|
47
|
+
this.decoder = config.decoder ?? decodeAnimationKeyframes;
|
|
48
|
+
this.config = {
|
|
49
|
+
transitionStartFrameCount: config.transitionStartFrameCount ?? 8,
|
|
50
|
+
transitionEndFrameCount: config.transitionEndFrameCount ?? 12
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Handle animation data received from RTC provider.
|
|
55
|
+
* @param protobufData - Raw protobuf bytes
|
|
56
|
+
* @param frameSeq - Frame sequence number (optional)
|
|
57
|
+
* @param isRecovered - Whether this frame was recovered via ALR
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
handleAnimationData(protobufData, frameSeq, isRecovered) {
|
|
61
|
+
if (this.isPlayingTransition) {
|
|
62
|
+
this.stopTransition();
|
|
63
|
+
}
|
|
64
|
+
this.animationFrameCount++;
|
|
65
|
+
this.renderedFrameCount++;
|
|
66
|
+
if (frameSeq !== void 0 && this.lastRenderedFrameSeq !== -1) {
|
|
67
|
+
if (frameSeq <= this.lastRenderedFrameSeq) {
|
|
68
|
+
logger.info(
|
|
69
|
+
"AnimationHandler",
|
|
70
|
+
`OUT-OF-ORDER/DUPLICATE: seq=${frameSeq}, lastRendered=${this.lastRenderedFrameSeq}${isRecovered ? " [RECOVERED]" : ""}`
|
|
71
|
+
);
|
|
72
|
+
} else if (frameSeq > this.lastRenderedFrameSeq + 1) {
|
|
73
|
+
const gap = frameSeq - this.lastRenderedFrameSeq - 1;
|
|
74
|
+
logger.info(
|
|
75
|
+
"AnimationHandler",
|
|
76
|
+
`GAP: ${gap} frame(s) between ${this.lastRenderedFrameSeq} and ${frameSeq}${isRecovered ? " [RECOVERED]" : ""}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (frameSeq !== void 0) {
|
|
81
|
+
this.lastRenderedFrameSeq = frameSeq;
|
|
82
|
+
}
|
|
83
|
+
const keyframes = this.decoder(protobufData);
|
|
84
|
+
if (keyframes && keyframes.length > 0) {
|
|
85
|
+
this.renderer.renderFrame(keyframes[0]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Handle transition packet - generate and play transition from idle to target.
|
|
90
|
+
* Only starts transition on the first packet; subsequent packets are ignored while transitioning.
|
|
91
|
+
* @param protobufData - Protobuf data containing target frame
|
|
92
|
+
* @param frameCount - Number of transition frames (overrides config if provided)
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
async handleTransitionData(protobufData, frameCount) {
|
|
96
|
+
if (this.hasHandledTransitionStart) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (this.isPlayingTransition && !this.isTransitioningToIdle) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (this.isGeneratingStartTransition) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!this.renderer.isReady()) {
|
|
106
|
+
logger.warn("AnimationHandler", "Renderer not ready for transition");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const keyframes = this.decoder(protobufData);
|
|
110
|
+
if (!keyframes || keyframes.length === 0) {
|
|
111
|
+
logger.warn("AnimationHandler", "No target keyframe in transition data");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.hasHandledTransitionStart = true;
|
|
115
|
+
this.hasHandledTransitionEnd = false;
|
|
116
|
+
const targetFrame = keyframes[0];
|
|
117
|
+
const frames = frameCount ?? this.config.transitionStartFrameCount;
|
|
118
|
+
logger.info("AnimationHandler", `Generating ${frames} transition frames to target`);
|
|
119
|
+
this.isGeneratingStartTransition = true;
|
|
120
|
+
try {
|
|
121
|
+
const transitionFrames = await this.renderer.generateTransitionFromIdle(
|
|
122
|
+
targetFrame,
|
|
123
|
+
frames
|
|
124
|
+
);
|
|
125
|
+
logger.info("AnimationHandler", `Generated ${transitionFrames.length} transition frames`);
|
|
126
|
+
this.isPlayingTransition = true;
|
|
127
|
+
this.isTransitioningToIdle = false;
|
|
128
|
+
this.transitionFrames = transitionFrames;
|
|
129
|
+
this.transitionFrameIndex = 0;
|
|
130
|
+
this.playTransitionFrame();
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.error("AnimationHandler", "Failed to generate transition:", error);
|
|
133
|
+
this.renderer.renderFrame(targetFrame);
|
|
134
|
+
} finally {
|
|
135
|
+
this.isGeneratingStartTransition = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Handle transition end - generate and play reverse transition back to idle.
|
|
140
|
+
* Only starts transition on the first packet; subsequent packets are ignored while transitioning.
|
|
141
|
+
* @param protobufData - Protobuf data containing last animation frame
|
|
142
|
+
* @param frameCount - Number of transition frames (overrides config if provided)
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
async handleTransitionToIdle(protobufData, frameCount) {
|
|
146
|
+
if (this.hasHandledTransitionEnd) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (this.isPlayingTransition && this.isTransitioningToIdle) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this.isGeneratingEndTransition) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!this.renderer.isReady()) {
|
|
156
|
+
logger.warn("AnimationHandler", "Renderer not ready for transition to idle");
|
|
157
|
+
this.renderer.renderFrame(void 0, true);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const keyframes = this.decoder(protobufData);
|
|
161
|
+
if (!keyframes || keyframes.length === 0) {
|
|
162
|
+
logger.warn("AnimationHandler", "No last keyframe in transition end data, starting idle directly");
|
|
163
|
+
this.renderer.renderFrame(void 0, true);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.hasHandledTransitionEnd = true;
|
|
167
|
+
const lastFrame = keyframes[0];
|
|
168
|
+
const frames = frameCount ?? this.config.transitionEndFrameCount;
|
|
169
|
+
logger.info("AnimationHandler", `Generating ${frames} reverse transition frames to idle`);
|
|
170
|
+
this.isGeneratingEndTransition = true;
|
|
171
|
+
try {
|
|
172
|
+
const transitionFrames = await this.renderer.generateTransitionFromIdle(
|
|
173
|
+
lastFrame,
|
|
174
|
+
frames
|
|
175
|
+
);
|
|
176
|
+
logger.info("AnimationHandler", `Generated ${transitionFrames.length} transition frames, reversing for playback`);
|
|
177
|
+
const reversedFrames = transitionFrames.slice().reverse();
|
|
178
|
+
this.isPlayingTransition = true;
|
|
179
|
+
this.isTransitioningToIdle = true;
|
|
180
|
+
this.transitionFrames = reversedFrames;
|
|
181
|
+
this.transitionFrameIndex = 0;
|
|
182
|
+
this.playTransitionFrame();
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logger.error("AnimationHandler", "Failed to generate reverse transition:", error);
|
|
185
|
+
this.renderer.renderFrame(void 0, true);
|
|
186
|
+
} finally {
|
|
187
|
+
this.isGeneratingEndTransition = false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Start idle animation.
|
|
192
|
+
* @internal
|
|
193
|
+
*/
|
|
194
|
+
startIdle() {
|
|
195
|
+
this.renderer.renderFrame(void 0, true);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Reset animation frame tracking (call on session start).
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
201
|
+
resetTracking() {
|
|
202
|
+
this.lastRenderedFrameSeq = -1;
|
|
203
|
+
this.renderedFrameCount = 0;
|
|
204
|
+
this.animationFrameCount = 0;
|
|
205
|
+
this.hasHandledTransitionStart = false;
|
|
206
|
+
this.hasHandledTransitionEnd = false;
|
|
207
|
+
logger.info("AnimationHandler", "Frame tracking reset");
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check if currently playing transition frames.
|
|
211
|
+
* @internal
|
|
212
|
+
*/
|
|
213
|
+
isInTransition() {
|
|
214
|
+
return this.isPlayingTransition;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Stop transition playback.
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
stopTransition() {
|
|
221
|
+
if (this.isPlayingTransition || this.isGeneratingStartTransition || this.isGeneratingEndTransition) {
|
|
222
|
+
logger.info("AnimationHandler", "Stopping transition playback");
|
|
223
|
+
}
|
|
224
|
+
this.isPlayingTransition = false;
|
|
225
|
+
this.isTransitioningToIdle = false;
|
|
226
|
+
this.isGeneratingStartTransition = false;
|
|
227
|
+
this.isGeneratingEndTransition = false;
|
|
228
|
+
this.transitionFrames = [];
|
|
229
|
+
this.transitionFrameIndex = 0;
|
|
230
|
+
if (this.transitionTimeoutId !== null) {
|
|
231
|
+
clearTimeout(this.transitionTimeoutId);
|
|
232
|
+
this.transitionTimeoutId = null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Clean up resources.
|
|
237
|
+
* @internal
|
|
238
|
+
*/
|
|
239
|
+
dispose() {
|
|
240
|
+
this.stopTransition();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Play a single transition frame and schedule the next one.
|
|
244
|
+
* @internal
|
|
245
|
+
*/
|
|
246
|
+
playTransitionFrame() {
|
|
247
|
+
if (!this.isPlayingTransition || this.transitionFrameIndex >= this.transitionFrames.length) {
|
|
248
|
+
const wasTransitioningToIdle = this.isTransitioningToIdle;
|
|
249
|
+
this.isPlayingTransition = false;
|
|
250
|
+
this.isTransitioningToIdle = false;
|
|
251
|
+
this.transitionFrames = [];
|
|
252
|
+
this.transitionFrameIndex = 0;
|
|
253
|
+
if (this.transitionTimeoutId !== null) {
|
|
254
|
+
clearTimeout(this.transitionTimeoutId);
|
|
255
|
+
this.transitionTimeoutId = null;
|
|
256
|
+
}
|
|
257
|
+
logger.info("AnimationHandler", "Transition playback complete");
|
|
258
|
+
if (wasTransitioningToIdle) {
|
|
259
|
+
logger.info("AnimationHandler", "Starting idle animation after transition");
|
|
260
|
+
this.renderer.renderFrame(void 0, true);
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (!this.renderer.isReady()) {
|
|
265
|
+
this.isPlayingTransition = false;
|
|
266
|
+
this.isTransitioningToIdle = false;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const frame = this.transitionFrames[this.transitionFrameIndex];
|
|
270
|
+
this.renderer.renderFrame(frame);
|
|
271
|
+
this.transitionFrameIndex++;
|
|
272
|
+
this.transitionTimeoutId = setTimeout(() => {
|
|
273
|
+
if (this.isPlayingTransition) {
|
|
274
|
+
this.playTransitionFrame();
|
|
275
|
+
}
|
|
276
|
+
}, 40);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
export {
|
|
280
|
+
AnimationHandler
|
|
281
|
+
};
|
|
282
|
+
//# sourceMappingURL=index6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index6.js","sources":["../src/core/AnimationHandler.ts"],"sourcesContent":["/**\n * Animation Handler - Orchestrates animation playback and transitions.\n *\n * This module handles:\n * - Animation frame rendering\n * - Transition playback from idle to animation and back\n * - Frame timing at 25fps\n * - Session state tracking\n *\n * The handler relies on server-sent packet flags (Transition, TransitionEnd, Idle)\n * to determine when to generate transitions, rather than maintaining complex internal state.\n *\n * @internal\n * @packageDocumentation\n */\n\nimport type { Flame } from '../proto/animation';\nimport { decodeAnimationKeyframes } from '../proto/animation';\nimport { logger } from '../utils';\n\n/**\n * Interface for decoding protobuf animation data.\n * Allows custom decoder implementation if needed.\n * @internal\n */\nexport type AnimationDecoder = (protobufData: ArrayBuffer) => Flame[] | null;\n\n/**\n * Interface that applications must implement to render avatar frames.\n * The SDK calls these methods to control the avatar.\n * @internal\n */\nexport interface AvatarRenderer {\n /**\n * Render a single animation frame.\n * @param flame - The frame to render, or undefined to render idle\n * @param startIdle - If true and flame is undefined, start idle animation loop\n */\n renderFrame(flame: Flame | undefined, startIdle?: boolean): void;\n\n /**\n * Generate transition frames from idle position to target frame.\n * @param targetFrame - The target frame to transition to\n * @param frameCount - Number of transition frames to generate\n * @returns Promise resolving to array of transition frames\n */\n generateTransitionFromIdle(\n targetFrame: Flame,\n frameCount: number\n ): Promise<Flame[]>;\n\n /**\n * Check if the renderer is available and ready.\n * @returns true if renderer is ready to accept frames\n */\n isReady(): boolean;\n}\n\n/**\n * Configuration for AnimationHandler.\n * @internal\n */\nexport interface AnimationHandlerConfig {\n /**\n * Number of transition frames when starting animation.\n * Default: 8 (~320ms at 25fps)\n */\n transitionStartFrameCount?: number;\n\n /**\n * Number of transition frames when ending animation.\n * Default: 12 (~480ms at 25fps)\n */\n transitionEndFrameCount?: number;\n\n /**\n * Custom decoder function.\n * Uses built-in protobuf decoder if not provided.\n */\n decoder?: AnimationDecoder;\n}\n\n/**\n * AnimationHandler manages all animation playback and transition logic.\n *\n * @internal\n * This class is used internally by AvatarPlayer.\n * Applications should not instantiate this class directly.\n */\nexport class AnimationHandler {\n /** @internal */\n private renderer: AvatarRenderer;\n /** @internal */\n private decoder: AnimationDecoder;\n /** @internal */\n private config: Required<Omit<AnimationHandlerConfig, 'decoder'>>;\n\n // Frame tracking\n /** @internal */\n private animationFrameCount = 0;\n /** @internal */\n private lastRenderedFrameSeq = -1;\n /** @internal */\n private renderedFrameCount = 0;\n\n // Transition state\n /** @internal */\n private isPlayingTransition = false;\n /** @internal */\n private isTransitioningToIdle = false;\n /** @internal */\n private transitionFrames: Flame[] = [];\n /** @internal */\n private transitionFrameIndex = 0;\n /** @internal */\n private transitionTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n // Guards against race conditions during async transition generation\n /** @internal */\n private isGeneratingStartTransition = false;\n /** @internal */\n private isGeneratingEndTransition = false;\n\n // Session-level flags to prevent duplicate handling\n // These are reset in resetTracking() for new sessions\n /** @internal */\n private hasHandledTransitionStart = false;\n /** @internal */\n private hasHandledTransitionEnd = false;\n\n /**\n * @internal\n */\n constructor(renderer: AvatarRenderer, config: AnimationHandlerConfig = {}) {\n this.renderer = renderer;\n this.decoder = config.decoder ?? decodeAnimationKeyframes;\n this.config = {\n transitionStartFrameCount: config.transitionStartFrameCount ?? 8,\n transitionEndFrameCount: config.transitionEndFrameCount ?? 12,\n };\n }\n\n /**\n * Handle animation data received from RTC provider.\n * @param protobufData - Raw protobuf bytes\n * @param frameSeq - Frame sequence number (optional)\n * @param isRecovered - Whether this frame was recovered via ALR\n * @internal\n */\n handleAnimationData(\n protobufData: ArrayBuffer,\n frameSeq?: number,\n isRecovered?: boolean\n ): void {\n // If we were in transition, stop it and play normal animation\n if (this.isPlayingTransition) {\n this.stopTransition();\n }\n\n this.animationFrameCount++;\n this.renderedFrameCount++;\n\n // Check for out-of-order delivery (info level - expected with marker frames)\n if (frameSeq !== undefined && this.lastRenderedFrameSeq !== -1) {\n if (frameSeq <= this.lastRenderedFrameSeq) {\n logger.info(\n 'AnimationHandler',\n `OUT-OF-ORDER/DUPLICATE: seq=${frameSeq}, lastRendered=${this.lastRenderedFrameSeq}${isRecovered ? ' [RECOVERED]' : ''}`\n );\n } else if (frameSeq > this.lastRenderedFrameSeq + 1) {\n const gap = frameSeq - this.lastRenderedFrameSeq - 1;\n logger.info(\n 'AnimationHandler',\n `GAP: ${gap} frame(s) between ${this.lastRenderedFrameSeq} and ${frameSeq}${isRecovered ? ' [RECOVERED]' : ''}`\n );\n }\n }\n\n if (frameSeq !== undefined) {\n this.lastRenderedFrameSeq = frameSeq;\n }\n\n // Decode and render\n const keyframes = this.decoder(protobufData);\n\n if (keyframes && keyframes.length > 0) {\n this.renderer.renderFrame(keyframes[0]);\n }\n }\n\n /**\n * Handle transition packet - generate and play transition from idle to target.\n * Only starts transition on the first packet; subsequent packets are ignored while transitioning.\n * @param protobufData - Protobuf data containing target frame\n * @param frameCount - Number of transition frames (overrides config if provided)\n * @internal\n */\n async handleTransitionData(\n protobufData: ArrayBuffer,\n frameCount?: number\n ): Promise<void> {\n // Ignore if we've already handled a transition start for this session\n if (this.hasHandledTransitionStart) {\n return;\n }\n // Ignore if already playing or generating a start transition\n if (this.isPlayingTransition && !this.isTransitioningToIdle) {\n return;\n }\n if (this.isGeneratingStartTransition) {\n return;\n }\n\n if (!this.renderer.isReady()) {\n logger.warn('AnimationHandler', 'Renderer not ready for transition');\n return;\n }\n\n // Decode target frame\n const keyframes = this.decoder(protobufData);\n if (!keyframes || keyframes.length === 0) {\n logger.warn('AnimationHandler', 'No target keyframe in transition data');\n return;\n }\n\n // Mark that we've handled transition start for this session\n this.hasHandledTransitionStart = true;\n // Reset transition end flag since we're starting a new transition\n this.hasHandledTransitionEnd = false;\n\n const targetFrame = keyframes[0];\n const frames = frameCount ?? this.config.transitionStartFrameCount;\n logger.info('AnimationHandler', `Generating ${frames} transition frames to target`);\n\n // Set guard flag before async operation\n this.isGeneratingStartTransition = true;\n\n try {\n // Generate transition frames from idle to target\n const transitionFrames = await this.renderer.generateTransitionFromIdle(\n targetFrame,\n frames\n );\n logger.info('AnimationHandler', `Generated ${transitionFrames.length} transition frames`);\n\n // Start playing transition frames at 25fps\n this.isPlayingTransition = true;\n this.isTransitioningToIdle = false;\n this.transitionFrames = transitionFrames;\n this.transitionFrameIndex = 0;\n\n this.playTransitionFrame();\n } catch (error) {\n logger.error('AnimationHandler', 'Failed to generate transition:', error);\n // Fallback: render target directly\n this.renderer.renderFrame(targetFrame);\n } finally {\n this.isGeneratingStartTransition = false;\n }\n }\n\n /**\n * Handle transition end - generate and play reverse transition back to idle.\n * Only starts transition on the first packet; subsequent packets are ignored while transitioning.\n * @param protobufData - Protobuf data containing last animation frame\n * @param frameCount - Number of transition frames (overrides config if provided)\n * @internal\n */\n async handleTransitionToIdle(\n protobufData: ArrayBuffer,\n frameCount?: number\n ): Promise<void> {\n // Ignore if we've already handled a transition end for this session\n if (this.hasHandledTransitionEnd) {\n return;\n }\n // Ignore if already playing or generating an end transition\n if (this.isPlayingTransition && this.isTransitioningToIdle) {\n return;\n }\n if (this.isGeneratingEndTransition) {\n return;\n }\n\n if (!this.renderer.isReady()) {\n logger.warn('AnimationHandler', 'Renderer not ready for transition to idle');\n this.renderer.renderFrame(undefined, true);\n return;\n }\n\n // Decode last animation frame\n const keyframes = this.decoder(protobufData);\n if (!keyframes || keyframes.length === 0) {\n logger.warn('AnimationHandler', 'No last keyframe in transition end data, starting idle directly');\n this.renderer.renderFrame(undefined, true);\n return;\n }\n\n // Mark that we've handled transition end for this session\n this.hasHandledTransitionEnd = true;\n\n const lastFrame = keyframes[0];\n const frames = frameCount ?? this.config.transitionEndFrameCount;\n logger.info('AnimationHandler', `Generating ${frames} reverse transition frames to idle`);\n\n // Set guard flag before async operation\n this.isGeneratingEndTransition = true;\n\n try {\n // Generate transition frames from idle to last frame, then reverse\n const transitionFrames = await this.renderer.generateTransitionFromIdle(\n lastFrame,\n frames\n );\n logger.info('AnimationHandler', `Generated ${transitionFrames.length} transition frames, reversing for playback`);\n\n // Reverse frames to play from last animation frame back to idle\n const reversedFrames = transitionFrames.slice().reverse();\n\n // Start playing reversed transition at 25fps\n this.isPlayingTransition = true;\n this.isTransitioningToIdle = true;\n this.transitionFrames = reversedFrames;\n this.transitionFrameIndex = 0;\n\n this.playTransitionFrame();\n } catch (error) {\n logger.error('AnimationHandler', 'Failed to generate reverse transition:', error);\n this.renderer.renderFrame(undefined, true);\n } finally {\n this.isGeneratingEndTransition = false;\n }\n }\n\n /**\n * Start idle animation.\n * @internal\n */\n startIdle(): void {\n this.renderer.renderFrame(undefined, true);\n }\n\n /**\n * Reset animation frame tracking (call on session start).\n * @internal\n */\n resetTracking(): void {\n this.lastRenderedFrameSeq = -1;\n this.renderedFrameCount = 0;\n this.animationFrameCount = 0;\n // Reset session-level transition flags for new session\n this.hasHandledTransitionStart = false;\n this.hasHandledTransitionEnd = false;\n logger.info('AnimationHandler', 'Frame tracking reset');\n }\n\n /**\n * Check if currently playing transition frames.\n * @internal\n */\n isInTransition(): boolean {\n return this.isPlayingTransition;\n }\n\n /**\n * Stop transition playback.\n * @internal\n */\n stopTransition(): void {\n if (\n this.isPlayingTransition ||\n this.isGeneratingStartTransition ||\n this.isGeneratingEndTransition\n ) {\n logger.info('AnimationHandler', 'Stopping transition playback');\n }\n this.isPlayingTransition = false;\n this.isTransitioningToIdle = false;\n this.isGeneratingStartTransition = false;\n this.isGeneratingEndTransition = false;\n // Note: We intentionally do NOT reset hasHandledTransitionStart/End here\n // Those are session-level flags, only reset in resetTracking()\n this.transitionFrames = [];\n this.transitionFrameIndex = 0;\n if (this.transitionTimeoutId !== null) {\n clearTimeout(this.transitionTimeoutId);\n this.transitionTimeoutId = null;\n }\n }\n\n /**\n * Clean up resources.\n * @internal\n */\n dispose(): void {\n this.stopTransition();\n }\n\n /**\n * Play a single transition frame and schedule the next one.\n * @internal\n */\n private playTransitionFrame(): void {\n if (\n !this.isPlayingTransition ||\n this.transitionFrameIndex >= this.transitionFrames.length\n ) {\n // Transition complete\n const wasTransitioningToIdle = this.isTransitioningToIdle;\n this.isPlayingTransition = false;\n this.isTransitioningToIdle = false;\n this.transitionFrames = [];\n this.transitionFrameIndex = 0;\n if (this.transitionTimeoutId !== null) {\n clearTimeout(this.transitionTimeoutId);\n this.transitionTimeoutId = null;\n }\n logger.info('AnimationHandler', 'Transition playback complete');\n\n // If transitioning to idle, start idle animation\n if (wasTransitioningToIdle) {\n logger.info('AnimationHandler', 'Starting idle animation after transition');\n this.renderer.renderFrame(undefined, true);\n }\n return;\n }\n\n if (!this.renderer.isReady()) {\n this.isPlayingTransition = false;\n this.isTransitioningToIdle = false;\n return;\n }\n\n const frame = this.transitionFrames[this.transitionFrameIndex];\n this.renderer.renderFrame(frame);\n this.transitionFrameIndex++;\n\n // Schedule next frame at 25fps (40ms)\n this.transitionTimeoutId = setTimeout(() => {\n if (this.isPlayingTransition) {\n this.playTransitionFrame();\n }\n }, 40);\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index7.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const LOG_LEVEL_PRIORITY = {
|
|
2
|
+
info: 0,
|
|
3
|
+
warning: 1,
|
|
4
|
+
error: 2,
|
|
5
|
+
none: 3
|
|
6
|
+
};
|
|
7
|
+
let currentLevel = "warning";
|
|
8
|
+
let prefix = "[RTC]";
|
|
9
|
+
function configureLogger(config) {
|
|
10
|
+
if (config.level !== void 0) {
|
|
11
|
+
currentLevel = config.level;
|
|
12
|
+
}
|
|
13
|
+
if (config.prefix !== void 0) {
|
|
14
|
+
prefix = config.prefix;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function shouldLog(level) {
|
|
18
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentLevel];
|
|
19
|
+
}
|
|
20
|
+
function formatMessage(component, message) {
|
|
21
|
+
return `${prefix}[${component}] ${message}`;
|
|
22
|
+
}
|
|
23
|
+
const logger = {
|
|
24
|
+
/**
|
|
25
|
+
* Log info message (debug-level, verbose).
|
|
26
|
+
*/
|
|
27
|
+
info(component, message, ...args) {
|
|
28
|
+
if (shouldLog("info")) {
|
|
29
|
+
console.log(formatMessage(component, message), ...args);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Log warning message.
|
|
34
|
+
*/
|
|
35
|
+
warn(component, message, ...args) {
|
|
36
|
+
if (shouldLog("warning")) {
|
|
37
|
+
console.warn(formatMessage(component, message), ...args);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Log error message.
|
|
42
|
+
*/
|
|
43
|
+
error(component, message, ...args) {
|
|
44
|
+
if (shouldLog("error")) {
|
|
45
|
+
console.error(formatMessage(component, message), ...args);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
configureLogger,
|
|
51
|
+
logger
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index7.js","sources":["../src/utils/logger.ts"],"sourcesContent":["/**\n * Logger utility for @spatialwalk/avatarkit-rtc SDK.\n *\n * Supports three log levels: info, warning, error.\n * External consumers can configure the log level at initialization.\n *\n * @packageDocumentation\n */\n\n/**\n * Log levels supported by the SDK.\n */\nexport type LogLevel = 'info' | 'warning' | 'error' | 'none';\n\n/**\n * Logger configuration options.\n * @internal\n */\nexport interface LoggerConfig {\n /** Minimum log level to output. Default: 'warning' */\n level?: LogLevel;\n /** Custom prefix for log messages. Default: '[RTC]' */\n prefix?: string;\n}\n\n// Log level priority (higher = more important)\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n info: 0,\n warning: 1,\n error: 2,\n none: 3,\n};\n\n// Global logger state\nlet currentLevel: LogLevel = 'warning';\nlet prefix = '[RTC]';\n\n/**\n * Configure the SDK logger.\n * @internal\n */\nexport function configureLogger(config: LoggerConfig): void {\n if (config.level !== undefined) {\n currentLevel = config.level;\n }\n if (config.prefix !== undefined) {\n prefix = config.prefix;\n }\n}\n\n/**\n * Get current log level.\n * @internal\n */\nexport function getLogLevel(): LogLevel {\n return currentLevel;\n}\n\n/**\n * Check if a log level should be output.\n */\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentLevel];\n}\n\n/**\n * Format log message with prefix and component name.\n */\nfunction formatMessage(component: string, message: string): string {\n return `${prefix}[${component}] ${message}`;\n}\n\n/**\n * Internal logger instance.\n * @internal\n */\nexport const logger = {\n /**\n * Log info message (debug-level, verbose).\n */\n info(component: string, message: string, ...args: unknown[]): void {\n if (shouldLog('info')) {\n console.log(formatMessage(component, message), ...args);\n }\n },\n\n /**\n * Log warning message.\n */\n warn(component: string, message: string, ...args: unknown[]): void {\n if (shouldLog('warning')) {\n console.warn(formatMessage(component, message), ...args);\n }\n },\n\n /**\n * Log error message.\n */\n error(component: string, message: string, ...args: unknown[]): void {\n if (shouldLog('error')) {\n console.error(formatMessage(component, message), ...args);\n }\n },\n};\n"],"names":[],"mappings":"AA0BA,MAAM,qBAA+C;AAAA,EACnD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AACR;AAGA,IAAI,eAAyB;AAC7B,IAAI,SAAS;AAMN,SAAS,gBAAgB,QAA4B;AAC1D,MAAI,OAAO,UAAU,QAAW;AAC9B,mBAAe,OAAO;AAAA,EACxB;AACA,MAAI,OAAO,WAAW,QAAW;AAC/B,aAAS,OAAO;AAAA,EAClB;AACF;AAaA,SAAS,UAAU,OAA0B;AAC3C,SAAO,mBAAmB,KAAK,KAAK,mBAAmB,YAAY;AACrE;AAKA,SAAS,cAAc,WAAmB,SAAyB;AACjE,SAAO,GAAG,MAAM,IAAI,SAAS,KAAK,OAAO;AAC3C;AAMO,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,EAIpB,KAAK,WAAmB,YAAoB,MAAuB;AACjE,QAAI,UAAU,MAAM,GAAG;AACrB,cAAQ,IAAI,cAAc,WAAW,OAAO,GAAG,GAAG,IAAI;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,WAAmB,YAAoB,MAAuB;AACjE,QAAI,UAAU,SAAS,GAAG;AACxB,cAAQ,KAAK,cAAc,WAAW,OAAO,GAAG,GAAG,IAAI;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAmB,YAAoB,MAAuB;AAClE,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,MAAM,cAAc,WAAW,OAAO,GAAG,GAAG,IAAI;AAAA,IAC1D;AAAA,EACF;AACF;"}
|
package/dist/index8.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { logger } from "./index7.js";
|
|
2
|
+
import { BinaryReader } from "./index9.js";
|
|
3
|
+
function decodeFlame(reader, length) {
|
|
4
|
+
const end = reader.pos + length;
|
|
5
|
+
const flame = {
|
|
6
|
+
translation: [],
|
|
7
|
+
rotation: [],
|
|
8
|
+
neckPose: [],
|
|
9
|
+
jawPose: [],
|
|
10
|
+
eyePose: [],
|
|
11
|
+
eyeLid: [],
|
|
12
|
+
expression: []
|
|
13
|
+
};
|
|
14
|
+
while (reader.pos < end) {
|
|
15
|
+
const tag = reader.uint32();
|
|
16
|
+
switch (tag >>> 3) {
|
|
17
|
+
case 1:
|
|
18
|
+
if (tag === 13) {
|
|
19
|
+
flame.translation.push(reader.float());
|
|
20
|
+
} else if (tag === 10) {
|
|
21
|
+
const end2 = reader.uint32() + reader.pos;
|
|
22
|
+
while (reader.pos < end2) {
|
|
23
|
+
flame.translation.push(reader.float());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
case 2:
|
|
28
|
+
if (tag === 21) {
|
|
29
|
+
flame.rotation.push(reader.float());
|
|
30
|
+
} else if (tag === 18) {
|
|
31
|
+
const end2 = reader.uint32() + reader.pos;
|
|
32
|
+
while (reader.pos < end2) {
|
|
33
|
+
flame.rotation.push(reader.float());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
case 3:
|
|
38
|
+
if (tag === 29) {
|
|
39
|
+
flame.neckPose.push(reader.float());
|
|
40
|
+
} else if (tag === 26) {
|
|
41
|
+
const end2 = reader.uint32() + reader.pos;
|
|
42
|
+
while (reader.pos < end2) {
|
|
43
|
+
flame.neckPose.push(reader.float());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case 4:
|
|
48
|
+
if (tag === 37) {
|
|
49
|
+
flame.jawPose.push(reader.float());
|
|
50
|
+
} else if (tag === 34) {
|
|
51
|
+
const end2 = reader.uint32() + reader.pos;
|
|
52
|
+
while (reader.pos < end2) {
|
|
53
|
+
flame.jawPose.push(reader.float());
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case 5:
|
|
58
|
+
if (tag === 45) {
|
|
59
|
+
flame.eyePose.push(reader.float());
|
|
60
|
+
} else if (tag === 42) {
|
|
61
|
+
const end2 = reader.uint32() + reader.pos;
|
|
62
|
+
while (reader.pos < end2) {
|
|
63
|
+
flame.eyePose.push(reader.float());
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
case 6:
|
|
68
|
+
if (tag === 53) {
|
|
69
|
+
flame.eyeLid.push(reader.float());
|
|
70
|
+
} else if (tag === 50) {
|
|
71
|
+
const end2 = reader.uint32() + reader.pos;
|
|
72
|
+
while (reader.pos < end2) {
|
|
73
|
+
flame.eyeLid.push(reader.float());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 7:
|
|
78
|
+
if (tag === 61) {
|
|
79
|
+
flame.expression.push(reader.float());
|
|
80
|
+
} else if (tag === 58) {
|
|
81
|
+
const end2 = reader.uint32() + reader.pos;
|
|
82
|
+
while (reader.pos < end2) {
|
|
83
|
+
flame.expression.push(reader.float());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
reader.skip(tag & 7);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return flame;
|
|
92
|
+
}
|
|
93
|
+
function decodeFlameAnimation(reader, length) {
|
|
94
|
+
const end = reader.pos + length;
|
|
95
|
+
const animation = { keyframes: [] };
|
|
96
|
+
while (reader.pos < end) {
|
|
97
|
+
const tag = reader.uint32();
|
|
98
|
+
switch (tag >>> 3) {
|
|
99
|
+
case 1:
|
|
100
|
+
if (tag === 10) {
|
|
101
|
+
animation.keyframes.push(decodeFlame(reader, reader.uint32()));
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
reader.skip(tag & 7);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return animation;
|
|
109
|
+
}
|
|
110
|
+
function decodeServerResponseAnimation(reader, length) {
|
|
111
|
+
const end = reader.pos + length;
|
|
112
|
+
const response = {
|
|
113
|
+
connectionId: "",
|
|
114
|
+
reqId: "",
|
|
115
|
+
end: false,
|
|
116
|
+
animation: void 0
|
|
117
|
+
};
|
|
118
|
+
while (reader.pos < end) {
|
|
119
|
+
const tag = reader.uint32();
|
|
120
|
+
switch (tag >>> 3) {
|
|
121
|
+
case 1:
|
|
122
|
+
if (tag === 10) {
|
|
123
|
+
response.connectionId = reader.string();
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
case 2:
|
|
127
|
+
if (tag === 18) {
|
|
128
|
+
response.reqId = reader.string();
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case 3:
|
|
132
|
+
if (tag === 24) {
|
|
133
|
+
response.end = reader.bool();
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
case 4:
|
|
137
|
+
if (tag === 34) {
|
|
138
|
+
response.animation = decodeFlameAnimation(reader, reader.uint32());
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
reader.skip(tag & 7);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return response;
|
|
146
|
+
}
|
|
147
|
+
function decodeAnimationMessage(data) {
|
|
148
|
+
const reader = new BinaryReader(data);
|
|
149
|
+
const message = {
|
|
150
|
+
type: 0,
|
|
151
|
+
serverResponseAnimation: void 0
|
|
152
|
+
};
|
|
153
|
+
while (reader.pos < reader.len) {
|
|
154
|
+
const tag = reader.uint32();
|
|
155
|
+
switch (tag >>> 3) {
|
|
156
|
+
case 1:
|
|
157
|
+
if (tag === 8) {
|
|
158
|
+
message.type = reader.int32();
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
case 6:
|
|
162
|
+
if (tag === 50) {
|
|
163
|
+
message.serverResponseAnimation = decodeServerResponseAnimation(reader, reader.uint32());
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
default:
|
|
167
|
+
reader.skip(tag & 7);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return message;
|
|
171
|
+
}
|
|
172
|
+
function decodeAnimationKeyframes(protobufData) {
|
|
173
|
+
var _a, _b;
|
|
174
|
+
try {
|
|
175
|
+
const message = decodeAnimationMessage(new Uint8Array(protobufData));
|
|
176
|
+
if ((_b = (_a = message.serverResponseAnimation) == null ? void 0 : _a.animation) == null ? void 0 : _b.keyframes) {
|
|
177
|
+
return message.serverResponseAnimation.animation.keyframes;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
logger.error("proto", "Failed to decode animation keyframes:", error);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
decodeAnimationKeyframes,
|
|
187
|
+
decodeAnimationMessage
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=index8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index8.js","sources":["../src/proto/animation.ts"],"sourcesContent":["/**\n * Minimal protobuf definitions for animation data decoding.\n *\n * This is a stripped-down version containing only the types needed\n * to decode animation frames from the server response.\n *\n * Based on driveningress/v2/driveningress.proto\n *\n * @packageDocumentation\n */\n\nimport { BinaryReader } from '@bufbuild/protobuf/wire';\nimport { logger } from '../utils';\n\n/**\n * Flame - represents a single animation frame.\n * Contains FLAME model parameters for facial animation.\n * @internal\n */\nexport interface Flame {\n /** Translation parameters (3 floats: x, y, z) */\n translation: number[];\n /** Rotation parameters (quaternion or euler angles) */\n rotation: number[];\n /** Neck pose parameters */\n neckPose: number[];\n /** Jaw pose parameters */\n jawPose: number[];\n /** Eye pose parameters */\n eyePose: number[];\n /** Eyelid parameters */\n eyeLid: number[];\n /** Expression blend shape weights */\n expression: number[];\n}\n\n/**\n * FlameAnimation - contains keyframes.\n * @internal\n */\nexport interface FlameAnimation {\n keyframes: Flame[];\n}\n\n/**\n * ServerResponseAnimation - server response containing animation data.\n * @internal\n */\nexport interface ServerResponseAnimation {\n connectionId: string;\n reqId: string;\n end: boolean;\n animation?: FlameAnimation | undefined;\n}\n\n/**\n * AnimationMessage - top-level message wrapper.\n * @internal\n */\nexport interface AnimationMessage {\n type: number;\n serverResponseAnimation?: ServerResponseAnimation | undefined;\n}\n\n// ============== Decoders ==============\n\n/**\n * Decode a Flame message from protobuf.\n * @internal\n */\nfunction decodeFlame(reader: BinaryReader, length: number): Flame {\n const end = reader.pos + length;\n const flame: Flame = {\n translation: [],\n rotation: [],\n neckPose: [],\n jawPose: [],\n eyePose: [],\n eyeLid: [],\n expression: [],\n };\n\n while (reader.pos < end) {\n const tag = reader.uint32();\n switch (tag >>> 3) {\n case 1: // translation\n if (tag === 13) {\n flame.translation.push(reader.float());\n } else if (tag === 10) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.translation.push(reader.float());\n }\n }\n break;\n case 2: // rotation\n if (tag === 21) {\n flame.rotation.push(reader.float());\n } else if (tag === 18) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.rotation.push(reader.float());\n }\n }\n break;\n case 3: // neckPose\n if (tag === 29) {\n flame.neckPose.push(reader.float());\n } else if (tag === 26) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.neckPose.push(reader.float());\n }\n }\n break;\n case 4: // jawPose\n if (tag === 37) {\n flame.jawPose.push(reader.float());\n } else if (tag === 34) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.jawPose.push(reader.float());\n }\n }\n break;\n case 5: // eyePose\n if (tag === 45) {\n flame.eyePose.push(reader.float());\n } else if (tag === 42) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.eyePose.push(reader.float());\n }\n }\n break;\n case 6: // eyeLid\n if (tag === 53) {\n flame.eyeLid.push(reader.float());\n } else if (tag === 50) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.eyeLid.push(reader.float());\n }\n }\n break;\n case 7: // expression\n if (tag === 61) {\n flame.expression.push(reader.float());\n } else if (tag === 58) {\n const end2 = reader.uint32() + reader.pos;\n while (reader.pos < end2) {\n flame.expression.push(reader.float());\n }\n }\n break;\n default:\n reader.skip(tag & 7);\n }\n }\n\n return flame;\n}\n\n/**\n * Decode a FlameAnimation message from protobuf.\n * @internal\n */\nfunction decodeFlameAnimation(reader: BinaryReader, length: number): FlameAnimation {\n const end = reader.pos + length;\n const animation: FlameAnimation = { keyframes: [] };\n\n while (reader.pos < end) {\n const tag = reader.uint32();\n switch (tag >>> 3) {\n case 1: // keyframes\n if (tag === 10) {\n animation.keyframes.push(decodeFlame(reader, reader.uint32()));\n }\n break;\n default:\n reader.skip(tag & 7);\n }\n }\n\n return animation;\n}\n\n/**\n * Decode a ServerResponseAnimation message from protobuf.\n * @internal\n */\nfunction decodeServerResponseAnimation(reader: BinaryReader, length: number): ServerResponseAnimation {\n const end = reader.pos + length;\n const response: ServerResponseAnimation = {\n connectionId: \"\",\n reqId: \"\",\n end: false,\n animation: undefined,\n };\n\n while (reader.pos < end) {\n const tag = reader.uint32();\n switch (tag >>> 3) {\n case 1: // connectionId\n if (tag === 10) {\n response.connectionId = reader.string();\n }\n break;\n case 2: // reqId\n if (tag === 18) {\n response.reqId = reader.string();\n }\n break;\n case 3: // end\n if (tag === 24) {\n response.end = reader.bool();\n }\n break;\n case 4: // animation\n if (tag === 34) {\n response.animation = decodeFlameAnimation(reader, reader.uint32());\n }\n break;\n default:\n reader.skip(tag & 7);\n }\n }\n\n return response;\n}\n\n/**\n * Decode an AnimationMessage from protobuf bytes.\n * @param data - Raw protobuf bytes\n * @returns Decoded AnimationMessage\n * @internal\n */\nexport function decodeAnimationMessage(data: Uint8Array): AnimationMessage {\n const reader = new BinaryReader(data);\n const message: AnimationMessage = {\n type: 0,\n serverResponseAnimation: undefined,\n };\n\n while (reader.pos < reader.len) {\n const tag = reader.uint32();\n switch (tag >>> 3) {\n case 1: // type\n if (tag === 8) {\n message.type = reader.int32();\n }\n break;\n case 6: // serverResponseAnimation (field number 6 in the proto)\n if (tag === 50) {\n message.serverResponseAnimation = decodeServerResponseAnimation(reader, reader.uint32());\n }\n break;\n default:\n reader.skip(tag & 7);\n }\n }\n\n return message;\n}\n\n/**\n * Decode animation keyframes from protobuf data.\n * @param protobufData - Raw protobuf bytes\n * @returns Array of Flame keyframes, or null if decoding fails\n * @internal\n */\nexport function decodeAnimationKeyframes(protobufData: ArrayBuffer): Flame[] | null {\n try {\n const message = decodeAnimationMessage(new Uint8Array(protobufData));\n\n if (message.serverResponseAnimation?.animation?.keyframes) {\n return message.serverResponseAnimation.animation.keyframes;\n }\n\n return null;\n } catch (error) {\n logger.error('proto', 'Failed to decode animation keyframes:', error);\n return null;\n }\n}\n"],"names":[],"mappings":"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"}
|