trtc-electron-sdk 13.3.800-alpha.0 → 13.3.800-alpha.2

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 CHANGED
@@ -11,13 +11,13 @@ Tencent Real-Time Communication (TRTC) is a set of low-latency, high-quality rea
11
11
  - ⬆️ [Publishing to CSS CDN](https://www.tencentcloud.com/document/product/647/47858#)
12
12
 
13
13
  ## 💻 Environment Support
14
- - 🍎 Support macOS x64 and arm64(**Electron 11+**)
15
- - ⚙️ Support Windows ia32 and x64
14
+ - 🍎 macOS **11.0 (Big Sur)** and above, x64 and arm64 (**Electron 11+**)
15
+ - 🪟 Windows 7 SP1 and above, ia32 and x64
16
16
  - 🌈 Electron: 8.5.0 and above
17
17
 
18
- | macOS | Windows | Electron |
19
- | :----------: | :---------: | :------------: |
20
- | x64 \| arm64 | ia32 \| x64 | 8.5.0 and above |
18
+ | macOS | Windows | Electron |
19
+ | :--------------------: | :---------: | :------------: |
20
+ | 11.0+ (x64 \| arm64) | ia32 \| x64 | 8.5.0 and above |
21
21
 
22
22
 
23
23
  ## ⏬ Install
package/liteav/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './trtc_define';
2
2
  export * from './trtc_code';
3
3
  export * from './vod_player';
4
+ export { VodPlayerEvents, VodPlayerEventMap, IVodPlayer } from './types';
4
5
  export * from './extensions/DeviceManager';
5
6
  export * from './extensions/AudioEffectManager';
6
7
  export * from './extensions/MediaMixingManager';
package/liteav/index.js CHANGED
@@ -13,9 +13,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.VodPlayerEvents = void 0;
16
17
  __exportStar(require("./trtc_define"), exports);
17
18
  __exportStar(require("./trtc_code"), exports);
18
19
  __exportStar(require("./vod_player"), exports);
20
+ var types_1 = require("./types");
21
+ Object.defineProperty(exports, "VodPlayerEvents", { enumerable: true, get: function () { return types_1.VodPlayerEvents; } });
19
22
  __exportStar(require("./extensions/DeviceManager"), exports);
20
23
  __exportStar(require("./extensions/AudioEffectManager"), exports);
21
24
  __exportStar(require("./extensions/MediaMixingManager"), exports);
package/liteav/types.d.ts CHANGED
@@ -1,8 +1,53 @@
1
1
  import TRTCMediaMixingManager, { TRTCMediaMixingService } from './extensions/MediaMixingManager';
2
- import { AudioMusicParam, Rect, TRTCAppScene, TRTCAudioQuality, TRTCBeautyStyle, TRTCDeviceType, TRTCLogLevel, TRTCAudioFrame, TRTCVideoRotation, TRTCVideoStreamType, TRTCWaterMarkSrcType, TRTCPluginType, TRTCPluginInfo, TRTCVideoProcessPluginOptions, TRTCMediaEncryptDecryptPluginOptions, TRTCAudioRecordingParams, TRTCScreenCaptureSourceInfo, TRTCScreenCaptureProperty, TRTCSpeedTestParams, TRTCRecordType, TRTCDeviceInfo, TRTCCameraCaptureParams, TRTCImageBuffer, TRTCInitConfig, TRTCAudioParallelParams, TRTCVoiceReverbType, TRTCVoiceChangerType, TRTCMusicPlayObserver, TRTCAudioFrameCallback, TRTCAudioProcessPluginOptions, TRTCPublishTarget, TRTCStreamEncoderParam, TRTCStreamMixingConfig, TRTCRenderParams, TRTCRoleType, TRTCSwitchRoomParam, TRTCVideoFrame, TRTCVideoPixelFormat, TRTCVideoBufferType } from './trtc_define';
2
+ import { AudioMusicParam, Rect, TRTCAppScene, TRTCAudioQuality, TRTCBeautyStyle, TRTCDeviceType, TRTCLogLevel, TRTCAudioFrame, TRTCVideoFillMode, TRTCVideoRotation, TRTCVideoStreamType, TRTCWaterMarkSrcType, TRTCPluginType, TRTCPluginInfo, TRTCVideoProcessPluginOptions, TRTCMediaEncryptDecryptPluginOptions, TRTCAudioRecordingParams, TRTCScreenCaptureSourceInfo, TRTCScreenCaptureProperty, TRTCSpeedTestParams, TRTCRecordType, TRTCDeviceInfo, TRTCCameraCaptureParams, TRTCImageBuffer, TRTCInitConfig, TRTCAudioParallelParams, TRTCVoiceReverbType, TRTCVoiceChangerType, TRTCMusicPlayObserver, TRTCAudioFrameCallback, TRTCAudioProcessPluginOptions, TRTCPublishTarget, TRTCStreamEncoderParam, TRTCStreamMixingConfig, TRTCRenderParams, TRTCRoleType, TRTCSwitchRoomParam, TRTCVideoFrame, TRTCVideoPixelFormat, TRTCVideoBufferType } from './trtc_define';
3
3
  export interface TRTCVideoRenderCallback {
4
4
  onRenderVideoFrame(userId: string, streamType: TRTCVideoStreamType, frame: TRTCVideoFrame): void;
5
5
  }
6
+ export declare enum VodPlayerEvents {
7
+ onVodPlayerStarted = "onVodPlayerStarted",
8
+ onVodPlayerLoaded = "onVodPlayerLoaded",
9
+ onVodPlayerProgress = "onVodPlayerProgress",
10
+ onVodPlayerPaused = "onVodPlayerPaused",
11
+ onVodPlayerResumed = "onVodPlayerResumed",
12
+ onVodPlayerStopped = "onVodPlayerStopped",
13
+ onVodPlayerError = "onVodPlayerError"
14
+ }
15
+ export interface VodPlayerEventMap {
16
+ [VodPlayerEvents.onVodPlayerStarted]: [msLength: number];
17
+ [VodPlayerEvents.onVodPlayerLoaded]: [durationMs: number, width: number, height: number];
18
+ [VodPlayerEvents.onVodPlayerProgress]: [msPos: number];
19
+ [VodPlayerEvents.onVodPlayerPaused]: [];
20
+ [VodPlayerEvents.onVodPlayerResumed]: [];
21
+ [VodPlayerEvents.onVodPlayerStopped]: [reason: number];
22
+ [VodPlayerEvents.onVodPlayerError]: [error: number];
23
+ }
24
+ export interface IVodPlayer {
25
+ on<K extends keyof VodPlayerEventMap>(event: K, listener: (...args: VodPlayerEventMap[K]) => void): this;
26
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
27
+ off<K extends keyof VodPlayerEventMap>(event: K, listener: (...args: VodPlayerEventMap[K]) => void): this;
28
+ off(event: string | symbol, listener: (...args: any[]) => void): this;
29
+ setView(view: HTMLElement | null): void;
30
+ setRenderRotation(rotation: TRTCVideoRotation): void;
31
+ setFillMode(mode: TRTCVideoFillMode): void;
32
+ setMirror(mirror: boolean): void;
33
+ preload(): void;
34
+ start(): void;
35
+ pause(): void;
36
+ resume(): void;
37
+ seek(msPos: number): void;
38
+ switchSource(newMediaFile: string): void;
39
+ stop(): void;
40
+ getDuration(): number;
41
+ getWidth(): number;
42
+ getHeight(): number;
43
+ mute(mute: boolean): void;
44
+ setVolume(volume: number): void;
45
+ publishVideo(): void;
46
+ publishAudio(): void;
47
+ unpublishVideo(): void;
48
+ unpublishAudio(): void;
49
+ destroy(): void;
50
+ }
6
51
  export interface ITRTCCloud {
7
52
  createSubCloud(config?: TRTCInitConfig): ITRTCCloud | null;
8
53
  enterRoom(params: any, scene: TRTCAppScene): void;
package/liteav/types.js CHANGED
@@ -1,2 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VodPlayerEvents = void 0;
4
+ var VodPlayerEvents;
5
+ (function (VodPlayerEvents) {
6
+ VodPlayerEvents["onVodPlayerStarted"] = "onVodPlayerStarted";
7
+ VodPlayerEvents["onVodPlayerLoaded"] = "onVodPlayerLoaded";
8
+ VodPlayerEvents["onVodPlayerProgress"] = "onVodPlayerProgress";
9
+ VodPlayerEvents["onVodPlayerPaused"] = "onVodPlayerPaused";
10
+ VodPlayerEvents["onVodPlayerResumed"] = "onVodPlayerResumed";
11
+ VodPlayerEvents["onVodPlayerStopped"] = "onVodPlayerStopped";
12
+ VodPlayerEvents["onVodPlayerError"] = "onVodPlayerError";
13
+ })(VodPlayerEvents = exports.VodPlayerEvents || (exports.VodPlayerEvents = {}));
@@ -1,34 +1,5 @@
1
1
  import { TRTCVideoRotation, TRTCVideoFillMode } from './trtc_define';
2
- /**
3
- * VodPlayer 播放器事件枚举
4
- */
5
- export declare enum VodPlayerEvents {
6
- /** 播放开始,参数:msLength(总时长,毫秒) */
7
- onVodPlayerStarted = "onVodPlayerStarted",
8
- /** 播放进度更新,参数:msPos(当前进度,毫秒) */
9
- onVodPlayerProgress = "onVodPlayerProgress",
10
- /** 播放暂停 */
11
- onVodPlayerPaused = "onVodPlayerPaused",
12
- /** 播放恢复 */
13
- onVodPlayerResumed = "onVodPlayerResumed",
14
- /** 播放停止,参数:reason(0=用户主动停止,1=文件播放完,2=视频断流) */
15
- onVodPlayerStopped = "onVodPlayerStopped",
16
- /** 播放错误,参数:error(错误码) */
17
- onVodPlayerError = "onVodPlayerError"
18
- }
19
- /**
20
- * VodPlayer 事件与回调参数的类型映射
21
- *
22
- * 用于 on/off 方法的类型约束,使事件名和回调参数在编译期匹配。
23
- */
24
- export interface VodPlayerEventMap {
25
- [VodPlayerEvents.onVodPlayerStarted]: [msLength: number];
26
- [VodPlayerEvents.onVodPlayerProgress]: [msPos: number];
27
- [VodPlayerEvents.onVodPlayerPaused]: [];
28
- [VodPlayerEvents.onVodPlayerResumed]: [];
29
- [VodPlayerEvents.onVodPlayerStopped]: [reason: number];
30
- [VodPlayerEvents.onVodPlayerError]: [error: number];
31
- }
2
+ import { IVodPlayer, VodPlayerEventMap } from './types';
32
3
  /**
33
4
  *
34
5
  * 腾讯云点播播放器
@@ -51,21 +22,29 @@ export interface VodPlayerEventMap {
51
22
  * // 使用完毕后销毁
52
23
  * VodPlayer.releaseVodPlayer(player);
53
24
  */
54
- export declare class VodPlayer {
25
+ export declare class VodPlayer implements IVodPlayer {
26
+ /** `_dataRenderCallback` 中允许连续重置 video buffer 的最大次数,超过后退化为周期日志 */
27
+ private static readonly MAX_BUFFER_RETRY;
28
+ /** 重试上限达到后,每隔多少帧打一次 warn 日志(按 30fps 估算 ≈ 2 秒一次) */
29
+ private static readonly BUFFER_RETRY_LOG_INTERVAL;
55
30
  private nativeVodPlayer;
56
31
  private videoRender;
57
32
  private mediaFilePath;
58
- private repeat;
59
33
  private isStarted;
60
34
  private isDestroyed;
35
+ private isRenderPrepared;
36
+ /** 数据回调中连续 setVideoBuffer 失败次数(用于 _dataRenderCallback 退避策略) */
37
+ private _setBufferRetryCount;
61
38
  private logger;
62
39
  private eventEmitter;
63
40
  /**
64
41
  * 创建 VodPlayer 实例
65
42
  *
66
- * @param mediaFilePath - 媒体文件路径(本地路径或在线 URL
43
+ * @param mediaFilePath - 媒体文件路径(本地绝对路径或在线 URL),必填且必须是非空字符串
67
44
  * @param repeat - 是否循环播放,默认 false
68
45
  * @returns VodPlayer 实例
46
+ *
47
+ * @throws {TypeError} 当 `mediaFilePath` 不是非空字符串时抛出
69
48
  */
70
49
  static createVodPlayer(mediaFilePath: string, repeat?: boolean): VodPlayer;
71
50
  /**
@@ -100,61 +79,120 @@ export declare class VodPlayer {
100
79
  /**
101
80
  * 设置视频渲染的 DOM 容器
102
81
  *
103
- * @param view - HTML 元素,用于承载视频画面
82
+ * @param view - HTML 元素,用于承载视频画面;传 `null` 时仅解除当前 view 关联,
83
+ * 播放/渲染管线不受影响(可后续再次 `setView(newDom)` 切换容器)。
84
+ *
85
+ * @note 若调用方需要彻底停止渲染,请使用 {@link stop} 或 {@link destroy}。
104
86
  */
105
87
  setView(view: HTMLElement | null): void;
88
+ /**
89
+ * 预加载多媒体文件
90
+ *
91
+ * 预加载完成后会触发 {@link VodPlayerEvents.onVodPlayerLoaded} 事件,事件参数携带
92
+ * 媒体信息(时长、宽高);同时 C++ 层会产生首帧并推送到 JS 层的视频渲染管线,
93
+ * 在 view 上展示首帧画面。
94
+ *
95
+ * @example
96
+ * player.on(VodPlayerEvents.onVodPlayerLoaded, (durationMs, width, height) => {
97
+ * console.log('预加载完成:', durationMs, width, height);
98
+ * player.start();
99
+ * });
100
+ * player.preload();
101
+ *
102
+ * @note 可以先调用 preload 再调用 {@link start} 播放,也可以不调用 preload 直接调用 {@link start}。
103
+ * @note 若已经调用过 {@link start},再调用 preload 将无效。
104
+ */
105
+ preload(): void;
106
106
  /**
107
107
  * 开始播放
108
108
  *
109
+ * 支持的视频格式:mp4、mkv、mov。
110
+ * 支持的音频格式:opus、aac。
111
+ *
112
+ * 重复调用会被忽略。
113
+ *
114
+ * @note 可以先调用 {@link preload} 预加载完成后再调用 start,也可以跳过 preload 直接调用 start。
115
+ * @note 从 SDK 10.7 版本开始,需要通过 TXLiveBase#setLicence 设置 Licence 后方可成功播放,
116
+ * 否则将播放失败(黑屏),全局仅设置一次即可。
109
117
  */
110
118
  start(): void;
111
119
  /**
112
120
  * 停止播放
113
- *
114
121
  */
115
122
  stop(): void;
116
123
  /**
117
124
  * 暂停播放
125
+ *
126
+ * @note 仅在 {@link start} 之后调用才有效;未播放时调用会被忽略并打 warn 日志。
118
127
  */
119
128
  pause(): void;
120
129
  /**
121
130
  * 恢复播放
131
+ *
132
+ * @note 仅在 {@link start} 之后调用才有效;未播放时调用会被忽略并打 warn 日志。
122
133
  */
123
134
  resume(): void;
124
135
  /**
125
- * 跳转到指定播放位置
136
+ * 跳转到指定播放位置(seek)
126
137
  *
127
138
  * @param msPos - 目标位置,单位毫秒
139
+ *
140
+ * @note 调用前必须先 {@link preload} 或 {@link start},否则会被忽略并打 warn 日志。
128
141
  */
129
142
  seek(msPos: number): void;
130
143
  /**
131
- * 获取媒体文件总时长
144
+ * 无缝切换当前播放的媒体文件
145
+ *
146
+ * 在后台预加载新的媒体源,待新源就绪后无缝切换,避免切换时出现黑屏。
147
+ * 切换成功后触发 {@link VodPlayerEvents.onVodPlayerLoaded} 事件,携带新文件的媒体信息。
148
+ *
149
+ * @param newMediaFile - 新的媒体文件路径或 URL
150
+ *
151
+ * @note 若当前处于暂停状态,切换后仍保持暂停并展示新文件第一帧。
152
+ * @note 切换过程中调用 {@link stop} 会取消切换并停止当前播放。
153
+ * @note 调用前必须先 {@link preload} 或 {@link start},否则会被忽略并打 warn 日志。
154
+ * @note 传入空串会被忽略并打 warn 日志(C++ 层亦有同等保护)。
155
+ */
156
+ switchSource(newMediaFile: string): void;
157
+ /**
158
+ * 获取视频总时长
159
+ *
160
+ * @returns 总时长,单位毫秒;播放器已销毁时返回 0。
132
161
  *
133
- * @returns 总时长,单位毫秒
162
+ * @note 跨平台口径已统一为 `int64_t`(即使 Windows x64 下底层 `long` 是 32 位,
163
+ * binding 层也会显式扩到 64 位避免 ~49 天回绕);JS `number` 安全整数上限 2^53,
164
+ * 业务上点播时长不会触及。
134
165
  */
135
166
  getDuration(): number;
136
167
  /**
137
168
  * 获取视频宽度
138
169
  *
139
- * @returns 视频宽度(像素)
170
+ * @returns 视频宽度(像素);纯音频文件或播放器已销毁时返回 0
140
171
  */
141
172
  getWidth(): number;
142
173
  /**
143
174
  * 获取视频高度
144
175
  *
145
- * @returns 视频高度(像素)
176
+ * @returns 视频高度(像素);纯音频文件或播放器已销毁时返回 0
146
177
  */
147
178
  getHeight(): number;
148
179
  /**
149
- * 设置画面旋转角度
180
+ * 设置画面顺时针旋转角度
150
181
  *
151
- * @param rotation - 旋转角度,参考 TRTCVideoRotation 枚举
182
+ * 仅对窗口渲染模式生效。
183
+ *
184
+ * @param rotation - 旋转角度,参考 {@link TRTCVideoRotation} 枚举,
185
+ * 支持 TRTCVideoRotation0 / 90 / 180 / 270,默认 TRTCVideoRotation0
152
186
  */
153
187
  setRenderRotation(rotation: TRTCVideoRotation): void;
154
188
  /**
155
189
  * 设置画面填充模式
156
190
  *
157
- * @param mode - 填充模式,参考 TRTCVideoFillMode 枚举
191
+ * 仅对窗口渲染模式生效。
192
+ *
193
+ * @param mode - 填充模式,参考 {@link TRTCVideoFillMode} 枚举:
194
+ * - Fill:画面填满窗口,可能被拉伸或裁剪
195
+ * - Fit:画面按比例适配窗口,可能出现黑边(默认值)
158
196
  */
159
197
  setFillMode(mode: TRTCVideoFillMode): void;
160
198
  /**
@@ -172,28 +210,32 @@ export declare class VodPlayer {
172
210
  /**
173
211
  * 设置播放音量
174
212
  *
175
- * @param volume - 音量大小,取值范围 0-100
213
+ * @param volume - 音量大小,取值范围 [0, 150],100 为原始音量,默认值 100
176
214
  */
177
215
  setVolume(volume: number): void;
178
216
  /**
179
- * 开始推送视频流到 TRTC
217
+ * 向已关联的 TRTC 实例推送视频流
218
+ *
219
+ * @note 推流前播放器需已关联到 TRTC 实例(内部由 {@link start} 自动处理)。
180
220
  */
181
221
  publishVideo(): void;
182
222
  /**
183
- * 开始推送音频流到 TRTC
223
+ * 向已关联的 TRTC 实例推送音频流
224
+ *
225
+ * @note 绑定后音频播放由 TRTC 接管。
184
226
  */
185
227
  publishAudio(): void;
186
228
  /**
187
- * 停止推送视频流到 TRTC
229
+ * 停止向已关联的 TRTC 实例推送视频流
188
230
  */
189
231
  unpublishVideo(): void;
190
232
  /**
191
- * 停止推送音频流到 TRTC
233
+ * 停止向已关联的 TRTC 实例推送音频流
192
234
  */
193
235
  unpublishAudio(): void;
194
236
  /**
195
237
  * @private
196
- * 将点播播放器关联到 TRTC 实例(用于推流)
238
+ * 将点播播放器关联到 TRTC 实例(用于辅助流推流,绑定后音频播放由 TRTC 接管)
197
239
  */
198
240
  private attachTRTC;
199
241
  /**
@@ -201,9 +243,15 @@ export declare class VodPlayer {
201
243
  * 将点播播放器从 TRTC 实例分离
202
244
  */
203
245
  private detachTRTC;
246
+ /**
247
+ * 类型安全地发射事件,参数类型由 `VodPlayerEventMap` 推导。
248
+ * 通过 `setImmediate` 让 emit 异步化,避免在 N-API 回调栈中触发用户代码。
249
+ */
204
250
  private fire;
205
251
  private _createVideoRender;
206
252
  private _destroyVideoRender;
253
+ private _prepareRender;
254
+ private _teardownRender;
207
255
  private _setVideoRenderBuffer;
208
256
  private _addDataRenderCallback;
209
257
  private _removeDataRenderCallback;
@@ -1,29 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VodPlayer = exports.VodPlayerEvents = void 0;
3
+ exports.VodPlayer = void 0;
4
4
  const events_1 = require("events");
5
5
  const trtc_define_1 = require("./trtc_define");
6
6
  const video_render_1 = require("./Live/video-render");
7
7
  const logger_1 = require("./logger");
8
+ const types_1 = require("./types");
8
9
  const NodeTRTCEngine = require('../build/Release/trtc_electron_sdk.node');
9
- /**
10
- * VodPlayer 播放器事件枚举
11
- */
12
- var VodPlayerEvents;
13
- (function (VodPlayerEvents) {
14
- /** 播放开始,参数:msLength(总时长,毫秒) */
15
- VodPlayerEvents["onVodPlayerStarted"] = "onVodPlayerStarted";
16
- /** 播放进度更新,参数:msPos(当前进度,毫秒) */
17
- VodPlayerEvents["onVodPlayerProgress"] = "onVodPlayerProgress";
18
- /** 播放暂停 */
19
- VodPlayerEvents["onVodPlayerPaused"] = "onVodPlayerPaused";
20
- /** 播放恢复 */
21
- VodPlayerEvents["onVodPlayerResumed"] = "onVodPlayerResumed";
22
- /** 播放停止,参数:reason(0=用户主动停止,1=文件播放完,2=视频断流) */
23
- VodPlayerEvents["onVodPlayerStopped"] = "onVodPlayerStopped";
24
- /** 播放错误,参数:error(错误码) */
25
- VodPlayerEvents["onVodPlayerError"] = "onVodPlayerError";
26
- })(VodPlayerEvents = exports.VodPlayerEvents || (exports.VodPlayerEvents = {}));
27
10
  /**
28
11
  *
29
12
  * 腾讯云点播播放器
@@ -54,9 +37,10 @@ class VodPlayer {
54
37
  this._dataRenderCallback = this._dataRenderCallback.bind(this);
55
38
  this.nativeVodPlayer = new NodeTRTCEngine.NodeVodPlayer(mediaFilePath, repeat);
56
39
  this.mediaFilePath = mediaFilePath;
57
- this.repeat = repeat;
58
40
  this.isStarted = false;
59
41
  this.isDestroyed = false;
42
+ this.isRenderPrepared = false;
43
+ this._setBufferRetryCount = 0;
60
44
  this.videoRender = null;
61
45
  this._createVideoRender();
62
46
  this._initEventCallback();
@@ -65,11 +49,17 @@ class VodPlayer {
65
49
  /**
66
50
  * 创建 VodPlayer 实例
67
51
  *
68
- * @param mediaFilePath - 媒体文件路径(本地路径或在线 URL
52
+ * @param mediaFilePath - 媒体文件路径(本地绝对路径或在线 URL),必填且必须是非空字符串
69
53
  * @param repeat - 是否循环播放,默认 false
70
54
  * @returns VodPlayer 实例
55
+ *
56
+ * @throws {TypeError} 当 `mediaFilePath` 不是非空字符串时抛出
71
57
  */
72
58
  static createVodPlayer(mediaFilePath, repeat = false) {
59
+ if (typeof mediaFilePath !== 'string' || mediaFilePath.length === 0) {
60
+ // 空串或非字符串透传到 C++ 后 createTXVodPlayer 行为未定义,提前抛错让调用方立刻发现
61
+ throw new TypeError(`VodPlayer.createVodPlayer: mediaFilePath must be a non-empty string, got: ${JSON.stringify(mediaFilePath)}`);
62
+ }
73
63
  return new VodPlayer(mediaFilePath, repeat);
74
64
  }
75
65
  /**
@@ -112,23 +102,72 @@ class VodPlayer {
112
102
  /**
113
103
  * 设置视频渲染的 DOM 容器
114
104
  *
115
- * @param view - HTML 元素,用于承载视频画面
105
+ * @param view - HTML 元素,用于承载视频画面;传 `null` 时仅解除当前 view 关联,
106
+ * 播放/渲染管线不受影响(可后续再次 `setView(newDom)` 切换容器)。
107
+ *
108
+ * @note 若调用方需要彻底停止渲染,请使用 {@link stop} 或 {@link destroy}。
116
109
  */
117
110
  setView(view) {
118
- var _a;
111
+ var _a, _b;
119
112
  if (this.isDestroyed) {
120
113
  this.logger.warn('setView: player already destroyed');
121
114
  return;
122
115
  }
116
+ if (view === null) {
117
+ // 仅解除 view 关联,不下发 destroyRender / removeDataRenderCallback
118
+ // 否则后续 setView(newDom) 还需要重建整条渲染管线
119
+ this.logger.info('setView: view is null, detach current view');
120
+ (_a = this.videoRender) === null || _a === void 0 ? void 0 : _a.setRenderView(null);
121
+ return;
122
+ }
123
123
  this.logger.info(`setView view: ${view}`);
124
- (_a = this.videoRender) === null || _a === void 0 ? void 0 : _a.setRenderView(view);
124
+ (_b = this.videoRender) === null || _b === void 0 ? void 0 : _b.setRenderView(view);
125
+ }
126
+ /**
127
+ * 预加载多媒体文件
128
+ *
129
+ * 预加载完成后会触发 {@link VodPlayerEvents.onVodPlayerLoaded} 事件,事件参数携带
130
+ * 媒体信息(时长、宽高);同时 C++ 层会产生首帧并推送到 JS 层的视频渲染管线,
131
+ * 在 view 上展示首帧画面。
132
+ *
133
+ * @example
134
+ * player.on(VodPlayerEvents.onVodPlayerLoaded, (durationMs, width, height) => {
135
+ * console.log('预加载完成:', durationMs, width, height);
136
+ * player.start();
137
+ * });
138
+ * player.preload();
139
+ *
140
+ * @note 可以先调用 preload 再调用 {@link start} 播放,也可以不调用 preload 直接调用 {@link start}。
141
+ * @note 若已经调用过 {@link start},再调用 preload 将无效。
142
+ */
143
+ preload() {
144
+ var _a;
145
+ if (this.isDestroyed) {
146
+ this.logger.warn('preload: player already destroyed');
147
+ return;
148
+ }
149
+ if (this.isStarted) {
150
+ this.logger.warn('preload: already started, ignored');
151
+ return;
152
+ }
153
+ this.logger.info(`preload mediaFile: ${this.mediaFilePath}`);
154
+ this._prepareRender();
155
+ (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.preload();
125
156
  }
126
157
  /**
127
158
  * 开始播放
128
159
  *
160
+ * 支持的视频格式:mp4、mkv、mov。
161
+ * 支持的音频格式:opus、aac。
162
+ *
163
+ * 重复调用会被忽略。
164
+ *
165
+ * @note 可以先调用 {@link preload} 预加载完成后再调用 start,也可以跳过 preload 直接调用 start。
166
+ * @note 从 SDK 10.7 版本开始,需要通过 TXLiveBase#setLicence 设置 Licence 后方可成功播放,
167
+ * 否则将播放失败(黑屏),全局仅设置一次即可。
129
168
  */
130
169
  start() {
131
- var _a, _b;
170
+ var _a;
132
171
  if (this.isDestroyed) {
133
172
  this.logger.warn('start: player already destroyed');
134
173
  return;
@@ -138,35 +177,35 @@ class VodPlayer {
138
177
  this.logger.warn('start: already started, ignored');
139
178
  return;
140
179
  }
141
- (_a = this.videoRender) === null || _a === void 0 ? void 0 : _a.createRender();
142
- this._setVideoRenderBuffer();
143
- this._addDataRenderCallback();
180
+ this._prepareRender();
144
181
  this.attachTRTC();
145
- (_b = this.nativeVodPlayer) === null || _b === void 0 ? void 0 : _b.start();
182
+ (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.start();
146
183
  this.isStarted = true;
147
184
  }
148
185
  /**
149
186
  * 停止播放
150
- *
151
187
  */
152
188
  stop() {
153
- var _a, _b;
189
+ var _a;
154
190
  if (this.isDestroyed) {
155
191
  this.logger.warn('stop: player already destroyed');
156
192
  return;
157
193
  }
158
194
  this.logger.info('stop');
159
- if (!this.isStarted) {
195
+ if (!this.isStarted && !this.isRenderPrepared) {
160
196
  return;
161
197
  }
162
- this.isStarted = false;
163
- (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.stop();
164
- this.detachTRTC();
165
- (_b = this.videoRender) === null || _b === void 0 ? void 0 : _b.destroyRender();
166
- this._removeDataRenderCallback();
198
+ if (this.isStarted) {
199
+ this.isStarted = false;
200
+ (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.stop();
201
+ this.detachTRTC();
202
+ }
203
+ this._teardownRender();
167
204
  }
168
205
  /**
169
206
  * 暂停播放
207
+ *
208
+ * @note 仅在 {@link start} 之后调用才有效;未播放时调用会被忽略并打 warn 日志。
170
209
  */
171
210
  pause() {
172
211
  var _a;
@@ -174,11 +213,17 @@ class VodPlayer {
174
213
  this.logger.warn('pause: player already destroyed');
175
214
  return;
176
215
  }
216
+ if (!this.isStarted) {
217
+ this.logger.warn('pause: player not started, ignored');
218
+ return;
219
+ }
177
220
  this.logger.info('pause');
178
221
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.pause();
179
222
  }
180
223
  /**
181
224
  * 恢复播放
225
+ *
226
+ * @note 仅在 {@link start} 之后调用才有效;未播放时调用会被忽略并打 warn 日志。
182
227
  */
183
228
  resume() {
184
229
  var _a;
@@ -186,13 +231,19 @@ class VodPlayer {
186
231
  this.logger.warn('resume: player already destroyed');
187
232
  return;
188
233
  }
234
+ if (!this.isStarted) {
235
+ this.logger.warn('resume: player not started, ignored');
236
+ return;
237
+ }
189
238
  this.logger.info('resume');
190
239
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.resume();
191
240
  }
192
241
  /**
193
- * 跳转到指定播放位置
242
+ * 跳转到指定播放位置(seek)
194
243
  *
195
244
  * @param msPos - 目标位置,单位毫秒
245
+ *
246
+ * @note 调用前必须先 {@link preload} 或 {@link start},否则会被忽略并打 warn 日志。
196
247
  */
197
248
  seek(msPos) {
198
249
  var _a;
@@ -200,13 +251,54 @@ class VodPlayer {
200
251
  this.logger.warn('seek: player already destroyed');
201
252
  return;
202
253
  }
254
+ if (!this.isStarted && !this.isRenderPrepared) {
255
+ this.logger.warn(`seek: player not preloaded or started, ignored (msPos=${msPos})`);
256
+ return;
257
+ }
203
258
  this.logger.info(`seek msPos: ${msPos}`);
204
259
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.seek(msPos);
205
260
  }
206
261
  /**
207
- * 获取媒体文件总时长
262
+ * 无缝切换当前播放的媒体文件
263
+ *
264
+ * 在后台预加载新的媒体源,待新源就绪后无缝切换,避免切换时出现黑屏。
265
+ * 切换成功后触发 {@link VodPlayerEvents.onVodPlayerLoaded} 事件,携带新文件的媒体信息。
266
+ *
267
+ * @param newMediaFile - 新的媒体文件路径或 URL
268
+ *
269
+ * @note 若当前处于暂停状态,切换后仍保持暂停并展示新文件第一帧。
270
+ * @note 切换过程中调用 {@link stop} 会取消切换并停止当前播放。
271
+ * @note 调用前必须先 {@link preload} 或 {@link start},否则会被忽略并打 warn 日志。
272
+ * @note 传入空串会被忽略并打 warn 日志(C++ 层亦有同等保护)。
273
+ */
274
+ switchSource(newMediaFile) {
275
+ var _a;
276
+ if (this.isDestroyed) {
277
+ this.logger.warn('switchSource: player already destroyed');
278
+ return;
279
+ }
280
+ if (!newMediaFile) {
281
+ this.logger.warn('switchSource: newMediaFile is empty, ignored');
282
+ return;
283
+ }
284
+ if (!this.isStarted && !this.isRenderPrepared) {
285
+ this.logger.warn(`switchSource: player not preloaded or started, ignored (newMediaFile=${newMediaFile})`);
286
+ return;
287
+ }
288
+ this.logger.info(`switchSource newMediaFile: ${newMediaFile}`);
289
+ this.mediaFilePath = newMediaFile;
290
+ // 切换源后如果出现 buffer 尺寸变化,需要让 _dataRenderCallback 退避计数重置
291
+ this._setBufferRetryCount = 0;
292
+ (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.switchSource(newMediaFile);
293
+ }
294
+ /**
295
+ * 获取视频总时长
296
+ *
297
+ * @returns 总时长,单位毫秒;播放器已销毁时返回 0。
208
298
  *
209
- * @returns 总时长,单位毫秒
299
+ * @note 跨平台口径已统一为 `int64_t`(即使 Windows x64 下底层 `long` 是 32 位,
300
+ * binding 层也会显式扩到 64 位避免 ~49 天回绕);JS `number` 安全整数上限 2^53,
301
+ * 业务上点播时长不会触及。
210
302
  */
211
303
  getDuration() {
212
304
  var _a, _b;
@@ -219,7 +311,7 @@ class VodPlayer {
219
311
  /**
220
312
  * 获取视频宽度
221
313
  *
222
- * @returns 视频宽度(像素)
314
+ * @returns 视频宽度(像素);纯音频文件或播放器已销毁时返回 0
223
315
  */
224
316
  getWidth() {
225
317
  var _a, _b;
@@ -232,7 +324,7 @@ class VodPlayer {
232
324
  /**
233
325
  * 获取视频高度
234
326
  *
235
- * @returns 视频高度(像素)
327
+ * @returns 视频高度(像素);纯音频文件或播放器已销毁时返回 0
236
328
  */
237
329
  getHeight() {
238
330
  var _a, _b;
@@ -243,9 +335,12 @@ class VodPlayer {
243
335
  return (_b = (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.getHeight()) !== null && _b !== void 0 ? _b : 0;
244
336
  }
245
337
  /**
246
- * 设置画面旋转角度
338
+ * 设置画面顺时针旋转角度
339
+ *
340
+ * 仅对窗口渲染模式生效。
247
341
  *
248
- * @param rotation - 旋转角度,参考 TRTCVideoRotation 枚举
342
+ * @param rotation - 旋转角度,参考 {@link TRTCVideoRotation} 枚举,
343
+ * 支持 TRTCVideoRotation0 / 90 / 180 / 270,默认 TRTCVideoRotation0
249
344
  */
250
345
  setRenderRotation(rotation) {
251
346
  var _a;
@@ -259,7 +354,11 @@ class VodPlayer {
259
354
  /**
260
355
  * 设置画面填充模式
261
356
  *
262
- * @param mode - 填充模式,参考 TRTCVideoFillMode 枚举
357
+ * 仅对窗口渲染模式生效。
358
+ *
359
+ * @param mode - 填充模式,参考 {@link TRTCVideoFillMode} 枚举:
360
+ * - Fill:画面填满窗口,可能被拉伸或裁剪
361
+ * - Fit:画面按比例适配窗口,可能出现黑边(默认值)
263
362
  */
264
363
  setFillMode(mode) {
265
364
  var _a, _b;
@@ -302,7 +401,7 @@ class VodPlayer {
302
401
  /**
303
402
  * 设置播放音量
304
403
  *
305
- * @param volume - 音量大小,取值范围 0-100
404
+ * @param volume - 音量大小,取值范围 [0, 150],100 为原始音量,默认值 100
306
405
  */
307
406
  setVolume(volume) {
308
407
  var _a;
@@ -314,7 +413,9 @@ class VodPlayer {
314
413
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.setVolume(volume);
315
414
  }
316
415
  /**
317
- * 开始推送视频流到 TRTC
416
+ * 向已关联的 TRTC 实例推送视频流
417
+ *
418
+ * @note 推流前播放器需已关联到 TRTC 实例(内部由 {@link start} 自动处理)。
318
419
  */
319
420
  publishVideo() {
320
421
  var _a;
@@ -326,7 +427,9 @@ class VodPlayer {
326
427
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.publishVideo();
327
428
  }
328
429
  /**
329
- * 开始推送音频流到 TRTC
430
+ * 向已关联的 TRTC 实例推送音频流
431
+ *
432
+ * @note 绑定后音频播放由 TRTC 接管。
330
433
  */
331
434
  publishAudio() {
332
435
  var _a;
@@ -338,7 +441,7 @@ class VodPlayer {
338
441
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.publishAudio();
339
442
  }
340
443
  /**
341
- * 停止推送视频流到 TRTC
444
+ * 停止向已关联的 TRTC 实例推送视频流
342
445
  */
343
446
  unpublishVideo() {
344
447
  var _a;
@@ -350,7 +453,7 @@ class VodPlayer {
350
453
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.unpublishVideo();
351
454
  }
352
455
  /**
353
- * 停止推送音频流到 TRTC
456
+ * 停止向已关联的 TRTC 实例推送音频流
354
457
  */
355
458
  unpublishAudio() {
356
459
  var _a;
@@ -364,7 +467,7 @@ class VodPlayer {
364
467
  // ─── 内部方法 ───────────────────────────────────────────────
365
468
  /**
366
469
  * @private
367
- * 将点播播放器关联到 TRTC 实例(用于推流)
470
+ * 将点播播放器关联到 TRTC 实例(用于辅助流推流,绑定后音频播放由 TRTC 接管)
368
471
  */
369
472
  attachTRTC() {
370
473
  var _a;
@@ -388,6 +491,10 @@ class VodPlayer {
388
491
  this.logger.info('detachTRTC');
389
492
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.detachTRTC();
390
493
  }
494
+ /**
495
+ * 类型安全地发射事件,参数类型由 `VodPlayerEventMap` 推导。
496
+ * 通过 `setImmediate` 让 emit 异步化,避免在 N-API 回调栈中触发用户代码。
497
+ */
391
498
  fire(event, ...args) {
392
499
  setImmediate(() => {
393
500
  this.eventEmitter.emit(event, ...args);
@@ -401,11 +508,32 @@ class VodPlayer {
401
508
  this.videoRender.setVideoPixelFormat(trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32);
402
509
  }
403
510
  _destroyVideoRender() {
511
+ this._teardownRender();
404
512
  if (this.videoRender) {
405
513
  this.videoRender.destroy();
406
514
  this.videoRender = null;
407
515
  }
516
+ }
517
+ _prepareRender() {
518
+ var _a;
519
+ if (this.isRenderPrepared) {
520
+ return;
521
+ }
522
+ (_a = this.videoRender) === null || _a === void 0 ? void 0 : _a.createRender();
523
+ this._setVideoRenderBuffer();
524
+ this._addDataRenderCallback();
525
+ this.isRenderPrepared = true;
526
+ this._setBufferRetryCount = 0;
527
+ }
528
+ _teardownRender() {
529
+ var _a;
530
+ if (!this.isRenderPrepared) {
531
+ return;
532
+ }
533
+ (_a = this.videoRender) === null || _a === void 0 ? void 0 : _a.destroyRender();
408
534
  this._removeDataRenderCallback();
535
+ this.isRenderPrepared = false;
536
+ this._setBufferRetryCount = 0;
409
537
  }
410
538
  _setVideoRenderBuffer() {
411
539
  var _a;
@@ -424,47 +552,69 @@ class VodPlayer {
424
552
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.setDataCallback(null);
425
553
  }
426
554
  _dataRenderCallback(args) {
427
- if (this.videoRender) {
428
- const [userId, type, width, height, timestamp, rotation, valid, bufferId] = args;
429
- const result = this.videoRender.renderVideoData(userId, type, width, height, timestamp, rotation, valid, bufferId);
430
- if (!result) {
431
- this.logger.info(`_dataRenderCallback: renderVideoData returned false, re-setting video buffer`);
432
- this._setVideoRenderBuffer();
555
+ if (!this.videoRender) {
556
+ this.logger.warn('_dataRenderCallback: videoRender is null, frame dropped');
557
+ return;
558
+ }
559
+ const [userId, type, width, height, timestamp, rotation, valid, bufferId] = args;
560
+ const result = this.videoRender.renderVideoData(userId, type, width, height, timestamp, rotation, valid, bufferId);
561
+ if (result) {
562
+ // 渲染成功,重置重试计数,避免长期累计后误触发降级
563
+ if (this._setBufferRetryCount !== 0) {
564
+ this._setBufferRetryCount = 0;
433
565
  }
566
+ return;
434
567
  }
435
- else {
436
- this.logger.warn('_dataRenderCallback: videoRender is null, frame dropped');
568
+ // renderVideoData 返回 false 通常意味着 buffer 尺寸 / 格式不匹配,需要重新 setVideoBuffer。
569
+ // 但若底层持续失败(例如尺寸一直对不上),不能每帧都重置 buffer——30fps 下每秒会触发 30
570
+ // 跨 N-API 调用,引发明显性能抖动。这里加重试上限:前 N 次正常重置,超过后退避到周期性日志。
571
+ this._setBufferRetryCount += 1;
572
+ if (this._setBufferRetryCount <= VodPlayer.MAX_BUFFER_RETRY) {
573
+ this.logger.info(`_dataRenderCallback: renderVideoData returned false (retry ${this._setBufferRetryCount}/${VodPlayer.MAX_BUFFER_RETRY}), re-setting video buffer`);
574
+ this._setVideoRenderBuffer();
575
+ }
576
+ else if (this._setBufferRetryCount % VodPlayer.BUFFER_RETRY_LOG_INTERVAL === 0) {
577
+ this.logger.warn(`_dataRenderCallback: renderVideoData keeps failing for ${this._setBufferRetryCount} frames, giving up frame`);
437
578
  }
438
579
  }
439
580
  _initEventCallback() {
440
581
  var _a;
441
582
  (_a = this.nativeVodPlayer) === null || _a === void 0 ? void 0 : _a.setEventCallback((args) => {
442
- const key = args[0];
443
- const data = args[1];
444
- switch (key) {
445
- case VodPlayerEvents.onVodPlayerStarted:
446
- this.fire(VodPlayerEvents.onVodPlayerStarted, data.msLength);
583
+ switch (args[0]) {
584
+ case types_1.VodPlayerEvents.onVodPlayerStarted:
585
+ this.fire(types_1.VodPlayerEvents.onVodPlayerStarted, args[1].msLength);
586
+ break;
587
+ case types_1.VodPlayerEvents.onVodPlayerLoaded: {
588
+ const data = args[1];
589
+ this.fire(types_1.VodPlayerEvents.onVodPlayerLoaded, data.durationMs, data.width, data.height);
447
590
  break;
448
- case VodPlayerEvents.onVodPlayerProgress:
449
- this.fire(VodPlayerEvents.onVodPlayerProgress, data.msPos);
591
+ }
592
+ case types_1.VodPlayerEvents.onVodPlayerProgress:
593
+ this.fire(types_1.VodPlayerEvents.onVodPlayerProgress, args[1].msPos);
450
594
  break;
451
- case VodPlayerEvents.onVodPlayerPaused:
452
- this.fire(VodPlayerEvents.onVodPlayerPaused);
595
+ case types_1.VodPlayerEvents.onVodPlayerPaused:
596
+ this.fire(types_1.VodPlayerEvents.onVodPlayerPaused);
453
597
  break;
454
- case VodPlayerEvents.onVodPlayerResumed:
455
- this.fire(VodPlayerEvents.onVodPlayerResumed);
598
+ case types_1.VodPlayerEvents.onVodPlayerResumed:
599
+ this.fire(types_1.VodPlayerEvents.onVodPlayerResumed);
456
600
  break;
457
- case VodPlayerEvents.onVodPlayerStopped:
458
- this.fire(VodPlayerEvents.onVodPlayerStopped, data.reason);
601
+ case types_1.VodPlayerEvents.onVodPlayerStopped:
602
+ this.fire(types_1.VodPlayerEvents.onVodPlayerStopped, args[1].reason);
459
603
  break;
460
- case VodPlayerEvents.onVodPlayerError:
461
- this.fire(VodPlayerEvents.onVodPlayerError, data.error);
604
+ case types_1.VodPlayerEvents.onVodPlayerError:
605
+ this.fire(types_1.VodPlayerEvents.onVodPlayerError, args[1].error);
462
606
  break;
463
- default:
464
- this.logger.warn(`unknown vod player event: ${key}`);
607
+ default: {
608
+ const exhaustiveCheck = args[0];
609
+ this.logger.warn(`unknown vod player event: ${String(exhaustiveCheck)}`);
465
610
  break;
611
+ }
466
612
  }
467
613
  });
468
614
  }
469
615
  }
470
616
  exports.VodPlayer = VodPlayer;
617
+ /** `_dataRenderCallback` 中允许连续重置 video buffer 的最大次数,超过后退化为周期日志 */
618
+ VodPlayer.MAX_BUFFER_RETRY = 3;
619
+ /** 重试上限达到后,每隔多少帧打一次 warn 日志(按 30fps 估算 ≈ 2 秒一次) */
620
+ VodPlayer.BUFFER_RETRY_LOG_INTERVAL = 60;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trtc-electron-sdk",
3
- "version": "13.3.800-alpha.0",
3
+ "version": "13.3.800-alpha.2",
4
4
  "description": "trtc electron sdk",
5
5
  "main": "./liteav/index.js",
6
6
  "types": "./liteav/index.d.ts",