本文介绍如何实现音频数据的自定义处理
原理说明
TRTC Electron SDK 从 11.9.604 版本开始,支持以动态库插件的方式,实现用户对音频数据的自定义处理。
实现步骤:
- 基于 TRTC Electron SDK 提供的 C++ 头文件,开发音频自定义处理库。
- Javascript 层调用
addPlugin()
接口添加音频自定义处理插件,传入构建好的音频自定义处理库文件路径;再用插件的 enable() 接口开启音频自定义处理。
开发音频自定义处理插件(C++)
SDK 提供了两个 C++ 头文件实现音频自定义处理:
ITRTCPlugin.h
头文件声明了插件对象的创建、销毁、初始化、设置参数等接口,用于管理插件的生命周期。IAudioFramePlugin.h
头文件定义了监听 SDK 音频自定义回调的接口,用于完成对音频数据的自定义处理。
AudioFramePlugin.zip是一个示例工程,实现了一个简单的音频自定义处理插件,支持 Windows 和 Mac OS 下构建。
ITRTCPlugin.h
class ITRTCPlugin {
public:
virtual ~ITRTCPlugin() {}
/**
* 初始化插件
*
* 创建插件后,会立刻触发该初始化函数,用户可以在这里做一些初始化操作。
* @return true: 插件初始化成功,false: 插件初始化失败。
* 如果返回 false 表示初始化失败,Javascript 层通过 setPluginCallback() 函数设置回调函数会收到错误码:-4.
*/
virtual bool init() = 0;
/**
* 反初始化插件
*
* 插件销毁时,会触发该函数,用户可以在这里做一些清理操作。
*
* @return true: 插件反初始化成功,false: 插件反初始化失败。
*/
virtual bool uninit() = 0;
/**
* 资源加载
*
* 会在 init() 函数之后被调用。
*
* @param path 插件资源目录。该目录为当前插件库文件所在目录,如果插件有其它资源文件依赖,可以放在插件库文件同目录下,在这里加载。
* @return true: 资源加载成功,false: 资源加载失败。
* 如果返回 false 表示资源加载失败,Javascript 层通过 setPluginCallback() 函数设置回调函数会收到错误码:-5.
*/
virtual bool load(const char* path) = 0;
/**
* 资源卸载
*
* 会在 uninit() 函数之前被调用。
*/
virtual bool unload() = 0;
/**
* 开启/关闭当前插件
*
* Javascript 层的 TRTCPluginInfo.enable/disable() 接口调用会触发该函数。
*
* @param enabled true: 开启插件,false: 关闭插件。
* @return true: 成功开启/关闭插件,false: 失败。
*/
virtual bool enable(bool enabled) = 0;
/**
* 设置插件参数
*
* Javascript 层的 TRTCPluginInfo.setParameter() 接口调用会触发该函数。
*
* @param param 插件参数,格式为 json 字符串。
* @return true: 设置成功,false: 设置失败。
*/
virtual bool setParameter(const char* param) = 0;
/**
* 获取插件类型
*
* 具体的插件子类中需要重写。
*/
virtual TRTC_PLUGIN_TYPE getPluginType() {
return TRTC_PLUGIN_TYPE::TRTC_PLUGIN_UNKNOWN;
}
/**
* 设置错误回调函数
*
* @param callback 回调函数。是对 Javascript 层 setPluginCallback() 接口设置回调函数的封装。
*/
void setErrorCallback(PluginErrorCallbackFunc callback, void* param) {
error_callback_ = callback;
param_ = param;
}
/**
* 错误码上报函数
*
* 该函数调用后,会触发 Javascript 层 setPluginCallback() 接口设置的回调函数,将错误码和错误信息透传给 Javascript 层,用户可以根据错误码做相应处理。
*
* @param error 错误码。
* @param msg 错误描述。
*/
void onError(int error, const char* msg) {
if (error_callback_) {
error_callback_(error, msg, param_);
}
}
private:
PluginErrorCallbackFunc error_callback_ = nullptr;
void* param_ = nullptr;
};
// 创建插件实例对象,Javascript 层 addPlugin() 接口最终会调用到该函数
extern "C" EXPORTS ITRTCPlugin* createTRTCPlugin();
// 销毁插件实例对象,Javascript 层 removePlugin() 接口最终会调用到该函数
extern "C" EXPORTS void destoryTRTCPlugin(ITRTCPlugin*);
IAudioFramePlugin.h
struct TRTCAudioFrame {
int audioFormat; /// 【字段含义】音频帧的格式。
char* data; /// 【字段含义】音频数据。
uint32_t length; /// 【字段含义】音频数据的长度。
uint32_t sampleRate; /// 【字段含义】采样率。
uint32_t channel; /// 【字段含义】声道数。
uint64_t timestamp; /// 【字段含义】时间戳,单位ms。
char* extraData; /// 【字段含义】音频额外数据,远端用户通过 `onLocalProcessedAudioFrame` 写入的数据会通过该字段回调。
uint32_t extraDataLength; /// 【字段含义】音频消息数据的长度。
};
class IAudioFramePlugin : public ITRTCPlugin {
public:
~IAudioFramePlugin() override {}
/**
* 本地采集并经过音频模块前处理后的音频数据回调
*
* 当您设置完音频数据自定义回调之后,SDK 内部会把刚采集到并经过前处理(ANS、AEC、AGC)之后的数据,以 PCM 格式的形式通过本接口回调给您。
* - 此接口回调出的音频时间帧长固定为0.02s,格式为 PCM 格式。
* - 由时间帧长转化为字节帧长的公式为 `采样率 × 时间帧长 × 声道数 × 采样点位宽`。
* - 以 TRTC 默认的音频录制格式48000采样率、单声道、16采样点位宽为例,字节帧长为 `48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节`。
* @param frame PCM 格式的音频数据帧。
* @note
* 1. 请不要在此回调函数中做任何耗时操作,由于 SDK 每隔 20ms 就要处理一帧音频数据,如果您的处理时间超过 20ms,就会导致声音异常。
* 2. 此接口回调出的音频数据是可读写的,也就是说您可以在回调函数中同步修改音频数据,但请保证处理耗时。
* 3. 此接口回调出的音频数据已经经过了前处理(ANS、AEC、AGC),\\但**不包含**背景音、音效、混响等前处理效果,延迟较低。
*/
virtual void onCapturedAudioFrame(TRTCAudioFrame* frame) = 0;
/**
* 本地采集并经过音频模块前处理、音效处理和混 BGM 后的音频数据回调
*
* 当您设置完音频数据自定义回调之后,SDK 内部会把刚采集到并经过前处理、音效处理和混 BGM 之后的数据,在最终进行网络编码之前,以 PCM 格式的形式通过本接口回调给您。
* - 此接口回调出的音频时间帧长固定为0.02s,格式为 PCM 格式。
* - 由时间帧长转化为字节帧长的公式为`采样率 × 时间帧长 × 声道数 × 采样点位宽`。
* - 以 TRTC 默认的音频录制格式48000采样率、单声道、16采样点位宽为例,字节帧长为`48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节`。
* 特殊说明:
* 您可以通过设置接口中的 `TRTCAudioFrame.extraData` 字段,达到传输信令的目的。由于音频帧头部的数据块不能太大,建议您写入 `extraData` 时,尽量将信令控制在几个字节的大小,如果超过 100 个字节,写入的数据不会被发送。
* 房间内其他用户可以通过 ITRTCAudioFrameCallback
中的 `onRemoteUserAudioFrame` 中的 `TRTCAudioFrame.extraData` 字段回调接收数据。
* @param frame PCM 格式的音频数据帧。
* @note
* 1. 请不要在此回调函数中做任何耗时操作,由于 SDK 每隔 20ms 就要处理一帧音频数据,如果您的处理时间超过 20ms,就会导致声音异常。
* 2. 此接口回调出的音频数据是可读写的,也就是说您可以在回调函数中同步修改音频数据,但请保证处理耗时。
* 3. 此接口回调出的数据已经经过了前处理(ANS、AEC、AGC)、音效和混 BGM 处理,声音的延迟相比于 onCapturedAudioFrame
要高一些。
*/
virtual void onLocalProcessedAudioFrame(TRTCAudioFrame* frame) = 0;
/**
* 混音前的每一路远程用户的音频数据
*
* 当您设置完音频数据自定义回调之后,SDK 内部会把远端的每一路原始数据,在最终混音之前,以 PCM 格式的形式通过本接口回调给您。
* - 此接口回调出的音频时间帧长固定为0.02s,格式为 PCM 格式。
* - 由时间帧长转化为字节帧长的公式为`采样率 × 时间帧长 × 声道数 × 采样点位宽`。
* - 以 TRTC 默认的音频录制格式48000采样率、单声道、16采样点位宽为例,字节帧长为`48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节`。
* @param frame PCM 格式的音频数据帧。
* @param userId 用户标识。
* @note 此接口回调出的音频数据是只读的,不支持修改。
*/
virtual void onPlayAudioFrame(TRTCAudioFrame* frame, const char* userId) = 0;
/**
* 将各路待播放音频混合之后并在最终提交系统播放之前的数据回调
*
* 当您设置完音频数据自定义回调之后,SDK 内部会把各路待播放的音频混合之后的音频数据,在提交系统播放之前,以 PCM 格式的形式通过本接口回调给您。
* - 此接口回调出的音频时间帧长固定为0.02s,格式为 PCM 格式。
* - 由时间帧长转化为字节帧长的公式为 `采样率 × 时间帧长 × 声道数 × 采样点位宽`。
* - 以 TRTC 默认的音频录制格式48000采样率、单声道、16采样点位宽为例,字节帧长为 `48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节`。
* @param frame PCM 格式的音频数据帧。
* @note
* 1. 请不要在此回调函数中做任何耗时操作,由于 SDK 每隔 20ms 就要处理一帧音频数据,如果您的处理时间超过 20ms,就会导致声音异常。
* 2. 此接口回调出的音频数据是可读写的,也就是说您可以在回调函数中同步修改音频数据,但请保证处理耗时。
* 3. 此接口回调出的是对各路待播放音频数据的混合,但其中并不包含耳返的音频数据。
*/
virtual void onMixedPlayAudioFrame(TRTCAudioFrame* frame) = 0;
/**
* SDK 所有音频混合后的音频数据(包括采集到的和待播放的)
*
* 当您设置完音频数据自定义回调之后,SDK 内部会把所有采集到的和待播放的音频数据混合起来,以 PCM 格式的形式通过本接口回调给您,便于您进行自定义录制。
* - 此接口回调出的音频时间帧长固定为0.02s,格式为 PCM 格式。
* - 由时间帧长转化为字节帧长的公式为 `采样率 × 时间帧长 × 声道数 × 采样点位宽`。
* - 以 TRTC 默认的音频录制格式48000采样率、单声道、16采样点位宽为例,字节帧长为 `48000 × 0.02s × 1 × 16bit = 15360bit = 1920字节`。
* @param frame PCM 格式的音频数据帧。
* @note
* 1. 此接口回调出的是SDK所有音频数据的混合数据,包括:经过 3A 前处理、特效叠加以及背景音乐混音后的本地音频,所有远端音频,但不包括耳返音频。
* 2. 此接口回调出的音频数据不支持修改。
*/
virtual void onMixedAllAudioFrame(TRTCAudioFrame* frame) = 0;
};
使用音频自定义处理插件(Javascript)
通过 SDK 提供的 addPlugin()
接口创建一个音频自定义处理插件,返回一个 TRTCPluginInfo
对象,调用该对象的 enable()
接口开启音频自定义处理。
import TRTCCloud, { TRTCPluginType, TRTCPluginInfo } from 'trtc-electron-sdk';
const trtcCloud = TRTCCloud.getTRTCShareInstance();
// 开启音频自定义处理回调
trtcCloud.setPluginParams(TRTCPluginType.TRTCPluginTypeAudioProcess, {
enable: true // 设置为 false 后,会停止发送音视频数据到音频自定义处理插件
});
// 注册插件回调监听
trtcCloud.setPluginCallback((pluginId, errorCode, msg) => {
console.log(`plugin callback: ${pluginId}, errorCode: ${errorCode}, msg: ${msg}`);
});
// 创建插件
const plugin: TRTCPluginInfo = trtcCloud.addPlugin({
id: 'custom-audio-frame-process', // 用户必须保证 ID 的唯一性
path: '', // 构建好的插件文件路径,Windows 下是 ‘.dll’ 文件,MacOS 下是 ‘.dylib’ 文件
type: TRTCPluginType.TRTCPluginTypeAudioProcess // 自定义音频处理插件类型
});
// 启动插件
plugin.enable();
// 设置插件参数
plugin.setParameter(JSON.stringify({'key1':'value1', 'key2':123}));
-
TRTCCloud.setPluginParams() 接口
支持通过 enable 参数控制是否开启 SDK 的音频自定义处理功能。
-
TRTCCloud.addPlugin() 接口
添加插件,依次触发 C++
ITRTCPlugin.h
类中的 createTRTCPlugin()、init()、load() 函数。 -
TRTCCloud.removePlugin() 接口
删除插件,依次触发 C++
ITRTCPlugin.h
类中的 unload()、uninit()、destoryTRTCPlugin() 函数。 -
TRTCCloud.setPluginCallback() 接口
设置插件回调函数,监听插件创建、销毁、运行情况。设置的回调函数有两个触发源头:
- addPlugin/removePlugin() 时的处理结果通知;
- 插件内部 C++
ITRTCPlugin.h
类中的 onError() 函数触发的异常通知。
-
TRTCPluginInfo.enable/disable() 接口
开启/关闭当前插件,触发 C++
ITRTCPlugin.h
类中的 enable() 函数。 -
TRTCPluginInfo.setParameter() 接口
给当前插件设置参数,触发 C++
ITRTCPlugin.h
类中的 setParameter() 函数。