@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,27 +0,0 @@
1
- export interface FPSStats {
2
- current: number;
3
- average: number;
4
- min: number;
5
- max: number;
6
- droppedFrames: number;
7
- totalFrames: number;
8
- }
9
- export declare class FPSTracker {
10
- private frameTimestamps;
11
- private maxSamples;
12
- private lastFrameTime;
13
- private frameCount;
14
- private lastUpdateTime;
15
- private currentFPS;
16
- private totalFrames;
17
- private totalTime;
18
- private droppedFrames;
19
- private totalRenderedFrames;
20
- constructor(maxSamples?: number);
21
- tick(rendered?: boolean): void;
22
- getStats(): FPSStats;
23
- getCurrentFPS(): number;
24
- getAverageFPS(): number;
25
- reset(): void;
26
- getDroppedFrames(): number;
27
- }
@@ -1,22 +0,0 @@
1
- export declare function isDEV(): boolean;
2
- export declare function isSupportMES(): boolean;
3
- export declare function getStyleStr(styles: Record<string, any>): string;
4
- export declare function updateCanvasXOffset(canvas: any, offset_PX: any): void;
5
- export declare function generateFingerprint(): string;
6
- export declare function getDeviceInfo(): {
7
- device_id: string;
8
- device_brand: string;
9
- device_model: string;
10
- os_version: string;
11
- browser: string;
12
- browser_version: string;
13
- browser_ua: string;
14
- };
15
- /**
16
- * 校验scale格式(支持小数)并返回合法值
17
- * @param {unknown} scale - 待校验的scale值
18
- * @param {string} tag - 日志标签
19
- * @returns {number | string} 合法值,非法返回1
20
- */
21
- export declare function validateScaleWithDecimal(scale: unknown, tag: string): number | string;
22
- export declare function isharmony(): boolean;
@@ -1,9 +0,0 @@
1
- declare function executeLog(consoleMethod: any, ...args: any[]): void;
2
- declare function setLoggingEnabled(enabled: any): void;
3
- declare let isLoggingEnabled: boolean;
4
- declare namespace sdkLog {
5
- function log(...args: any[]): void;
6
- function info(...args: any[]): void;
7
- function warn(...args: any[]): void;
8
- function error(...args: any[]): void;
9
- }
@@ -1,15 +0,0 @@
1
- type EpochClock = () => number;
2
- /** Normalizes browser-specific BlobEvent.timecode units to epoch milliseconds. */
3
- export declare class MediaRecorderTimestamp {
4
- private readonly chunkMs;
5
- private readonly startedAt;
6
- private readonly now;
7
- private timecodeScale;
8
- private firstTimecode;
9
- private lastTimecode;
10
- private lastObservedAt;
11
- private lastTimestamp;
12
- constructor(chunkMs: number, now?: EpochClock);
13
- resolve(timecode: number): number;
14
- }
15
- export {};
@@ -1,40 +0,0 @@
1
- /**
2
- * PCM 音频参数分析工具
3
- * 用于检测和分析 PCM 音频数据的基本参数
4
- */
5
- export interface PCMAudioParams {
6
- sampleRate: number;
7
- channels: number;
8
- bitDepth: number;
9
- format: string;
10
- encoding: string;
11
- bytesPerSample: number;
12
- sampleCount?: number;
13
- duration?: number;
14
- }
15
- /**
16
- * 分析 PCM 音频数据的基本参数
17
- * @param pcmData PCM 音频数据(Uint8Array)
18
- * @param knownSampleRate 已知的采样率(可选,如果不提供则使用默认值 24000)
19
- * @returns PCM 音频参数
20
- */
21
- export declare function analyzePCMParams(pcmData?: Uint8Array, knownSampleRate?: number): PCMAudioParams;
22
- /**
23
- * 检测 PCM 数据的可能参数(通过分析数据特征)
24
- * 注意:这种方法只能提供推测,最准确的参数应该从服务器配置中获取
25
- * @param pcmData PCM 音频数据
26
- * @returns 检测到的参数(可能不准确)
27
- */
28
- export declare function detectPCMParams(pcmData: Uint8Array): Partial<PCMAudioParams>;
29
- /**
30
- * 格式化显示 PCM 参数
31
- * @param params PCM 音频参数
32
- * @returns 格式化的字符串
33
- */
34
- export declare function formatPCMParams(params: PCMAudioParams): string;
35
- /**
36
- * 从音频渲染器获取当前 PCM 配置
37
- * @param audioRenderer 音频渲染器实例
38
- * @returns PCM 音频参数
39
- */
40
- export declare function getPCMParamsFromRenderer(audioRenderer: any): PCMAudioParams;
@@ -1,15 +0,0 @@
1
- export declare const performanceConstant: {
2
- load_resource: string;
3
- ttsa_connect: string;
4
- ttsa_ready: string;
5
- first_avatar_render: string;
6
- first_webgl_render: string;
7
- ttsa_body_res: string;
8
- start_action_res: string;
9
- start_action_render: string;
10
- load_video: string;
11
- decode_video: string;
12
- voice_response_play: string;
13
- first_decode_video: string;
14
- first_render: string;
15
- };
@@ -1,20 +0,0 @@
1
- type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD';
2
- interface RequestOptions {
3
- method?: HttpMethod;
4
- data?: Record<string, any>;
5
- headers?: Record<string, string>;
6
- }
7
- export default function request(url: string, options?: RequestOptions): Promise<Response>;
8
- /**
9
- * 资源下载请求
10
- * @returns {Promise<[isError: boolean, data: ArrayBuffer | string]>}
11
- * */
12
- export declare function XMLRequest(options: {
13
- url: string;
14
- cache?: {
15
- enable: boolean;
16
- };
17
- timeout?: number;
18
- onProgress: (progress: number, e: ProgressEvent) => void;
19
- }): Promise<[boolean, ArrayBuffer | string]>;
20
- export {};
@@ -1,31 +0,0 @@
1
- interface FrameAnimationControllerOptions {
2
- defaultSpeed?: number;
3
- frameRate?: number;
4
- frameCallback?: (frame: number, timeJson: any) => void;
5
- }
6
- export default class FrameAnimationController {
7
- private startTime;
8
- private currentFrame;
9
- private speed;
10
- private frameCallback;
11
- private isPlaying;
12
- private animationId;
13
- private frameRate;
14
- private startTimeFromDate;
15
- private currentFrameFromDate;
16
- constructor({ defaultSpeed, frameRate, frameCallback, }?: FrameAnimationControllerOptions);
17
- get playing(): boolean;
18
- play(): void;
19
- pause(): void;
20
- reset(): void;
21
- setSpeed(speed: number): void;
22
- gotoFrame(frameNumber: number): void;
23
- nextFrame(): void;
24
- prevFrame(): void;
25
- destroy(): void;
26
- private _animate;
27
- private _updateFrame;
28
- getCurrentFrame(): number;
29
- updateStartTime(frame: number): void;
30
- }
31
- export {};
@@ -1,9 +0,0 @@
1
- /**
2
- * @file 时间处理工具函数
3
- */
4
- /**
5
- * 格式化时间戳字符串为 'YYYY-MM-DD HH:mm:ss' 格式
6
- * @param timestamp - ISO 格式的时间字符串, e.g., "2025-07-02T15:02:36.391615"
7
- * @returns 格式化后的时间字符串,如果输入无效则返回 'N/A'
8
- */
9
- export declare function formatTimestamp(timestamp: string): string;
@@ -1,28 +0,0 @@
1
- /**
2
- * 视频解码能力探测片(H.264 High / level 5.0 / 2160x1920 / 10 帧 / 无音轨)
3
- *
4
- * 源视频:
5
- * https://public-xmov.oss-cn-hangzhou.aliyuncs.com/avatar_sdk_prod/body_ani/M_MX01_show/look/Suyuan_14748_new/1080x1920_fullbody/idle_youling01_cut_000.mp4
6
- * 源视频实测参数:h264 / High / level 5.0 / coded 2160x1920 / 24fps / 24 帧 / 无音轨
7
- * 期望 codec 字符串:avc1.640032
8
- *
9
- * 生成命令:
10
- * ffmpeg -y -i "<源视频>" -frames:v 10 -an -c:v libx264 \
11
- * -profile:v high -level 5.0 -refs 5 -crf 51 -preset veryfast \
12
- * -pix_fmt yuv420p -movflags +faststart probe_final.mp4
13
- *
14
- * 校验命令(期望输出 h264 / High / 50 / 2160 / 1920 / 10):
15
- * ffprobe -v error -show_entries stream=codec_name,profile,level,coded_width,coded_height,nb_frames \
16
- * -of default=noprint_wrappers=1 probe_final.mp4
17
- *
18
- * 保真说明:
19
- * - level 与 refs 强制对齐源视频,使 SPS 可观测字段(profile_idc / level_idc / 分辨率 /
20
- * CABAC / weighted_pred / transform_8x8 / 约束标志位)与生产码流逐位一致,codec 字符串同为 avc1.640032
21
- * - 仅压缩帧数(24→10)与质量(CRF 51),分辨率保持 2160x1920,
22
- * 以便真实覆盖 Android 硬解在 2160x1920 / level 5.0 下的能力边界
23
- * - 体积 10436 B,base64 后约 13.6KB
24
- *
25
- * 保鲜约定:源视频编码参数一旦变化(如改为 H.265、调整 level、更换编码器特性),
26
- * 本探测片会静默失真,需重新执行上述生成命令并同步更新本注释与期望 codec 字符串。
27
- */
28
- export declare const VIDEO_PROBE_BASE64: string;
@@ -1,54 +0,0 @@
1
- import { type SDKError } from "../types/error";
2
- import { type ISessionResponse } from "../modules/ResourceManager";
3
- /**
4
- * 调试信息浮层(支持复制会话ID + 禁止疯狂刷新DOM)
5
- */
6
- export declare class DebugOverlay {
7
- private container;
8
- private sdkContainer;
9
- private sdk;
10
- private sessionInfo;
11
- private startTime;
12
- private updateInterval;
13
- private errors;
14
- private fps;
15
- private frameCount;
16
- private lastFpsUpdate;
17
- private videoInfo;
18
- private audioInfo;
19
- private eventInfo;
20
- private memoryInfo;
21
- private perfMetrics;
22
- private currentFrameIndex;
23
- private domRefs;
24
- constructor(sdk: any, sessionInfo: ISessionResponse);
25
- setAudioInfo(info: {
26
- sf: number;
27
- ef: number;
28
- ad: Uint8Array;
29
- }): void;
30
- setEventData(info: {
31
- sf: number;
32
- ef: number;
33
- event: any[];
34
- }): void;
35
- addError(error: SDKError): void;
36
- show(): void;
37
- hide(): void;
38
- destroy(): void;
39
- setVideoInfo(info: any): void;
40
- private updateTextOnly;
41
- private setText;
42
- private copySessionId;
43
- private createOverlay;
44
- private formatErrors;
45
- private updatePerfMetrics;
46
- private formatPerfMetrics;
47
- private formatMemory;
48
- private trackFPS;
49
- private updateMemory;
50
- private trackMemory;
51
- private getNetworkInfo;
52
- private applyStyles;
53
- private enableDrag;
54
- }
@@ -1 +0,0 @@
1
- export declare const workerURL: string;
@@ -1,328 +0,0 @@
1
- /**
2
- * 音频渲染
3
- * 使用 AudioContext 控制
4
- */
5
-
6
- import ResourceManager from "../modules/ResourceManager";
7
- import { DataCacheQueue } from "../control/DataCacheQueue";
8
- // import PCMAudioPlayer from "./Audio";
9
- import AudioWorklet from "./AudioWorklet";
10
- import { performanceConstant } from "../utils/perfermance";
11
- import XmovAvatar from "../index";
12
- import { AvatarStatus } from "../types";
13
- import MediaSourceAudioPlayer from "./MSEAudioPlayer";
14
- import { isSupportMES } from "../utils";
15
-
16
- type Option = {
17
- sdk: XmovAvatar;
18
- dataCacheQueue: DataCacheQueue;
19
- resourceManager: ResourceManager;
20
- };
21
-
22
- export default class AudioRender {
23
- private TAG = "[AudioRenderer]";
24
- private options: Option;
25
- isPlaying = false;
26
- lastFrameIndex: number = -1;
27
- audio: AudioWorklet | null = null;
28
- firstFrameIndex: number = -1;
29
- resourceManager: ResourceManager;
30
- valume: number = 1;
31
- speech_id: number = -1; // 当前播放的音频id
32
- cacheFirstFrameIndex: number = -1; // 缓存下来的第一个音频的索引
33
- cacheAudioData: Uint8Array[] = []; // 缓存下来的音频数据
34
- oldSpeechId: number = -1; // 上一个播放的音频id
35
- mseCacheAudioData: Array<{ data: ArrayBuffer; frameIndex?: number; speechId?: number }> = [];
36
- private mseAudioPlayer: MediaSourceAudioPlayer | null = null;
37
-
38
- constructor(options: Option) {
39
- this.options = options;
40
- this.resourceManager = options.resourceManager;
41
- if(this.resourceManager.config.raw_audio) {
42
- this.audio = new AudioWorklet();
43
- this.audio.init();
44
- } else {
45
- this.mseAudioPlayer = MediaSourceAudioPlayer.getInstance(options.sdk);
46
- }
47
- }
48
- updateAudioData(audioList: any) {
49
- if (
50
- !this.resourceManager.config.raw_audio ||
51
- audioList[0].sid === this.oldSpeechId
52
- )
53
- return;
54
- if (this.firstFrameIndex === -1) {
55
- this.firstFrameIndex = audioList[0].sf;
56
- }
57
- let totalLength = 0;
58
- for (const audio of audioList) {
59
- totalLength += audio.ad?.length || 0;
60
- }
61
-
62
- // 创建合并后的Uint8Array
63
- const mergedData = new Uint8Array(totalLength);
64
- let offset = 0;
65
-
66
- // 填充合并数据
67
- for (const audio of audioList) {
68
- if (audio.ad) {
69
- mergedData.set(new Uint8Array(audio.ad), offset);
70
- offset += audio.ad.length;
71
- }
72
- }
73
- if (this.speech_id === -1) {
74
- this.speech_id = audioList[0].sid;
75
- this.audio?.receivePCMStream(mergedData);
76
- } else {
77
- if (this.speech_id !== audioList[0].sid) {
78
- // 先缓存下来
79
- if (this.cacheFirstFrameIndex === -1) {
80
- this.cacheFirstFrameIndex = audioList[0].sf;
81
- }
82
- this.cacheAudioData.push(mergedData);
83
- } else {
84
- // 直接播放
85
- this.audio?.receivePCMStream(mergedData);
86
- }
87
- }
88
- }
89
-
90
- _updateAudio(audioList: any) {
91
- if (!this.mseAudioPlayer || audioList.length === 0) return;
92
-
93
- const firstAudio = audioList[0];
94
-
95
- // 检查是否是旧的 speech_id
96
- if (firstAudio.sid === this.oldSpeechId) {
97
- return;
98
- }
99
-
100
- // 如果 speech_id 变化,停止当前播放并重置
101
- if (this.speech_id !== -1 && firstAudio.sid !== this.speech_id) {
102
- (window as any).avatarSDKLogger?.log(
103
- this.TAG,
104
- `检测到新的 speech_id: ${firstAudio.sid}, 当前 WebM 播放 (旧: ${this.speech_id}), 新: ${firstAudio.sid}, 缓存数据长度: ${this.mseCacheAudioData.length}`
105
- );
106
- if(this.cacheFirstFrameIndex === -1) {
107
- this.cacheFirstFrameIndex = firstAudio.sf;
108
- }
109
- for(const item of audioList) {
110
- this.mseCacheAudioData.push({
111
- data: item.ad,
112
- frameIndex: item.sf,
113
- speechId: item.sid,
114
- })
115
- }
116
- } else {
117
- // 记录第一个音频段的帧索引
118
- if (this.firstFrameIndex === -1) {
119
- this.firstFrameIndex = firstAudio.sf;
120
- }
121
-
122
- // 更新 speech_id
123
- if (this.speech_id === -1) {
124
- this.speech_id = firstAudio.sid;
125
- } else if (this.speech_id !== firstAudio.sid) {
126
- this.speech_id = firstAudio.sid;
127
- }
128
-
129
- // 处理每个音频段
130
- for (const audio of audioList) {
131
- if (audio.ad) {
132
- // 将 Uint8Array 转换为 ArrayBuffer
133
- const audioData = audio.ad instanceof ArrayBuffer
134
- ? audio.ad
135
- : audio.ad.buffer.slice(audio.ad.byteOffset, audio.ad.byteOffset + audio.ad.byteLength);
136
-
137
- this.mseAudioPlayer.addAudioSegment(
138
- audioData,
139
- );
140
- }
141
- }
142
- }
143
- }
144
-
145
- async render(frameIndex: number) {
146
- (window as any).avatarSDKLogger.log("当前帧索引:", frameIndex, "音频开始播放帧:", this.firstFrameIndex);
147
- if (this.lastFrameIndex === -1) {
148
- this.lastFrameIndex = frameIndex;
149
- }
150
- if (this.options.sdk.getStatus() === AvatarStatus.offline) return;
151
- // PCM 流式播放(raw_audio = true)
152
- if (
153
- frameIndex >= this.firstFrameIndex &&
154
- this.firstFrameIndex != -1 &&
155
- !this.isPlaying &&
156
- this.resourceManager.config.raw_audio
157
- ) {
158
- (window as any).performanceTracker.markEnd(
159
- performanceConstant.start_action_render,
160
- "speak"
161
- );
162
- this.isPlaying = true;
163
- this.audio?.start();
164
- return;
165
- }
166
-
167
- // WebM 流式播放(raw_audio = false)
168
- if (!this.resourceManager.config.raw_audio && this.mseAudioPlayer) {
169
- // 检查是否应该开始播放(帧对齐)
170
- if (
171
- this.firstFrameIndex !== -1 &&
172
- frameIndex >= this.firstFrameIndex &&
173
- !this.mseAudioPlayer.isPlaying &&
174
- !this.isPlaying
175
- ) {
176
- // 优化:确保有足够的数据缓冲再开始播放
177
- const stats = this.mseAudioPlayer.getStats();
178
- const hasEnoughData = stats.queueLength > 0 || stats.bufferedRanges !== '-';
179
- if (hasEnoughData) {
180
- try {
181
- (window as any).avatarSDKLogger.log("开始播放", frameIndex);
182
- this.isPlaying = true;
183
- await this.mseAudioPlayer.start();
184
- (window as any).performanceTracker.markEnd(
185
- performanceConstant.start_action_render,
186
- "speak"
187
- );
188
- } catch (error) {
189
- (window as any).avatarSDKLogger.error("[AudioRenderer] WebM 音频播放失败:", {
190
- error,
191
- frameIndex,
192
- firstFrameIndex: this.firstFrameIndex,
193
- queueLength: stats.queueLength,
194
- });
195
- }
196
- } else {
197
- // 数据不足,等待更多数据
198
- (window as any).avatarSDKLogger.debug?.(
199
- "[AudioRenderer]",
200
- `等待更多 WebM 音频数据 (frameIndex: ${frameIndex}, queueLength: ${stats.queueLength})`
201
- );
202
- }
203
- }
204
- return; // WebM 模式下不需要继续处理 PCM 播放逻辑
205
- }
206
- }
207
-
208
- resume() {
209
- if (this.audio && this.resourceManager.config.raw_audio) {
210
- this.audio?.resume();
211
- } else {
212
- // if (this.audioCtx.state === "suspended") {
213
- // this.audioCtx
214
- // .resume()
215
- // .then(() => {
216
- // (window as any).avatarSDKLogger.info("音频上下文恢复成功");
217
- // })
218
- // .catch((error) => {
219
- // (window as any).avatarSDKLogger.error("音频上下文恢复失败:", error);
220
- // });
221
- // }
222
- }
223
- }
224
-
225
- pause() {
226
- this.isPlaying = false;
227
- this.options.dataCacheQueue._clearAudio(this.speech_id);
228
- this.lastFrameIndex = -1;
229
- this.firstFrameIndex = -1;
230
- this.cacheFirstFrameIndex = -1;
231
- this.cacheAudioData = [];
232
- this.mseCacheAudioData = [];
233
- this.oldSpeechId = -1;
234
- // 停止 PCM 播放
235
- this.audio?.stop();
236
-
237
- // 停止 WebM 播放
238
- this.mseAudioPlayer?.stop(this.addNewAudioSegment.bind(this));
239
- this.speech_id = -1;
240
- }
241
-
242
- stop(speech_id: number) {
243
- // speech_id === -1 表示强制清理(用于 pauseRender / destroy / 断网重连场景),不受 oldSpeechId 限制
244
- // 否则如果 oldSpeechId >= speech_id 直接返回,避免重复处理
245
- if (speech_id !== -1 && this.oldSpeechId >= speech_id) return;
246
-
247
- this.isPlaying = false;
248
- this.options.dataCacheQueue._clearAudio(speech_id);
249
- this.firstFrameIndex = -1;
250
- // 处理缓存的音频数据
251
- (window as any).avatarSDKLogger?.log(
252
- this.TAG,
253
- `清除缓存数据 (speech_id: ${this.speech_id}, PCM缓存数据长度: ${this.cacheAudioData.length}, WebM缓存数据长度: ${this.mseCacheAudioData.length})`
254
- );
255
- if (this.resourceManager.config.raw_audio) {
256
- // 停止 PCM 播放
257
- this.audio?.stop();
258
- } else {
259
- // 停止 WebM 播放
260
- this.mseAudioPlayer?.stop(this.addNewAudioSegment.bind(this));
261
- }
262
- this.oldSpeechId = speech_id;
263
- this.speech_id = -1;
264
- if (speech_id === -1) {
265
- // 强制清理:清空所有缓存数据,不推入音频流(用于 pauseRender / destroy / 断网重连)
266
- this.cacheFirstFrameIndex = -1;
267
- this.cacheAudioData = [];
268
- this.mseCacheAudioData = [];
269
- } else if (this.cacheAudioData.length > 0) {
270
- this.firstFrameIndex = this.cacheFirstFrameIndex;
271
- this.cacheFirstFrameIndex = -1;
272
- // 新的缓存下来的数据,推入音频流
273
- for (const data of this.cacheAudioData) {
274
- this.audio?.receivePCMStream(data);
275
- }
276
- this.cacheAudioData = [];
277
- }
278
- }
279
-
280
- addNewAudioSegment() {
281
- if(this.mseCacheAudioData.length > 0) {
282
- this.firstFrameIndex = this.cacheFirstFrameIndex;
283
- this.cacheFirstFrameIndex = -1;
284
- // 新的缓存下来的数据,推入音频流
285
- for (const audio of this.mseCacheAudioData) {
286
- if (audio.data) {
287
- this.mseAudioPlayer?.addAudioSegment(
288
- audio.data,
289
- );
290
- }
291
- }
292
- this.mseCacheAudioData = [];
293
- }
294
- }
295
-
296
- setVolume(valume: number) {
297
- this.valume = valume;
298
- if (this.resourceManager.config.raw_audio) {
299
- this.audio?.setVolume(valume);
300
- } else {
301
- // WebM 模式:使用 MSEAudioPlayer 设置音量
302
- this.mseAudioPlayer?.setVolume(valume);
303
- }
304
- }
305
-
306
- setSpeed(speed: number) {
307
- if (this.resourceManager.config.raw_audio) {
308
- this.audio?.setSpeed(speed);
309
- } else {
310
- // WebM 模式:使用 MSEAudioPlayer 设置播放速度
311
- this.mseAudioPlayer?.setSpeed(speed);
312
- }
313
- }
314
-
315
- getAudioPlayer(): AudioWorklet | null {
316
- return this.audio;
317
- }
318
-
319
- destroy() {
320
- this.stop(-1);
321
- this.cacheAudioData = [];
322
- this.mseCacheAudioData = [];
323
- this.audio?.destroy();
324
- // 直接调用 destroy(),不需要先 stop()(stop 会重建 MediaSource,浪费资源)
325
- this.mseAudioPlayer?.destroy();
326
- this.mseAudioPlayer = null;
327
- }
328
- }