@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.
- package/README.md +34 -6
- package/package-dist/agent/avatar.cjs +1 -0
- package/package-dist/agent/avatar.modern.js +1 -0
- package/package-dist/agent/avatar.module.js +1 -0
- package/package-dist/agent/avatar.umd.js +1 -0
- package/package-dist/agent/index.d.ts +42 -0
- package/{dist/agent/types.d.ts → package-dist/agent/public-types.d.ts} +115 -116
- package/package-dist/index.cjs +3 -0
- package/package-dist/index.d.ts +34 -0
- package/package-dist/index.modern.js +1 -0
- package/package-dist/index.module.js +1 -0
- package/package-dist/index.umd.js +14 -0
- package/package-dist/public-types.d.ts +205 -0
- package/package.json +41 -33
- package/dist/agent/avatar.cjs +0 -1
- package/dist/agent/avatar.modern.js +0 -1
- package/dist/agent/avatar.module.js +0 -1
- package/dist/agent/avatar.umd.js +0 -1
- package/dist/agent/index.d.ts +0 -137
- package/dist/baseRender/AudioRenderer.d.ts +0 -46
- package/dist/baseRender/AudioWorklet.d.ts +0 -78
- package/dist/baseRender/AvatarRenderer.d.ts +0 -225
- package/dist/baseRender/MSEAudioPlayer.d.ts +0 -51
- package/dist/baseRender/UIRenderer.d.ts +0 -39
- package/dist/baseRender/pcm-audio-processor.d.ts +0 -63
- package/dist/control/APIForwarder.d.ts +0 -23
- package/dist/control/DataCacheQueue.d.ts +0 -69
- package/dist/control/EventDispatcher.d.ts +0 -35
- package/dist/control/RenderScheduler.d.ts +0 -171
- package/dist/control/SaveAndDownload.d.ts +0 -14
- package/dist/control/StreamingClient.d.ts +0 -33
- package/dist/control/VideoPlayer.d.ts +0 -47
- package/dist/control/ttsa.d.ts +0 -125
- package/dist/encoding/media-recorder-encoder.d.ts +0 -33
- package/dist/encoding/pcm-webm-encoder.d.ts +0 -13
- package/dist/encoding/webcodec-opus-encoder.d.ts +0 -41
- package/dist/encoding/webm-muxer.d.ts +0 -90
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts +0 -251
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.module.js +0 -2
- package/dist/index.module.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/dist/modules/Composition.d.ts +0 -28
- package/dist/modules/ResourceManager.d.ts +0 -299
- package/dist/modules/TrackRenderer/base-track.d.ts +0 -4
- package/dist/modules/TrackRenderer/index.d.ts +0 -12
- package/dist/modules/TrackRenderer/render-implements.d.ts +0 -19
- package/dist/modules/TrackRenderer/track-pic.d.ts +0 -8
- package/dist/modules/TrackRenderer/track-subtitle.d.ts +0 -8
- package/dist/modules/cache-manager.d.ts +0 -5
- package/dist/modules/decoder.d.ts +0 -84
- package/dist/modules/error-handle.d.ts +0 -17
- package/dist/modules/network.d.ts +0 -21
- package/dist/modules/sentry-reporter.d.ts +0 -58
- package/dist/proto/face_data_pb.d.ts +0 -0
- package/dist/types/capability.d.ts +0 -15
- package/dist/types/error.d.ts +0 -70
- package/dist/types/event.d.ts +0 -25
- package/dist/types/frame-data.d.ts +0 -107
- package/dist/types/index.d.ts +0 -196
- package/dist/types/render.d.ts +0 -18
- package/dist/utils/DataInterface.d.ts +0 -113
- package/dist/utils/GLDevice.d.ts +0 -46
- package/dist/utils/GLPipeline.d.ts +0 -84
- package/dist/utils/GLPipelineDebugTools.d.ts +0 -5
- package/dist/utils/Math.d.ts +0 -21
- package/dist/utils/__tests__/capability-checker.test.d.ts +0 -1
- package/dist/utils/audio-session.d.ts +0 -11
- package/dist/utils/audio.d.ts +0 -1
- package/dist/utils/capability-checker.d.ts +0 -28
- package/dist/utils/encodeToken.d.ts +0 -14
- package/dist/utils/face.d.ts +0 -1
- package/dist/utils/float32-decoder.d.ts +0 -26
- package/dist/utils/fpsTracker.d.ts +0 -27
- package/dist/utils/index.d.ts +0 -22
- package/dist/utils/logger.d.ts +0 -9
- package/dist/utils/media-recorder-timestamp.d.ts +0 -15
- package/dist/utils/pcmAnalyzer.d.ts +0 -40
- package/dist/utils/perfermance.d.ts +0 -15
- package/dist/utils/request.d.ts +0 -20
- package/dist/utils/requestAnimateFrames.d.ts +0 -31
- package/dist/utils/time.d.ts +0 -9
- package/dist/utils/video-probe-asset.d.ts +0 -28
- package/dist/view/DebugOverlay.d.ts +0 -54
- package/dist/worker/streaming-video.d.ts +0 -1
- package/src/baseRender/AudioRenderer.ts +0 -328
- package/src/baseRender/AudioWorklet.ts +0 -776
- package/src/baseRender/AvatarRenderer.ts +0 -1550
- package/src/baseRender/MSEAudioPlayer.ts +0 -254
- package/src/baseRender/UIRenderer.ts +0 -195
- package/src/baseRender/pcm-audio-processor.js +0 -267
- package/src/control/APIForwarder.ts +0 -67
- package/src/control/DataCacheQueue.ts +0 -372
- package/src/control/EventDispatcher.ts +0 -106
- package/src/control/RenderScheduler.ts +0 -1000
- package/src/control/SaveAndDownload.js +0 -77
- package/src/control/StreamingClient.ts +0 -138
- package/src/control/VideoPlayer.ts +0 -306
- package/src/control/ttsa.ts +0 -754
- package/src/encoding/media-recorder-encoder.ts +0 -175
- package/src/encoding/pcm-webm-encoder.ts +0 -72
- package/src/encoding/webcodec-opus-encoder.ts +0 -258
- package/src/encoding/webm-muxer.ts +0 -337
- package/src/global.d.ts +0 -112
- package/src/index.ts +0 -2053
- package/src/modules/Composition.ts +0 -79
- package/src/modules/ResourceManager.ts +0 -926
- package/src/modules/TrackRenderer/base-track.ts +0 -7
- package/src/modules/TrackRenderer/index.ts +0 -40
- package/src/modules/TrackRenderer/render-implements.ts +0 -269
- package/src/modules/TrackRenderer/track-pic.ts +0 -19
- package/src/modules/TrackRenderer/track-subtitle.ts +0 -16
- package/src/modules/cache-manager.ts +0 -10
- package/src/modules/decoder.ts +0 -548
- package/src/modules/error-handle.ts +0 -20
- package/src/modules/network.ts +0 -71
- package/src/modules/sentry-reporter.ts +0 -171
- package/src/proto/face_data.proto +0 -45
- package/src/proto/face_data_pb.js +0 -1297
- package/src/proto/protobuf.min.js +0 -8
- package/src/types/capability.ts +0 -24
- package/src/types/error.ts +0 -211
- package/src/types/event.ts +0 -38
- package/src/types/frame-data.ts +0 -121
- package/src/types/index.ts +0 -220
- package/src/types/render.ts +0 -18
- package/src/utils/DataInterface.ts +0 -746
- package/src/utils/GLDevice.ts +0 -286
- package/src/utils/GLPipeline.ts +0 -1293
- package/src/utils/GLPipelineDebugTools.ts +0 -174
- package/src/utils/Math.ts +0 -266
- package/src/utils/__tests__/capability-checker.test.ts +0 -159
- package/src/utils/audio-session.ts +0 -144
- package/src/utils/audio.ts +0 -44
- package/src/utils/blueimp-md5.d.ts +0 -3
- package/src/utils/capability-checker.ts +0 -762
- package/src/utils/encodeToken.ts +0 -115
- package/src/utils/face.ts +0 -29
- package/src/utils/float32-decoder.js +0 -130
- package/src/utils/fpsTracker.ts +0 -100
- package/src/utils/index.ts +0 -182
- package/src/utils/logger.js +0 -36
- package/src/utils/media-recorder-timestamp.ts +0 -79
- package/src/utils/pcmAnalyzer.ts +0 -154
- package/src/utils/perfermance.ts +0 -141
- package/src/utils/request.ts +0 -107
- package/src/utils/requestAnimateFrames.ts +0 -195
- package/src/utils/time.ts +0 -32
- package/src/utils/video-probe-asset.ts +0 -99
- package/src/view/DebugOverlay.ts +0 -409
- package/src/worker/streaming-video.ts +0 -4647
- /package/{dist → package-dist}/agent/index.cjs +0 -0
- /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
|
-
}
|
package/dist/utils/index.d.ts
DELETED
|
@@ -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;
|
package/dist/utils/logger.d.ts
DELETED
|
@@ -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
|
-
};
|
package/dist/utils/request.d.ts
DELETED
|
@@ -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 {};
|
package/dist/utils/time.d.ts
DELETED
|
@@ -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
|
-
}
|