@xmov/avatar 2.4.0 → 2.5.0-alpha.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.
Files changed (157) hide show
  1. package/README.md +34 -6
  2. package/package-dist/agent/avatar.cjs +1 -0
  3. package/package-dist/agent/avatar.modern.js +1 -0
  4. package/package-dist/agent/avatar.module.js +1 -0
  5. package/package-dist/agent/avatar.umd.js +1 -0
  6. package/package-dist/agent/index.d.ts +42 -0
  7. package/{dist/agent/types.d.ts → package-dist/agent/public-types.d.ts} +115 -116
  8. package/package-dist/index.cjs +3 -0
  9. package/package-dist/index.d.ts +34 -0
  10. package/package-dist/index.modern.js +1 -0
  11. package/package-dist/index.module.js +1 -0
  12. package/package-dist/index.umd.js +14 -0
  13. package/package-dist/public-types.d.ts +205 -0
  14. package/package.json +41 -33
  15. package/dist/agent/avatar.cjs +0 -1
  16. package/dist/agent/avatar.modern.js +0 -1
  17. package/dist/agent/avatar.module.js +0 -1
  18. package/dist/agent/avatar.umd.js +0 -1
  19. package/dist/agent/index.d.ts +0 -137
  20. package/dist/baseRender/AudioRenderer.d.ts +0 -46
  21. package/dist/baseRender/AudioWorklet.d.ts +0 -78
  22. package/dist/baseRender/AvatarRenderer.d.ts +0 -225
  23. package/dist/baseRender/MSEAudioPlayer.d.ts +0 -51
  24. package/dist/baseRender/UIRenderer.d.ts +0 -39
  25. package/dist/baseRender/pcm-audio-processor.d.ts +0 -63
  26. package/dist/control/APIForwarder.d.ts +0 -23
  27. package/dist/control/DataCacheQueue.d.ts +0 -69
  28. package/dist/control/EventDispatcher.d.ts +0 -35
  29. package/dist/control/RenderScheduler.d.ts +0 -171
  30. package/dist/control/SaveAndDownload.d.ts +0 -14
  31. package/dist/control/StreamingClient.d.ts +0 -33
  32. package/dist/control/VideoPlayer.d.ts +0 -47
  33. package/dist/control/ttsa.d.ts +0 -125
  34. package/dist/encoding/media-recorder-encoder.d.ts +0 -33
  35. package/dist/encoding/pcm-webm-encoder.d.ts +0 -13
  36. package/dist/encoding/webcodec-opus-encoder.d.ts +0 -41
  37. package/dist/encoding/webm-muxer.d.ts +0 -90
  38. package/dist/index.cjs +0 -2
  39. package/dist/index.cjs.map +0 -1
  40. package/dist/index.d.ts +0 -251
  41. package/dist/index.modern.js +0 -2
  42. package/dist/index.modern.js.map +0 -1
  43. package/dist/index.module.js +0 -2
  44. package/dist/index.module.js.map +0 -1
  45. package/dist/index.umd.js +0 -2
  46. package/dist/index.umd.js.map +0 -1
  47. package/dist/modules/Composition.d.ts +0 -28
  48. package/dist/modules/ResourceManager.d.ts +0 -299
  49. package/dist/modules/TrackRenderer/base-track.d.ts +0 -4
  50. package/dist/modules/TrackRenderer/index.d.ts +0 -12
  51. package/dist/modules/TrackRenderer/render-implements.d.ts +0 -19
  52. package/dist/modules/TrackRenderer/track-pic.d.ts +0 -8
  53. package/dist/modules/TrackRenderer/track-subtitle.d.ts +0 -8
  54. package/dist/modules/cache-manager.d.ts +0 -5
  55. package/dist/modules/decoder.d.ts +0 -84
  56. package/dist/modules/error-handle.d.ts +0 -17
  57. package/dist/modules/network.d.ts +0 -21
  58. package/dist/modules/sentry-reporter.d.ts +0 -58
  59. package/dist/proto/face_data_pb.d.ts +0 -0
  60. package/dist/types/capability.d.ts +0 -15
  61. package/dist/types/error.d.ts +0 -70
  62. package/dist/types/event.d.ts +0 -25
  63. package/dist/types/frame-data.d.ts +0 -107
  64. package/dist/types/index.d.ts +0 -196
  65. package/dist/types/render.d.ts +0 -18
  66. package/dist/utils/DataInterface.d.ts +0 -113
  67. package/dist/utils/GLDevice.d.ts +0 -46
  68. package/dist/utils/GLPipeline.d.ts +0 -84
  69. package/dist/utils/GLPipelineDebugTools.d.ts +0 -5
  70. package/dist/utils/Math.d.ts +0 -21
  71. package/dist/utils/__tests__/capability-checker.test.d.ts +0 -1
  72. package/dist/utils/audio-session.d.ts +0 -11
  73. package/dist/utils/audio.d.ts +0 -1
  74. package/dist/utils/capability-checker.d.ts +0 -28
  75. package/dist/utils/encodeToken.d.ts +0 -14
  76. package/dist/utils/face.d.ts +0 -1
  77. package/dist/utils/float32-decoder.d.ts +0 -26
  78. package/dist/utils/fpsTracker.d.ts +0 -27
  79. package/dist/utils/index.d.ts +0 -22
  80. package/dist/utils/logger.d.ts +0 -9
  81. package/dist/utils/media-recorder-timestamp.d.ts +0 -15
  82. package/dist/utils/pcmAnalyzer.d.ts +0 -40
  83. package/dist/utils/perfermance.d.ts +0 -15
  84. package/dist/utils/request.d.ts +0 -20
  85. package/dist/utils/requestAnimateFrames.d.ts +0 -31
  86. package/dist/utils/time.d.ts +0 -9
  87. package/dist/utils/video-probe-asset.d.ts +0 -28
  88. package/dist/view/DebugOverlay.d.ts +0 -54
  89. package/dist/worker/streaming-video.d.ts +0 -1
  90. package/src/baseRender/AudioRenderer.ts +0 -328
  91. package/src/baseRender/AudioWorklet.ts +0 -776
  92. package/src/baseRender/AvatarRenderer.ts +0 -1550
  93. package/src/baseRender/MSEAudioPlayer.ts +0 -254
  94. package/src/baseRender/UIRenderer.ts +0 -195
  95. package/src/baseRender/pcm-audio-processor.js +0 -267
  96. package/src/control/APIForwarder.ts +0 -67
  97. package/src/control/DataCacheQueue.ts +0 -372
  98. package/src/control/EventDispatcher.ts +0 -106
  99. package/src/control/RenderScheduler.ts +0 -1000
  100. package/src/control/SaveAndDownload.js +0 -77
  101. package/src/control/StreamingClient.ts +0 -138
  102. package/src/control/VideoPlayer.ts +0 -306
  103. package/src/control/ttsa.ts +0 -754
  104. package/src/encoding/media-recorder-encoder.ts +0 -175
  105. package/src/encoding/pcm-webm-encoder.ts +0 -72
  106. package/src/encoding/webcodec-opus-encoder.ts +0 -258
  107. package/src/encoding/webm-muxer.ts +0 -337
  108. package/src/global.d.ts +0 -112
  109. package/src/index.ts +0 -2053
  110. package/src/modules/Composition.ts +0 -79
  111. package/src/modules/ResourceManager.ts +0 -926
  112. package/src/modules/TrackRenderer/base-track.ts +0 -7
  113. package/src/modules/TrackRenderer/index.ts +0 -40
  114. package/src/modules/TrackRenderer/render-implements.ts +0 -269
  115. package/src/modules/TrackRenderer/track-pic.ts +0 -19
  116. package/src/modules/TrackRenderer/track-subtitle.ts +0 -16
  117. package/src/modules/cache-manager.ts +0 -10
  118. package/src/modules/decoder.ts +0 -548
  119. package/src/modules/error-handle.ts +0 -20
  120. package/src/modules/network.ts +0 -71
  121. package/src/modules/sentry-reporter.ts +0 -171
  122. package/src/proto/face_data.proto +0 -45
  123. package/src/proto/face_data_pb.js +0 -1297
  124. package/src/proto/protobuf.min.js +0 -8
  125. package/src/types/capability.ts +0 -24
  126. package/src/types/error.ts +0 -211
  127. package/src/types/event.ts +0 -38
  128. package/src/types/frame-data.ts +0 -121
  129. package/src/types/index.ts +0 -220
  130. package/src/types/render.ts +0 -18
  131. package/src/utils/DataInterface.ts +0 -746
  132. package/src/utils/GLDevice.ts +0 -286
  133. package/src/utils/GLPipeline.ts +0 -1293
  134. package/src/utils/GLPipelineDebugTools.ts +0 -174
  135. package/src/utils/Math.ts +0 -266
  136. package/src/utils/__tests__/capability-checker.test.ts +0 -159
  137. package/src/utils/audio-session.ts +0 -144
  138. package/src/utils/audio.ts +0 -44
  139. package/src/utils/blueimp-md5.d.ts +0 -3
  140. package/src/utils/capability-checker.ts +0 -762
  141. package/src/utils/encodeToken.ts +0 -115
  142. package/src/utils/face.ts +0 -29
  143. package/src/utils/float32-decoder.js +0 -130
  144. package/src/utils/fpsTracker.ts +0 -100
  145. package/src/utils/index.ts +0 -182
  146. package/src/utils/logger.js +0 -36
  147. package/src/utils/media-recorder-timestamp.ts +0 -79
  148. package/src/utils/pcmAnalyzer.ts +0 -154
  149. package/src/utils/perfermance.ts +0 -141
  150. package/src/utils/request.ts +0 -107
  151. package/src/utils/requestAnimateFrames.ts +0 -195
  152. package/src/utils/time.ts +0 -32
  153. package/src/utils/video-probe-asset.ts +0 -99
  154. package/src/view/DebugOverlay.ts +0 -409
  155. package/src/worker/streaming-video.ts +0 -4647
  156. /package/{dist → package-dist}/agent/index.cjs +0 -0
  157. /package/{dist → package-dist}/agent/index.umd.js +0 -0
