@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,154 +0,0 @@
1
- /**
2
- * PCM 音频参数分析工具
3
- * 用于检测和分析 PCM 音频数据的基本参数
4
- */
5
-
6
- export interface PCMAudioParams {
7
- sampleRate: number; // 采样率 (Hz)
8
- channels: number; // 声道数 (1=单声道, 2=双声道)
9
- bitDepth: number; // 位深度 (8, 16, 24, 32)
10
- format: string; // 格式 (S16LE, S24LE, F32LE 等)
11
- encoding: string; // 编码方式 (PCM)
12
- bytesPerSample: number; // 每个采样点的字节数
13
- sampleCount?: number; // 样本数量(如果提供了数据)
14
- duration?: number; // 时长(秒)(如果提供了数据)
15
- }
16
-
17
- /**
18
- * 分析 PCM 音频数据的基本参数
19
- * @param pcmData PCM 音频数据(Uint8Array)
20
- * @param knownSampleRate 已知的采样率(可选,如果不提供则使用默认值 24000)
21
- * @returns PCM 音频参数
22
- */
23
- export function analyzePCMParams(
24
- pcmData?: Uint8Array,
25
- knownSampleRate?: number
26
- ): PCMAudioParams {
27
- // 根据代码中的配置,PCM 音频使用固定参数
28
- const sampleRate = knownSampleRate || 24000;
29
- const channels = 1; // 单声道
30
- const bitDepth = 16; // 16位
31
- const format = 'S16LE'; // Signed 16-bit Little Endian
32
- const encoding = 'PCM'; // 未压缩的 PCM 编码
33
- const bytesPerSample = 2; // 16位 = 2字节/采样点
34
-
35
- let sampleCount: number | undefined;
36
- let duration: number | undefined;
37
-
38
- if (pcmData) {
39
- // 计算样本数量
40
- sampleCount = Math.floor(pcmData.length / bytesPerSample);
41
- // 计算时长(秒)
42
- duration = sampleCount / sampleRate;
43
- }
44
-
45
- return {
46
- sampleRate,
47
- channels,
48
- bitDepth,
49
- format,
50
- encoding,
51
- bytesPerSample,
52
- sampleCount,
53
- duration,
54
- };
55
- }
56
-
57
- /**
58
- * 检测 PCM 数据的可能参数(通过分析数据特征)
59
- * 注意:这种方法只能提供推测,最准确的参数应该从服务器配置中获取
60
- * @param pcmData PCM 音频数据
61
- * @returns 检测到的参数(可能不准确)
62
- */
63
- export function detectPCMParams(pcmData: Uint8Array): Partial<PCMAudioParams> {
64
- const dataLength = pcmData.length;
65
-
66
- // 检测位深度和字节序
67
- // 由于数据是 16 位,每个样本占 2 字节
68
- const bytesPerSample = 2; // 根据代码,固定为 2 字节(16位)
69
- const bitDepth = 16;
70
-
71
- // 检测声道数(通过数据长度和样本对齐)
72
- // 如果数据长度是 2 的倍数,可能是单声道
73
- // 如果数据长度是 4 的倍数,可能是双声道
74
- let channels = 1;
75
- if (dataLength % 4 === 0) {
76
- // 可能是双声道(2 通道 × 2 字节 = 4 字节/样本)
77
- // 但根据代码,这里固定为单声道
78
- channels = 1;
79
- }
80
-
81
- // 尝试检测采样率(这个很难准确检测,只能推测)
82
- // 如果知道数据时长和样本数,可以反推采样率
83
- const sampleCount = Math.floor(dataLength / bytesPerSample);
84
-
85
- return {
86
- channels,
87
- bitDepth,
88
- format: 'S16LE', // 根据代码,固定为 S16LE
89
- encoding: 'PCM',
90
- bytesPerSample,
91
- sampleCount,
92
- };
93
- }
94
-
95
- /**
96
- * 格式化显示 PCM 参数
97
- * @param params PCM 音频参数
98
- * @returns 格式化的字符串
99
- */
100
- export function formatPCMParams(params: PCMAudioParams): string {
101
- const parts = [
102
- `采样率: ${params.sampleRate} Hz`,
103
- `声道: ${params.channels === 1 ? '单声道 (Mono)' : `双声道 (Stereo, ${params.channels} channels)`}`,
104
- `位深度: ${params.bitDepth} bit`,
105
- `格式: ${params.format}`,
106
- `编码: ${params.encoding}`,
107
- `每样本字节数: ${params.bytesPerSample} bytes`,
108
- ];
109
-
110
- if (params.sampleCount !== undefined) {
111
- parts.push(`样本数: ${params.sampleCount.toLocaleString()}`);
112
- }
113
-
114
- if (params.duration !== undefined) {
115
- parts.push(`时长: ${params.duration.toFixed(3)} 秒 (${(params.duration * 1000).toFixed(0)} ms)`);
116
- }
117
-
118
- return parts.join('\n');
119
- }
120
-
121
- /**
122
- * 从音频渲染器获取当前 PCM 配置
123
- * @param audioRenderer 音频渲染器实例
124
- * @returns PCM 音频参数
125
- */
126
- export function getPCMParamsFromRenderer(audioRenderer: any): PCMAudioParams {
127
- // 尝试从 AudioWorklet 或 Audio 播放器获取配置
128
- let sampleRate = 24000; // 默认值
129
- let channels = 1;
130
- let bitDepth = 16;
131
- let format = 'S16LE';
132
- let bytesPerSample = 2;
133
-
134
- // 检查 AudioWorklet
135
- if (audioRenderer?.audio?.audioCtx) {
136
- sampleRate = audioRenderer.audio.audioCtx.sampleRate || 24000;
137
- }
138
-
139
- // 检查旧的 Audio 播放器
140
- if (audioRenderer?.audio?.SAMPLE_RATE) {
141
- sampleRate = audioRenderer.audio.SAMPLE_RATE;
142
- }
143
-
144
- // 根据代码,PCM 音频固定使用这些参数
145
- return {
146
- sampleRate,
147
- channels,
148
- bitDepth,
149
- format,
150
- encoding: 'PCM',
151
- bytesPerSample,
152
- };
153
- }
154
-
@@ -1,141 +0,0 @@
1
- import Ttsa from "../control/ttsa";
2
-
3
- export const performanceConstant = {
4
- load_resource: 'load_resource',
5
- ttsa_connect: 'ttsa_connect',
6
- ttsa_ready: 'ttsa_ready',
7
- first_avatar_render: 'first_avatar_render',
8
- first_webgl_render: 'first_webgl_render',
9
-
10
- ttsa_body_res: 'ttsa_body_res',
11
-
12
- start_action_res: 'start_action_res',
13
- start_action_render: 'start_action_render',
14
- load_video: 'load_video',
15
- decode_video: 'decode_video',
16
- voice_response_play: 'voice_response_play',
17
- first_decode_video: 'first_decode_video',
18
- first_render: 'first_render',
19
- };
20
- const performanceText={
21
- [performanceConstant.load_resource]: '加载资源',
22
- [performanceConstant.ttsa_connect]: 'ttsa连接',
23
- [performanceConstant.ttsa_ready]: 'ttsa准备返回first_start_timestamp',
24
- [performanceConstant.first_avatar_render]: '前端处理好第一帧渲染数据',
25
- [performanceConstant.first_webgl_render]: '前端处理好第一帧webgl渲染',
26
- [performanceConstant.ttsa_body_res]: 'ttsa返回body',
27
- [performanceConstant.start_action_res]: '触发行为返回',
28
- [performanceConstant.start_action_render]: '触发行为并渲染耗时',
29
- [performanceConstant.load_video]: '视频加载',
30
- [performanceConstant.decode_video]: '视频解码',
31
- [performanceConstant.voice_response_play]: 'speak音频响应',
32
- [performanceConstant.first_decode_video]: '第一帧视频解码',
33
- [performanceConstant.first_render]: '第一帧webgl渲染',
34
- }
35
-
36
- interface PerformanceMark {
37
- state?: string;
38
- time: number;
39
- }
40
-
41
- class PerformanceTracker {
42
- private marks: Map<keyof typeof performanceText, PerformanceMark> = new Map();
43
- private measures: Map<keyof typeof performanceText, PerformanceMark> = new Map();
44
- private reportFunc: Ttsa | null = null;
45
- private onStateRenderChange: (state: string, duration: number) => void = () => {};
46
- public firstDecodeTime = 0;
47
- public firstRenderTime = 0;
48
- constructor() {
49
- this.marks = new Map();
50
- this.measures = new Map();
51
- }
52
- getFirstInfo() {
53
- return {
54
- decode_speed: this.firstDecodeTime,
55
- render_speed: this.firstRenderTime,
56
- }
57
- }
58
- // 记录开始时间点
59
- markStart(key: string, state?: string) {
60
- this.marks.set(key as keyof typeof performanceText, {
61
- state,
62
- time: performance.now(),
63
- });
64
- }
65
-
66
- // 记录结束时间点并计算耗时
67
- markEnd(key: string, state?: string | any) {
68
- const mark = this.marks.get(key as keyof typeof performanceText);
69
- if (!mark) return;
70
- if(state && typeof state === 'string' && state!== mark?.state) {
71
- return;
72
- }
73
- const endTime = performance.now();
74
- const duration = endTime - mark.time;
75
- this.measures.set(key as keyof typeof performanceText, {
76
- state: mark?.state,
77
- time: duration,
78
- });
79
- // 提供给业务方状态改变到渲染耗时
80
- if(mark.state && key === performanceConstant.start_action_render) {
81
- this.onStateRenderChange(mark.state, duration);
82
- }
83
- this.reportMetric();
84
-
85
- if(key !== performanceConstant.load_video && key !== performanceConstant.decode_video) {
86
- this.marks.delete(key as keyof typeof performanceText);
87
- }
88
- // 记录第一帧解码时间
89
- if(key === performanceConstant.first_decode_video) {
90
- this.firstDecodeTime = duration;
91
- }
92
- // 记录第一帧渲染时间
93
- if(key === performanceConstant.first_render) {
94
- this.firstRenderTime = duration;
95
- }
96
- return duration;
97
- }
98
-
99
- // 获取所有性能数据
100
- getAllMetrics() {
101
- return Object.fromEntries(this.measures.entries());
102
- }
103
-
104
- getVideoMetrics() {
105
- const data = this.getAllMetrics();
106
- const filterData = Object.fromEntries(
107
- Object.entries(data).filter(([key]) => [performanceConstant.decode_video, performanceConstant.load_video].includes(key))
108
- );
109
- return filterData;
110
- }
111
-
112
- setReportFunc(ttsa: Ttsa) {
113
- this.reportFunc = ttsa;
114
- }
115
-
116
- // 上报数据到服务器
117
- reportMetric() {
118
- const data = this.getAllMetrics();
119
- if(Object.keys(data).length > 0 && this.reportFunc?.getStatus()) {
120
- // 过滤load_video和decode_video
121
- const filterData = Object.fromEntries(
122
- Object.entries(data).filter(([key]) => ![performanceConstant.load_video, performanceConstant.decode_video].includes(key))
123
- );
124
- if(Object.keys(filterData).length > 0) {
125
- this.reportFunc?.sendPerfLog(filterData);
126
- }
127
- // 遍历data
128
- for(const key in filterData) {
129
- if(key !== performanceConstant.decode_video && key !== performanceConstant.load_video) {
130
- this.measures.delete(key);
131
- }
132
- }
133
- }
134
- }
135
- setOnStateRenderChange(onStateRenderChange: (state: string, duration: number) => void) {
136
- this.onStateRenderChange = onStateRenderChange;
137
- }
138
- }
139
-
140
- // 创建全局单例
141
- (window as any).performanceTracker = new PerformanceTracker();
@@ -1,107 +0,0 @@
1
- type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD'
2
-
3
- interface RequestOptions {
4
- method?: HttpMethod
5
- data?: Record<string, any>
6
- headers?: Record<string, string>
7
- }
8
-
9
- interface ErrorResponse {
10
- status: number
11
- statusText: string
12
- message: string
13
- }
14
-
15
- const TAG = '[REQUEST]'
16
- export default async function request(
17
- url: string,
18
- options: RequestOptions = {}
19
- ) {
20
- const { method = 'GET', data, headers = {} } = options
21
-
22
- const config: RequestInit = {
23
- method,
24
- // headers,
25
- headers: {
26
- 'Content-Type': 'application/json',
27
- ...headers,
28
- },
29
- }
30
-
31
- if (method === 'GET' && data) {
32
- const params = new URLSearchParams(data).toString()
33
- url += `${url.includes('?') ? '&' : '?'}${params}`
34
- }
35
-
36
- if (method !== 'GET' && data) {
37
- config.body = JSON.stringify(data)
38
- }
39
-
40
- try {
41
- const response = await fetch(url, config)
42
-
43
- if (!response.ok) {
44
- const error: ErrorResponse = {
45
- status: response.status,
46
- statusText: response.statusText,
47
- message: `HTTP error! status: ${response.status}`,
48
- }
49
- throw error
50
- }
51
- return response
52
- } catch (error) {
53
- if (error instanceof Error) {
54
- throw {
55
- message: error.message,
56
- status: 500,
57
- statusText: 'Client Error',
58
- } as ErrorResponse
59
- }
60
- throw error
61
- }
62
- }
63
-
64
- /**
65
- * 资源下载请求
66
- * @returns {Promise<[isError: boolean, data: ArrayBuffer | string]>}
67
- * */
68
- export function XMLRequest(options: {
69
- url: string
70
- cache?: {
71
- enable: boolean
72
- }
73
- timeout?: number
74
- onProgress: (progress: number, e: ProgressEvent) => void
75
- }): Promise<[boolean, ArrayBuffer | string]> {
76
- return new Promise((resolve, reject) => {
77
- const xhr = new XMLHttpRequest();
78
- xhr.open('GET', options.url);
79
- xhr.responseType = 'arraybuffer';
80
- // 超时兜底:断网/请求挂起时不再永久等待(默认 60s,资源下载可能超时失败)
81
- xhr.timeout = options.timeout ?? 60000;
82
- xhr.onload = function() {
83
- if (xhr.readyState === 4 && xhr.status === 200) {
84
- const arrayBuffer = xhr.response;
85
- resolve([false, arrayBuffer]);
86
- } else {
87
- reject([true, `HTTP ${xhr.status} ${xhr.statusText}`]);
88
- }
89
- };
90
- xhr.onerror = function() {
91
- reject([true, 'network error']);
92
- };
93
- xhr.ontimeout = function() {
94
- reject([true, `timeout after ${xhr.timeout}ms`]);
95
- };
96
- xhr.onabort = function() {
97
- reject([true, 'aborted']);
98
- };
99
- xhr.onprogress = (e) => {
100
- if (e.lengthComputable) {
101
- const percent = Math.round((e.loaded / e.total) * 100);
102
- options.onProgress(percent, e);
103
- }
104
- };
105
- xhr.send();
106
- });
107
- }
@@ -1,195 +0,0 @@
1
- interface FrameAnimationControllerOptions {
2
- defaultSpeed?: number;
3
- frameRate?: number;
4
- frameCallback?: (frame: number, timeJson: any) => void;
5
- }
6
-
7
- export default class FrameAnimationController {
8
- private startTime: number | null; // 新增:开始时间
9
- private currentFrame: number;
10
- private speed: number;
11
- private frameCallback: ((frame: number, timeJson: any) => void) | null;
12
- private isPlaying: boolean;
13
- private animationId: number | null;
14
- private frameRate: number; // 新增:帧率
15
- private startTimeFromDate: number | null; // 新增:开始时间日期
16
- private currentFrameFromDate: number | null; // 新增:当前帧日期
17
-
18
- constructor({
19
- defaultSpeed = 1,
20
- frameRate = 24,
21
- frameCallback,
22
- }: FrameAnimationControllerOptions = {}) {
23
- this.startTime = null; // 初始化开始时间为null
24
- this.currentFrame = 1;
25
- this.speed = defaultSpeed;
26
- this.frameCallback = frameCallback || null;
27
- this.frameRate = frameRate; // 保存帧率
28
-
29
- this.isPlaying = false;
30
- this.animationId = null;
31
- this.startTimeFromDate = null; // 初始化开始时间日期为null
32
- this.currentFrameFromDate = null; // 初始化开始帧日期为null
33
- }
34
-
35
- get playing() {
36
- return this.isPlaying;
37
- }
38
-
39
- // 启动动画
40
- play(): void {
41
- if (this.isPlaying) return;
42
-
43
- this.isPlaying = true;
44
- (window as any).avatarSDKLogger.log("开始时间", this.startTime)
45
- // 记录开始时间(如果还没有开始时间)
46
- if (this.startTime === null) {
47
- (window as any).avatarSDKLogger.log("当前时间", performance.now())
48
- this.startTime = performance.now();
49
- // startTimeFromDate 必须与 startTime 同步设置,
50
- // 否则 pause→play 后两个时间源基准不一致,导致 frame_number_error 误报
51
- this.startTimeFromDate = Date.now();
52
- }
53
- this.animationId = requestAnimationFrame(this._animate.bind(this));
54
- }
55
-
56
- // 暂停动画
57
- pause(): void {
58
- this.isPlaying = false;
59
- if (this.animationId) {
60
- cancelAnimationFrame(this.animationId);
61
- this.animationId = null;
62
- }
63
- }
64
-
65
- // 重置动画
66
- reset(): void {
67
- this.pause();
68
- this.startTime = null; // 重置开始时间
69
- this.currentFrame = 1;
70
- this._updateFrame();
71
- this.startTimeFromDate = null; // 重置开始时间日期
72
- this.currentFrameFromDate = null; // 重置当前帧日期
73
- }
74
-
75
- // 设置速度
76
- setSpeed(speed: number): void {
77
- this.speed = speed;
78
- }
79
-
80
- // 跳转到指定帧
81
- gotoFrame(frameNumber: number): void {
82
- if (frameNumber < 1) frameNumber = 1;
83
-
84
- this.currentFrame = frameNumber;
85
- this._updateFrame();
86
- }
87
-
88
- // 下一帧
89
- nextFrame(): void {
90
- this.currentFrame++;
91
- this._updateFrame();
92
- }
93
-
94
- // 上一帧
95
- prevFrame(): void {
96
- this.gotoFrame(this.currentFrame - 1);
97
- }
98
-
99
- // 清理资源,在组件销毁时调用
100
- destroy(): void {
101
- this.pause();
102
- this.frameCallback = null;
103
- this.startTime = null; // 清理开始时间
104
- this.startTimeFromDate = null; // 清理开始时间日期
105
- this.currentFrame = 1; // 清理当前帧
106
- this.currentFrameFromDate = null; // 清理当前帧日期
107
- this.speed = 1;
108
- }
109
-
110
- // 动画循环
111
- private _animate(timestamp: number): void {
112
- let nowTime = performance.now();
113
- const deltaTime = timestamp - nowTime;
114
- // 如果差异在100ms以内,使用timestamp作为时间戳
115
- // 如果差异在100ms以上,则使用performance.now()作为时间戳
116
- if (Math.abs(deltaTime) < 100) {
117
- nowTime = timestamp;
118
- }
119
- if (this.currentFrame < 50) {
120
- (window as any).avatarSDKLogger.log("animate时间", timestamp, "开始时间", this.startTime, "performance", performance.now())
121
- }
122
- if (!this.isPlaying) return;
123
-
124
- // 基于开始时间计算当前应该显示的帧数
125
- if (this.startTime !== null) {
126
- const elapsedTime = nowTime - this.startTime;
127
- // 使用1/48秒的计算速率来提高准确度,然后除以2来保持1/24秒的显示速率
128
- const calculatedFrame =
129
- Math.floor(
130
- Math.floor(elapsedTime / (1000 / (this.frameRate * 2))) / 2
131
- ) + 1;
132
-
133
- // (window as any).avatarSDKLogger.log("计算帧号", calculatedFrame)
134
- // 如果计算出的帧号与当前帧号不同,更新帧号
135
- if (calculatedFrame !== this.currentFrame) {
136
- this.currentFrame = calculatedFrame;
137
- this._updateFrame();
138
- }
139
- // 基于开始时间日期计算当前应该显示的帧数
140
- const elapsedTimeFromDate = Date.now() - this.startTimeFromDate!;
141
- const calculatedFrameFromDate = Math.floor(
142
- Math.floor(elapsedTimeFromDate / (1000 / (this.frameRate * 2))) / 2
143
- ) + 1;
144
- // (window as any).avatarSDKLogger.log("计算日期帧号", calculatedFrameFromDate)
145
- this.currentFrameFromDate = calculatedFrameFromDate;
146
- }
147
-
148
- if (this.isPlaying) {
149
- this.animationId = requestAnimationFrame(this._animate.bind(this));
150
- }
151
- }
152
-
153
- // 更新帧 - 基于时间的帧计算
154
- private _updateFrame(): void {
155
- let timeJson:any = null
156
- if (this.frameCallback) {
157
- // 执行帧更新回调
158
- if (this.currentFrameFromDate !== this.currentFrame && this.currentFrameFromDate !== null) {
159
- timeJson = {
160
- "key": "frame_number_error",
161
- "Date帧号": this.currentFrameFromDate,
162
- "performance帧号": this.currentFrame,
163
- "开始时间": this.startTime,
164
- "开始时间日期": this.startTimeFromDate,
165
- "当前时间": Date.now(),
166
- "performance.now()": performance.now(),
167
- };
168
- }
169
- this.frameCallback(this.currentFrame, timeJson);
170
- }
171
- }
172
-
173
- // 获取当前帧
174
- getCurrentFrame(): number {
175
- // 这里不能直接返回this.currentFrame,因为存在用户将游览器置到后台,
176
- // 游览器会暂停requestAnimationFrame,导致this.currentFrame不更新
177
- // 这里需要重新计算
178
- if (this.startTime !== null) {
179
- const elapsedTime = performance.now() - this.startTime;
180
- const calculatedFrame =
181
- Math.floor(
182
- Math.floor(elapsedTime / (1000 / (this.frameRate * 2))) / 2
183
- ) + 1;
184
- return calculatedFrame;
185
- }
186
- return this.currentFrame;
187
- }
188
-
189
- // 更新开始时间
190
- updateStartTime(frame: number): void {
191
- this.startTime = performance.now() - (frame - 1) * (1000 / this.frameRate);
192
- // 同步 Date 时间基准,避免 performance 和 Date 双时间源脱节导致 frame_number_error 误报
193
- this.startTimeFromDate = Date.now() - (frame - 1) * (1000 / this.frameRate);
194
- }
195
- }
package/src/utils/time.ts DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * @file 时间处理工具函数
3
- */
4
-
5
- /**
6
- * 格式化时间戳字符串为 'YYYY-MM-DD HH:mm:ss' 格式
7
- * @param timestamp - ISO 格式的时间字符串, e.g., "2025-07-02T15:02:36.391615"
8
- * @returns 格式化后的时间字符串,如果输入无效则返回 'N/A'
9
- */
10
- export function formatTimestamp(timestamp: string): string {
11
- if (!timestamp || typeof timestamp !== 'string') {
12
- return 'N/A';
13
- }
14
- try {
15
- const date = new Date(timestamp);
16
- if (isNaN(date.getTime())) {
17
- return 'Invalid Date';
18
- }
19
-
20
- const year = date.getFullYear();
21
- const month = (date.getMonth() + 1).toString().padStart(2, '0');
22
- const day = date.getDate().toString().padStart(2, '0');
23
- const hours = date.getHours().toString().padStart(2, '0');
24
- const minutes = date.getMinutes().toString().padStart(2, '0');
25
- const seconds = date.getSeconds().toString().padStart(2, '0');
26
-
27
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
28
- } catch (error) {
29
- (window as any).avatarSDKLogger.error('Error formatting timestamp:', error);
30
- return timestamp; // 如果格式化失败,返回原始字符串
31
- }
32
- }