Electron API 说明
Electron SDK 在 Main 进程中连接设备、播放音视频、发送对讲音频并收发消息。安装、窗口隔离和打包配置见 Electron SDK 接入。
初始化与错误
TiRtc.init({ appId: 'your-app-id' }); // app.whenReady() 之后
// 释放所有连接和媒体对象之后:
TiRtc.shutdown();大部分控制方法同步返回 void;正常返回表示操作被受理,SDK 失败抛出 TiRtcError,参数类型错误可能抛出 TypeError。TiRtcError.code 是字符串类别,nativeCode 是数字错误码。异步方法返回 Promise,必须捕获拒绝;状态与错误回调继续反映后续运行结果。
连接与播放
已完成 TiRtc.init(),窗口 window 为 Main 持有的 BrowserWindow。以下会话对象创建音频、视频输出并发起连接。先持有实例,再调用 start(),使启动失败时也能处理资源清理。
import type { BrowserWindow } from 'electron';
import {
TiRtcConn, TiRtcAudioOutput, TiRtcVideoOutput, TiVideoView,
} from 'tirtc-electron';
export class PlaybackSession {
private conn = new TiRtcConn();
private audio: TiRtcAudioOutput | undefined;
private video: TiRtcVideoOutput | undefined;
private view: TiVideoView | undefined;
start(window: BrowserWindow, remoteId: string, token: string): void {
this.audio = new TiRtcAudioOutput();
this.video = new TiRtcVideoOutput();
this.view = new TiVideoView(window, { x: 0, y: 0, width: 640, height: 360 });
this.audio.onError = error => console.error('audio', error.code, error.nativeCode);
this.video.onError = error => console.error('video', error.code, error.nativeCode);
this.audio.onStateChanged = state => console.info('audio', state);
this.video.onStateChanged = state => console.info('video', state);
this.audio.attach(this.conn, 10);
this.video.mount(this.view);
this.video.attach(this.conn, 11);
this.conn.onStateChanged = (state, error) => {
if (error) console.error('connection', error.code, error.nativeCode);
if (state === 'connected') {
try { this.conn.subscribeAudio(10); this.conn.subscribeVideo(11); }
catch (error) { console.error('subscribe', error); }
}
};
this.conn.connect({ remoteId, token });
}
close(): void {
// 清理失败时保留实例,由业务控制流程处理或重试。
if (this.video) {
this.video.detach(); this.video.unmount(); this.video.dispose(); this.video = undefined;
}
if (this.audio) {
this.audio.detach(); this.audio.dispose(); this.audio = undefined;
}
if (this.view) { this.view.dispose(); this.view = undefined; }
this.conn.disconnect();
this.conn.dispose();
}
}在 Main 控制流程中创建 const session = new PlaybackSession(),再调用 session.start(window, deviceId, token);启动失败或业务结束时调用 session.close()。每次调用都用 try/catch 处理异常;清理失败时保留 session,处理或重试成功后再关闭 SDK。一个会话实例只启动一轮,成功关闭后创建新实例。不要在 SDK 回调中同步关闭会话。
连接成功以状态 connected 为准;音频 playing、视频 rendering 表示输出已推进。播放不需要额外的 play()。
Stream ID 为 0..15,发送、输出绑定和订阅保持一致,同一方向不同音视频流不能共用编号。示例 10 / 11 可按业务修改。TiVideoView.setBounds(rectangle) 调整原生画面区域;分辨率变化由 video.onRenderSizeChanged 通知。
Connection 控制与消息
| 用途 | 方法 / 回调 |
|---|---|
| 建连与断开 | connect({remoteId, token})、disconnect()、dispose() |
| 音频订阅 | subscribeAudio(id)、unsubscribeAudio(id) |
| 视频订阅 | subscribeVideo(id)、unsubscribeVideo(id)、requestVideoKeyframe(id) |
| 命令 | sendCommand(commandId, data: Uint8Array)、onCommand |
| 流消息 | sendStreamMessage({streamId, timestampMs, data})、onStreamMessage |
自定义命令字从 0x2001 起;流消息时间戳为非负 uint32 毫秒。用 try/catch 处理同步调用失败。
Audio Input
自定义音频输入需使用预发布包,安装方式见 Electron SDK 接入。
按任务选择麦克风对讲、发送外部 PCM或处理麦克风音频后发送。
interface TiRtcAudioInputOptions {
media?: number; // 1 PCM、2 G.711 A-law(默认)、3 AAC、4 Opus、5 AMR-NB
sampleRateHz?: 8000 | 16000; // 默认 16000;AMR-NB 仅 8000
channels?: 1;
mode?: 'builtIn' | 'custom'; // 默认 builtIn
captureEnabled?: boolean; // 默认 true
aecMode?: 'disabled' | 'enabled';
agcLevel?: 'disabled' | 'low' | 'medium' | 'high';
ansLevel?: 'disabled' | 'low' | 'medium' | 'high';
}默认模式由 SDK 采集、编码和发送。custom + false 使用外部 PCM,不打开麦克风;custom + true 通过 onPcmCaptured 回调 SDK 采集的 PCM,应用处理后必须显式提交。使用 SDK 麦克风前,应用取得系统权限。
| 操作 | 签名 |
|---|---|
| 创建与配置 | new TiRtcAudioInput()、setOptions(options): void |
| 绑定与启动 | attach(conn, streamId): void、start(): void |
| PCM 回调 | onPcmCaptured: ((pcm: Uint8Array) => void) | null |
| 提交 | submitPcm(pcm: Uint8Array): void |
| 停止与释放 | stop(): void、detach(): void、dispose(): void |
| 状态与错误 | state、onStateChanged、onError |
运行时不能修改参数;停止后可修改同一输入方式的参数,改变 mode 或 captureEnabled 还需先解绑。每次 setOptions 应显式传 media,省略时回到 G.711 A-law;其他省略字段保留已接受值。
PCM 为 S16LE、单声道、8 / 16 kHz,与配置一致。每次非空、偶数字节,不限固定帧长;单输入总预算 1 MiB。提交返回前复制当前 Uint8Array 的有效范围,成功不等于远端收到。暂满抛出 audio-input-buffer-full(6139),单次过大为 range-too-large(6117),本块均未受理。回调在 Main JavaScript 线程执行,收到 SDK 软件 3A 之前的 PCM;修改回调数据不会自动发送。
停止前先结束应用生产 / 处理任务。stop() 取消剩余数据,不等待排空,保留绑定供再次启动;停止成功后 detach()、dispose() 最终释放。已停止时可能抛出 not-started,处理后可继续清理。不要在 PCM 回调内同步停止或释放,安排到回调结束后的控制流程。
输出、录制与截图
TiRtcAudioOutput.setOptions(...)配置音频处理及缓冲;setVolume(percent)调整播放音量。TiRtcVideoOutput.setOptions(...)配置解码偏好及缓冲;mount(view)/unmount()挂载 / 解除原生视图。conn.startRecording({videoStreamId: 11, audioStreamId: 10})返回TiRtcRecordingTask;保持订阅送流,await task.stop()取得 MP4 文件。- 解码有可用视频帧后,
await video.takeSnapshot()取得 JPEG 文件。 - 文件提供
path,先用 Node.js 文件 API 复制到业务目录,再await file.delete()清理临时源。业务不需要音频时省略audioStreamId。
释放与日志
结束应用侧音频源及录制任务,再按输入停止 / 解绑 / 释放、输出解绑 / 解除视图 / 释放、视图释放、连接断开 / 释放、TiRtc.shutdown() 的顺序清理。每步失败时处理异常,保留对象引用,避免继续销毁仍在使用的父对象。
不要在 SDK 回调中同步销毁同一资源树,可通过 setImmediate 安排控制动作。回调自行捕获异常,避免异常进入事件循环。需要协助排查时,await TiRtcLogging.upload() 返回日志 ID。