@@ -1,548 +0,0 @@
1
- import { bodyFile, IRawBodyFrameData } from "../types/frame-data";
2
- import { workerURL } from "../worker/streaming-video";
3
- import ResourceManager, { IOfflineIdle } from "./ResourceManager";
4
- import { performanceConstant } from "../utils/perfermance";
5
- import SaveAndDownload from "../control/SaveAndDownload";
6
- import { DataCacheQueue } from "../control/DataCacheQueue";
7
- import { EErrorCode } from "../types/error";
8
- import { parseUint8ToFloat32 } from "../utils/float32-decoder";
9
-
10
-
11
- export default class ParallelDecoder {
12
- private tasks: Map<
13
- string,
14
- {
15
- worker: Worker;
16
- status: string;
17
- onMessage: (e: MessageEvent) => void;
18
- ef?: number;
19
- }
20
- >;
21
- private queue: Array<IRawBodyFrameData>;
22
- private maxParallel: number;
23
- private currentParallel: number;
24
- private _locked: boolean;
25
- private currentTaskId: string | null;
26
- private resourceManager: ResourceManager;
27
- private onFrame: (file: bodyFile, frame: any, index: number) => void;
28
- private onDone: (file: bodyFile, id: string) => void;
29
- private isFirstDecode: boolean;
30
- private pendingAbort: boolean = false;
31
- private abortAfterEf: number | null = null;
32
- private pendingNewQueue: Array<IRawBodyFrameData> | null = null;
33
- private currentDecodedFrameIndex: number | null = null;
34
- private abortAfterFrame: number | null = null;
35
- private cacheVideoCount: number = 5; // 缓存视频数量
36
- private saveAndDownload: SaveAndDownload;
37
- private dataCacheQueue: DataCacheQueue;
38
- private bodyFrameCountMap: Map<string, number>;
39
- private maxVideoCount: number = 2;
40
- private hardwareAcceleration: string = "default";
41
- private MAX_LOAD_VIDEO_TIMEOUT_MS = 2000; // 最大加载视频超时时间(毫秒)
42
-
43
- _offlineLastFrame = 0;
44
- _offlineIdle: IOfflineIdle[] = [];
45
- _offlineIdleIndex = 0;
46
- firstDecodeFrame: boolean = true;
47
- reportMessage: (message: {
48
- code: EErrorCode;
49
- message: string;
50
- e?: object;
51
- }) => void;
52
-
53
- constructor(options: {
54
- hardwareAcceleration: string;
55
- resourceManager: ResourceManager;
56
- saveAndDownload: SaveAndDownload;
57
- dataCacheQueue: DataCacheQueue;
58
- reportMessage: (message: {
59
- code: EErrorCode;
60
- message: string;
61
- e?: object;
62
- }) => void;
63
- }) {
64
- this.tasks = new Map();
65
- this.queue = [];
66
- this.maxParallel = 1;
67
- this.currentParallel = 0;
68
- this._locked = false; // 并发保护锁
69
- this.currentTaskId = null;
70
- this.resourceManager = options.resourceManager;
71
- this.onFrame = () => {};
72
- this.onDone = () => {};
73
- this.isFirstDecode = true;
74
- this.saveAndDownload = options.saveAndDownload;
75
- this.dataCacheQueue = options.dataCacheQueue;
76
- this.bodyFrameCountMap = new Map();
77
- this.hardwareAcceleration = options.hardwareAcceleration;
78
- this.reportMessage = options.reportMessage;
79
- }
80
-
81
- getRandomTaskId() {
82
- return Date.now() + "_" + Math.random().toString(36).slice(2, 10);
83
- }
84
-
85
- decode(
86
- files: Array<IRawBodyFrameData>,
87
- onFrame: (file: bodyFile, frame: any, index: number) => void,
88
- ) {
89
- if (this.isFirstDecode) {
90
- if (this._locked) return; // 并发保护
91
- this.abort(); // 清理所有旧任务
92
- this._locked = true;
93
- this.queue = files; // 拷贝一份队列
94
- this.onFrame = (file, frame, index) => {
95
- this.currentDecodedFrameIndex = file.startFrameIndex + index;
96
- this._handlePendingAbort(file, index);
97
- onFrame(file, frame, index);
98
- };
99
- this.currentParallel = 0;
100
- this.currentTaskId = this.getRandomTaskId(); // 唯一任务ID
101
- this.isFirstDecode = false;
102
- } else {
103
- this.updateQueue(files);
104
- }
105
- }
106
-
107
- clearOldQueue(frame: number) {
108
- this.queue = this.queue.filter((item) => item.sf >= frame);
109
- }
110
-
111
- updateQueue(files: Array<IRawBodyFrameData>) {
112
- this.abortAfterFrame = null;
113
- if (this.queue.length === 0) {
114
- this.queue.push(...files);
115
- // (window as any).avatarSDKLogger.log("队列为空,直接追加", JSON.stringify(this.getQueue()));
116
- return;
117
- }
118
-
119
- const lastQueueEf = this.queue[this.queue.length - 1].ef;
120
- const firstQueueSf = this.queue[0].sf;
121
- const newSf = files[0].sf;
122
- // (window as any).avatarSDKLogger.log("开始校验视频队列,目前队列", JSON.stringify(this.getQueue()));
123
-
124
- if (newSf >= lastQueueEf) {
125
- // 顺序追加
126
- this.queue.push(...files);
127
- // (window as any).avatarSDKLogger.log("顺序追加结果", JSON.stringify(this.getQueue()));
128
- } else if (newSf < firstQueueSf) {
129
- // 回退/seek,判断是否需要等待
130
- if (
131
- this.currentDecodedFrameIndex !== null &&
132
- newSf > this.currentDecodedFrameIndex + 1
133
- ) {
134
- // 有间隔,需要等待
135
- this.abortAfterFrame = newSf - 1;
136
- // 找到数据中ef小于abortAfterFrame的帧
137
- const oldQueue = this.queue.filter((item) => item.ef < newSf);
138
- this.queue = oldQueue.concat(files);
139
- // (window as any).avatarSDKLogger.log("等待处理到abortAfterFrame后再abort", this.abortAfterFrame, this.pendingNewQueue);
140
- return;
141
- } else {
142
- // 没有间隔,直接abort
143
- this.abort();
144
- this.queue = files.slice();
145
- this._locked = true;
146
- this.currentTaskId = this.getRandomTaskId();
147
- // (window as any).avatarSDKLogger.log("回退/seek,重置队列", JSON.stringify(this.getQueue()));
148
- // 清除队列中sf小于newsf的帧
149
- const newQueueSf = this.queue[0].sf;
150
- this.dataCacheQueue.clearOldFrames(newQueueSf);
151
- }
152
- } else if (newSf === firstQueueSf) {
153
- this.queue = files.slice();
154
- const newQueueSf = this.queue[0].sf;
155
- this.dataCacheQueue.clearOldFrames(newQueueSf);
156
- // (window as any).avatarSDKLogger.log("重置队列", JSON.stringify(this.getQueue()));
157
- } else {
158
- // 在队列中间,找到重叠点,截断再追加
159
- const mp4Index = this.queue.findIndex((item) => item.sf >= newSf);
160
- if (mp4Index !== -1) {
161
- this.queue.length = mp4Index;
162
- }
163
- this.abortAfterFrame = newSf-1;
164
- this.queue.push(...files);
165
- // (window as any).avatarSDKLogger.log("中间追加,截断后追加", JSON.stringify(this.getQueue()));
166
- }
167
- }
168
- _isIOS() {
169
- return /iPad|iPhone|iPod|iOS/.test(navigator.userAgent);
170
- }
171
-
172
- getQueue() {
173
- return this.queue.map(item => ({
174
- ...item,
175
- x_offset: []
176
- }));
177
- }
178
-
179
- // 定义超时函数:超过指定时间后 reject
180
- timeoutPromise(ms: number, reason = "请求超时") {
181
- return new Promise((_, reject) => {
182
- setTimeout(() => {
183
- // (window as any).avatarSDKLogger.error(reason);
184
- reject();
185
- }, ms);
186
- });
187
- }
188
-
189
- // 加载视频并添加超时控制(800ms)
190
- async loadVideoWithTimeout(videoKey: string): Promise<ArrayBuffer | null> {
191
- try {
192
- // Promise.race:谁先完成就取谁的结果(加载成功 或 超时)
193
- const arrayBuffer = await Promise.race([
194
- this.resourceManager.loadVideo(videoKey), // 原加载逻辑
195
- this.timeoutPromise(this.MAX_LOAD_VIDEO_TIMEOUT_MS, `视频 ${videoKey} 加载超时(${this.MAX_LOAD_VIDEO_TIMEOUT_MS}ms)`) // 800ms 超时
196
- ]);
197
- return arrayBuffer as ArrayBuffer; // 加载成功,返回 ArrayBuffer
198
- } catch (error) {
199
- return null; // 超时或失败,返回 null 标识
200
- }
201
- }
202
-
203
- async _tryStartNext() {
204
- // 新增:判断当前队列是否包含回退视频(sf < 当前已解码帧索引)
205
- const hasBackwardVideo = this.queue?.some(item =>
206
- this.currentDecodedFrameIndex !== null && item.sf < this.currentDecodedFrameIndex
207
- );
208
- // 缓存数量判断:回退场景下放宽限制(允许多1个)
209
- let maxAllowed: number;
210
-
211
- if (hasBackwardVideo) {
212
- // 回退时:iOS 保持原限制,其他平台临时+1
213
- maxAllowed = this._isIOS() ? this.maxVideoCount : this.maxVideoCount + 1;
214
- } else {
215
- // 非回退时:iOS 限制为1,其他平台用默认最大数量
216
- maxAllowed = this._isIOS() ? 1 : this.maxVideoCount;
217
- }
218
- if (this.dataCacheQueue.getBodyVideoNameListLength() >= maxAllowed) {
219
- return;
220
- }
221
- if (this.currentParallel < this.maxParallel && this.queue.length > 0) {
222
- const bodyInfo = this.queue.shift() as IRawBodyFrameData;
223
- this.saveAndDownload.appendMultipleToArray("videoData", [{
224
- ...bodyInfo,
225
- x_offset: parseUint8ToFloat32(bodyInfo.x_offset) || []
226
- }]);
227
- this._cleanupOldWorkers(bodyInfo.body_id ?? bodyInfo.id ?? 0);
228
- this.currentParallel++;
229
- (window as any).performanceTracker.markStart(
230
- performanceConstant.load_video,
231
- bodyInfo
232
- );
233
- if(!bodyInfo.n) {
234
- this.currentParallel--;
235
- this._tryStartNext();
236
- this.reportMessage({
237
- code: EErrorCode.INVALID_BODY_NAME,
238
- message: `body数据无Name: ${JSON.stringify(bodyInfo)}`,
239
- });
240
- return;
241
- }
242
- // 加载视频并添加超时控制
243
- const arrayBuffer = (await this.loadVideoWithTimeout(
244
- bodyInfo.n
245
- )) as ArrayBuffer;
246
- if(!arrayBuffer) {
247
- this.currentParallel--;
248
- this._tryStartNext();
249
- return;
250
- }
251
- (window as any).performanceTracker.markEnd(
252
- performanceConstant.load_video,
253
- bodyInfo
254
- );
255
- (window as any).performanceTracker.markStart(
256
- performanceConstant.decode_video,
257
- bodyInfo
258
- );
259
-
260
- (window as any).performanceTracker.markStart(
261
- performanceConstant.first_decode_video,
262
- bodyInfo
263
- );
264
- this._startWorker({
265
- name: bodyInfo.n,
266
- id: bodyInfo.id,
267
- frameState: bodyInfo.s,
268
- start: bodyInfo.asf,
269
- end: bodyInfo.aef,
270
- hfd: bodyInfo.hfd,
271
- data: arrayBuffer,
272
- startFrameIndex: bodyInfo.sf,
273
- endFrameIndex: bodyInfo.ef,
274
- body_id: bodyInfo.body_id,
275
- x_offset: parseUint8ToFloat32(bodyInfo.x_offset) || []
276
- });
277
- }
278
- }
279
-
280
- _startWorker(file: bodyFile) {
281
- const taskId = this.currentTaskId;
282
- let worker: Worker;
283
- try {
284
- worker = new Worker(workerURL);
285
- } catch (err) {
286
- this.reportMessage({
287
- code: EErrorCode.INIT_WORKER_ERROR,
288
- message: '初始化视频抽帧Worker失败',
289
- e: err as object,
290
- });
291
- return;
292
- }
293
- const id = `${file.body_id ?? file.id ?? 0}_${file.name}`;
294
- const data = file.data;
295
- const dataCopy = new ArrayBuffer(data.byteLength);
296
- new Uint8Array(dataCopy).set(new Uint8Array(data));
297
-
298
- // 监听 Worker 运行时错误(脚本加载/解析失败等)
299
- worker.addEventListener("error", (err) => {
300
- if (taskId !== this.currentTaskId) return;
301
- this.reportMessage({
302
- code: EErrorCode.INIT_WORKER_ERROR,
303
- message: '视频抽帧Worker运行时错误',
304
- e: err as object,
305
- });
306
- });
307
-
308
- const onMessage = (e: MessageEvent) => {
309
- if (taskId !== this.currentTaskId) return; // 只处理当前任务
310
- if (e.data.type === "frame") {
311
- if(this.firstDecodeFrame) {
312
- this.firstDecodeFrame = false;
313
- (window as any).performanceTracker.markEnd(
314
- performanceConstant.first_decode_video,
315
- file
316
- );
317
- }
318
- if(e.data.index < file.start || e.data.index > file.end) {
319
- e.data.frame.close();
320
- } else {
321
- this.cacheVideo();
322
- this.onFrame(file, e.data.frame, e.data.index);
323
- const bodyFrameCount = this.bodyFrameCountMap.get(file.name) ?? 0
324
- this.bodyFrameCountMap.set(file.name, bodyFrameCount + 1)
325
- if (this.abortAfterFrame && e.data.index + file.startFrameIndex >= this.abortAfterFrame) {
326
- (window as any).avatarSDKLogger.log("abortAfterFrame", this.abortAfterFrame, file)
327
- this.abortOne(id);
328
- this.abortAfterFrame = null;
329
- this._tryStartNext();
330
- }
331
- }
332
- } else if(e.data.type === "configSupport") {
333
- (window as any).avatarSDKLogger.log("configSupport", e.data);
334
- }else if (e.data.type === "done") {
335
- (window as any).performanceTracker.markEnd(
336
- performanceConstant.decode_video,
337
- file
338
- );
339
- const bodyFrameCount = this.bodyFrameCountMap.get(file.name) ?? 0
340
- if(bodyFrameCount < file.end- file.start - 1) {
341
- this.bodyFrameCountMap.delete(file.name)
342
- }
343
- const task = this.tasks.get(id);
344
- if (task) {
345
- task.status = "done";
346
- task.worker.postMessage({ type: "abort" });
347
- }
348
- this.currentParallel--;
349
- this.onDone && this.onDone(file, id);
350
- worker.removeEventListener("message", onMessage);
351
- worker.terminate();
352
- this.tasks.delete(id);
353
- if (this.currentParallel === 0 && this.queue.length === 0) {
354
- this._locked = false; // 解锁
355
- }
356
- this._tryStartNext();
357
- } else if (e.data.type === "error") {
358
- // 根据 Worker 上报的 error 内容区分错误码
359
- // Worker 中有两处 postMessage({ type: 'error' }):
360
- // 1. VideoDecoder error 回调 → 视频抽帧错误 (VIDEO_FRAME_EXTRACT_ERROR)
361
- // 2. 解码配置不支持 → 视频流处理错误 (PROCESS_VIDEO_STREAM_ERROR)
362
- const errorMsg = e.data.error ?? '';
363
- const code = errorMsg.includes('不支持')
364
- ? EErrorCode.PROCESS_VIDEO_STREAM_ERROR
365
- : EErrorCode.VIDEO_FRAME_EXTRACT_ERROR;
366
- this.reportMessage({
367
- code,
368
- message: errorMsg || '视频解码错误',
369
- e: { fileName: file.name, error: errorMsg } as object,
370
- });
371
- const task = this.tasks.get(id);
372
- if (task) {
373
- task.status = "error";
374
- task.worker.postMessage({ type: "abort" });
375
- }
376
- this.currentParallel--;
377
- this.onDone && this.onDone(file, id);
378
- worker.removeEventListener("message", onMessage);
379
- worker.terminate();
380
- this.tasks.delete(id);
381
- if (this.currentParallel === 0 && this.queue.length === 0) {
382
- this._locked = false; // 解锁
383
- }
384
- }
385
- };
386
- worker.addEventListener("message", onMessage);
387
- this.tasks.set(id, {
388
- worker,
389
- status: "decoding",
390
- onMessage,
391
- ef: file.endFrameIndex,
392
- });
393
- (window as any).avatarSDKLogger.log("start decode", file.name, file.startFrameIndex, file.endFrameIndex)
394
- worker.postMessage(
395
- {
396
- type: "decode",
397
- file: { data: dataCopy },
398
- start: file.start,
399
- end: file.end,
400
- hardwareAcceleration: this.hardwareAcceleration,
401
- taskId,
402
- },
403
- [dataCopy]
404
- );
405
- }
406
-
407
- // 在当前视频解出第一帧后,缓存队列中的前cacheVideoCount个视频(包括当前视频)
408
- // 首次调用会缓存当前视频及后续cacheVideoCount-1个视频
409
- // 后续每次调用会重新请求前cacheVideoCount个视频(已缓存的视频会被loadVideo内部过滤)
410
- cacheVideo() {
411
- const cacheQueue = this.queue.slice(0, this.cacheVideoCount);
412
- for (const item of cacheQueue) {
413
- this.resourceManager.preloadVideo(item.n);
414
- }
415
- }
416
-
417
- abort() {
418
- this.tasks.forEach(({ worker, status, onMessage }) => {
419
- if (status === "decoding") {
420
- worker.postMessage({ type: "abort" });
421
- if (onMessage) worker.removeEventListener("message", onMessage);
422
- worker.terminate();
423
- }
424
- });
425
- this.tasks.clear();
426
- this.queue = [];
427
- this.currentParallel = 0;
428
- this._locked = false; // 解锁
429
- this.currentTaskId = null;
430
- }
431
-
432
- abortOne(id: string) {
433
- const task = this.tasks.get(id);
434
- if (task && task.status === "decoding") {
435
- task?.worker.postMessage({ type: "abort" });
436
- if (task?.onMessage) {
437
- task.worker.removeEventListener("message", task.onMessage);
438
- }
439
- task?.worker.terminate();
440
- this.tasks.delete(id);
441
- this.currentParallel--;
442
- if (this.currentParallel === 0 && this.queue.length === 0) {
443
- this._locked = false; // 解锁
444
- }
445
- }
446
- }
447
-
448
- destroy() {
449
- this.abort();
450
- this.queue = [];
451
- this.currentParallel = 0;
452
- this._locked = false;
453
- this.currentTaskId = null;
454
- this.pendingAbort = false;
455
- this.abortAfterEf = null;
456
- this.pendingNewQueue = null;
457
- this.abortAfterFrame = null;
458
- this.currentDecodedFrameIndex = null;
459
- this.firstDecodeFrame = true;
460
- }
461
-
462
- _handlePendingAbort(file: bodyFile, index: number) {
463
- if (this.pendingAbort && this.abortAfterEf !== null) {
464
- const currentFrameIndex = file.startFrameIndex + index;
465
- if (currentFrameIndex >= this.abortAfterEf) {
466
- this.abort();
467
- this.queue = this.pendingNewQueue!;
468
- this._locked = true;
469
- this.currentTaskId = this.getRandomTaskId();
470
- // 重置等待标志
471
- this.pendingAbort = false;
472
- this.abortAfterEf = null;
473
- this.pendingNewQueue = null;
474
- }
475
- }
476
- }
477
-
478
- private _cleanupOldWorkers(newBodyId: number) {
479
- for (const [taskId, task] of this.tasks) {
480
- // taskId 格式如 '143_stand_by_unface03_cut_011'
481
- const [bodyIdStr] = taskId.split("_");
482
- const bodyId = parseInt(bodyIdStr, 10);
483
- if (!isNaN(bodyId) && bodyId < newBodyId) {
484
- task.worker.postMessage({ type: "abort" });
485
- if (task.onMessage)
486
- task.worker.removeEventListener("message", task.onMessage);
487
- task.worker.terminate();
488
- this.tasks.delete(taskId);
489
- }
490
- }
491
- }
492
- syncDecode(currentFrameIndex: number) {
493
- // 直接打断当前解帧的视频,理论上1s,很小概率用户会切换回来。
494
- const mp4List = this.queue.filter((item) => item.ef > currentFrameIndex);
495
- if (mp4List.length === 0) {
496
- return
497
- }
498
- this.abort();
499
- // 如果上述条件不满足,则打断当前处理中的任务,根据currentFrameIndex获取queue中ef>currentFrameIndex的queue,重新装填到解帧队列,开始解帧
500
- this.queue = mp4List;
501
- this._locked = true;
502
- this.currentTaskId = this.getRandomTaskId();
503
- this._tryStartNext();
504
- }
505
-
506
- /** 进入正常模式 */
507
- _reload() {
508
- this._offlineLastFrame = 0
509
- this.queue = []
510
- }
511
- /** 进入离线,清空剩余身体数据 */
512
- _offLineMode(offlineIdle: IOfflineIdle[], currentFrame: number) {
513
- this.abort()
514
- this._offlineLastFrame = currentFrame
515
- this._offlineIdle = offlineIdle
516
- this.currentParallel -= 1;
517
- this.queue = []
518
- this._offlineRun()
519
- }
520
- _offlineRun() {
521
- const idle = this._offlineIdle[this._offlineIdleIndex]
522
- if (idle) {
523
- this._offlineIdleIndex += 1
524
- this._putOfflineBodyData(idle)
525
- } else {
526
- this._offlineIdleIndex = 0
527
- }
528
- }
529
- /** 设置 idle 视频 */
530
- _putOfflineBodyData(idle: IOfflineIdle) {
531
- const delta = idle.aef - idle.asf
532
-
533
- const bodyInfo = {
534
- id: -1,
535
- n: idle.n,
536
- s: 'idle',
537
- hfd: false,
538
- body_id: -1,
539
- asf: idle.asf,
540
- aef: idle.aef,
541
- sf: this._offlineLastFrame,
542
- ef: this._offlineLastFrame + delta,
543
- x_offset: new Uint8Array(),
544
- }
545
- this.queue.push(bodyInfo)
546
- this._offlineLastFrame = bodyInfo.ef
547
- }
548
- }
@@ -1,20 +0,0 @@
1
- import { EErrorCode, ERROR_CODE_MAP, ERROR_NAME_MAP } from "../types/error";
2
-
3
- export default function Errors(params: {
4
- code: EErrorCode
5
- message: string
6
- e?: any
7
- session_id?: string
8
- client_speak_id?: string
9
- }) {
10
- return {
11
- code: params.code,
12
- error_code: ERROR_CODE_MAP[params.code] ?? params.code,
13
- error_name: ERROR_NAME_MAP[params.code] ?? 'UNKNOWN_ERROR',
14
- message: params.message,
15
- session_id: params.session_id || '',
16
- client_speak_id: params.client_speak_id,
17
- timestamp: Date.now(),
18
- originalError: params.e ? JSON.stringify(params.e) : null,
19
- }
20
- }
@@ -1,71 +0,0 @@
1
- interface NetworkOptions {
2
- offlineCallback(message?: string): void
3
- onlineCallback(message?: string): void
4
- // retryCallback(count: number): Promise<void>
5
- }
6
-
7
- export default class NetworkMonitor {
8
- private TAG = "[NetworkMonitor]";
9
- static ONLINE = true;
10
- private readonly options: NetworkOptions;
11
- private retryCount = 1;
12
- private retryTimer = -1;
13
- private bindHandleOnline: () => void;
14
- private bindHandleOffline: () => void;
15
- public constructor(options: NetworkOptions) {
16
- this.options = options;
17
- this.bindHandleOnline = this._handleOnline.bind(this);
18
- this.bindHandleOffline = this._handleOffline.bind(this);
19
- this._initEventListeners();
20
- }
21
-
22
- private _initEventListeners(): void {
23
- window.addEventListener('online', this.bindHandleOnline);
24
- window.addEventListener('offline', this.bindHandleOffline);
25
- }
26
-
27
- private _handleOnline(): void {
28
- window.clearTimeout(this.retryTimer)
29
- NetworkMonitor.ONLINE = true
30
- this.options.onlineCallback('[NetworkMonitor] 网络已恢复');
31
- }
32
-
33
- private _handleOffline(): void {
34
- NetworkMonitor.ONLINE = false
35
- this.options.offlineCallback('[NetworkMonitor] 网络已断开');
36
- this._attempt();
37
- }
38
-
39
- private _attempt(): void {
40
- let delay = 1000
41
- if (this.retryCount > 1) {
42
- delay = Math.pow(2, this.retryCount) * 1000;
43
- }
44
- this.retryTimer = window.setTimeout(() => {
45
- // this.options.retryCallback(this.retryCount)
46
- // .then((result) => {
47
- // if (result === null) {
48
- // this.retryCount += 1;
49
- // this._attempt();
50
- // } else {
51
- // this._handleOnline()
52
- // }
53
- // })
54
- // .catch(() => {
55
- // this.retryCount += 1;
56
- // this._attempt();
57
- // });
58
- }, delay);
59
- };
60
-
61
- setState(isOnline: boolean) {
62
- NetworkMonitor.ONLINE = isOnline;
63
- }
64
-
65
- destroy() {
66
- window.removeEventListener('online', this.bindHandleOnline);
67
- window.removeEventListener('offline', this.bindHandleOffline);
68
- this.retryTimer = -1;
69
- this.retryCount = 1;
70
- }
71
- }