nertc-electron-sdk
Version:
nertc-electron-sdk
158 lines (132 loc) • 5.79 kB
text/typescript
import { GlRenderer, SoftwareRenderer, IRenderer } from './index';
export type StreamType = 'main' | 'sub' | 'third' | 'fourth';
export interface DisplayConfig {
/** 视频缩放模式:0=等比缩放(Fit), 1=非等比填满(Fill), 2=等比裁剪(Crop) */
scalingMode?: number;
/** JS 渲染层镜像(CSS transform rotateY 180deg),与 C++ SDK 镜像并存 */
mirrorEnabled?: boolean;
/** 视频 letterbox 区域背景色,CSS 颜色字符串,默认 'black' */
backgroundColor?: string;
}
interface RendererEntry {
renderer: IRenderer;
config: DisplayConfig;
}
/**
* RendererManager — 渲染器单例管理器(框架无关)
*
* 职责:
* - 管理所有视频流的渲染器实例(按 uid + streamType 索引)
* - 统一管理 DisplayConfig(scalingMode / mirrorEnabled / backgroundColor)
* - 对 React 或其他 UI 框架提供独立的动态更新接口
*
* 与 C++ SDK mirror 的关系:
* - C++ setLocalVideoMirrorMode / setLocalVideoMirrorModeWithType:影响帧数据 mirror 标志,
* 决定远端用户所见(编码级别),同时也影响本地显示(通过帧头 mirror 位)
* - RendererManager.setMirrorMode:JS 层 CSS transform,纯本地显示覆盖,
* 不影响编码,不影响远端所见。forceMirror !== undefined 时优先于帧数据 mirror 标志
*/
export class RendererManager {
private static _instance: RendererManager;
private entries: Map<string, RendererEntry> = new Map();
private renderMode: number = 1; // 1=WebGL(GlRenderer), 2=Software(SoftwareRenderer)
private constructor() {}
static getInstance(): RendererManager {
if (!RendererManager._instance) {
RendererManager._instance = new RendererManager();
}
return RendererManager._instance;
}
private key(uid: string, streamType: StreamType): string {
return `${uid}-${streamType}`;
}
/** 设置渲染器类型(1=WebGL, 2=Software),在 bind 之前调用 */
setRenderMode(mode: number): void {
this.renderMode = mode;
}
/**
* 绑定渲染器到 DOM 元素,并应用初始 DisplayConfig
* 如果该 (uid, streamType) 已有渲染器,先销毁再重建
*/
bind(uid: string, streamType: StreamType, element: Element, config?: DisplayConfig): void {
const k = this.key(uid, streamType);
// 销毁已有渲染器
if (this.entries.has(k)) {
try { this.entries.get(k)!.renderer.unbind(); } catch (e) { /* ignore */ }
this.entries.delete(k);
}
const renderer: IRenderer = this.renderMode === 2
? new (SoftwareRenderer as any)()
: new GlRenderer();
renderer.bind(element);
const cfg: DisplayConfig = {
scalingMode: 0,
mirrorEnabled: false,
backgroundColor: 'black',
...config,
};
renderer.setContentMode(cfg.scalingMode ?? 0);
renderer.setMirrorMode(cfg.mirrorEnabled ?? false);
renderer.setBackgroundColor(cfg.backgroundColor ?? 'black');
this.entries.set(k, { renderer, config: cfg });
}
/** 解绑并销毁渲染器 */
unbind(uid: string, streamType: StreamType): void {
const k = this.key(uid, streamType);
if (this.entries.has(k)) {
try { this.entries.get(k)!.renderer.unbind(); } catch (e) { /* ignore */ }
this.entries.delete(k);
}
}
/** 推入视频帧数据(由 SDK 帧回调调用) */
drawFrame(uid: string, streamType: StreamType, imageData: any): void {
this.entries.get(this.key(uid, streamType))?.renderer.drawFrame(imageData);
}
// ── 独立动态更新接口 ──────────────────────────────────────────────────
/** 动态更新缩放模式(0=Fit, 1=Fill, 2=Crop) */
setScalingMode(uid: string, streamType: StreamType, mode: number): void {
const entry = this.entries.get(this.key(uid, streamType));
if (entry) {
entry.renderer.setContentMode(mode);
entry.renderer.refreshCanvas();
entry.config.scalingMode = mode;
}
}
/**
* 动态更新 JS 层镜像(CSS transform)
* 与 C++ SDK 的 setLocalVideoMirrorMode 并存,互不影响
*/
setMirrorMode(uid: string, streamType: StreamType, enabled: boolean): void {
const entry = this.entries.get(this.key(uid, streamType));
if (entry) {
entry.renderer.setMirrorMode(enabled);
entry.config.mirrorEnabled = enabled;
}
}
/** 动态更新背景色(letterbox 区域),立即生效 */
setBackgroundColor(uid: string, streamType: StreamType, color: string): void {
const entry = this.entries.get(this.key(uid, streamType));
if (entry) {
entry.renderer.setBackgroundColor(color);
entry.config.backgroundColor = color;
}
}
/** 批量更新 DisplayConfig */
setConfig(uid: string, streamType: StreamType, config: DisplayConfig): void {
if (config.scalingMode !== undefined) this.setScalingMode(uid, streamType, config.scalingMode);
if (config.mirrorEnabled !== undefined) this.setMirrorMode(uid, streamType, config.mirrorEnabled);
if (config.backgroundColor !== undefined) this.setBackgroundColor(uid, streamType, config.backgroundColor);
}
/** 读取当前 DisplayConfig(返回副本) */
getConfig(uid: string, streamType: StreamType): DisplayConfig | null {
const entry = this.entries.get(this.key(uid, streamType));
return entry ? { ...entry.config } : null;
}
hasRenderer(uid: string, streamType: StreamType): boolean {
return this.entries.has(this.key(uid, streamType));
}
/** 仅供调试:获取渲染器实例 */
getRenderer(uid: string, streamType: StreamType): IRenderer | null {
return this.entries.get(this.key(uid, streamType))?.renderer ?? null;
}
}