使用 Electron RTC SDK 时,在视频渲染和编码传输前,你可以自定义逻辑,对视频帧进行处理。
注意:
RTC SDK 和 CV SDK 进行了深度整合,联合提供强大且较下述方案更易集成的 RTC-CV 联合集成方案,强烈建议你使用联合方案。
macOS、Windows 下的 Electron 开发框架
此功能在视频处理链路的位置如下:
你需要自行构建视频处理器插件,并编译成动态库文件。在 Electron 应用项目中,调用 Electron RTC SDK 插件相关接口,指定和使用自定义插件,完成视频自定义处理。
你可以借助 插件示例项目.zip 构建自定义的视频处理插件。
插件代码结构说明如下:
.
├──3rd
│ ├── Common // 公共的资源
│ │ └── assets
│ │ ├── *.bundle. // 放模型资源
│ ├── Mac // Mac 平台的第三方美颜库
│ │ └── Beauty SDK
│ │ ├── include
│ │ ├── lib
│ ├── Windows // Windows 平台的第三方美颜库
│ │ └── Beauty SDK
│ │ ├── include
│ │ ├── lib
├──scripts
│ ├── release_win_win32.bat // 编译插件脚本
├──src
│ ├── common // 公共的代码,比如日志,utils 等
│ │ └── log.h
│ │ └── log.cpp
│ │ └── rapidjson // rapidjson 用于解析 JSON
│ ├── windows // Windows 平台特有的代码
│ │ └── dllmain.cpp // Windows 动态库入口
│ ├── IVideoPlugin.h // 定义插件,详见下文示例
│ ├── veRTCFUPlugin.h // 定义处理器方法,详见下文示例
│ ├── veRTCFUPlugin.cpp //实现处理器方法,详见下文示例
├──tools // 打包工具
│ ├── 7z.exe
├──CMakeLists.txt // cmake 工程文件
IVideoPlugin.h,实现 IVideoPlugin 纯虚方法,进行视频帧前处理。/*
* @brief 视频插件接口类
*/
#pragma once
#include <stdint.h>
#if defined(_WIN32)
#define DLL_EXPORTS __declspec(dllexport)
#else
#define DLL_EXPORTS
#endif
namespace bytertc {
/**
* @type keytype
* @brief 自定义视频插件帧
*/
struct VideoPluginFrame {
int width; // 视频帧宽,单位:像素
int height; // 视频帧高,单位:像素
int yStride; // Y 数据行的长度
int uStride; // U 数据行的长度
int vStride; // V 数据行的长度
uint8_t* yBuffer; // Y 数据缓冲区
uint8_t* uBuffer; // U 数据缓冲区
uint8_t* vBuffer; // V 数据缓冲区
int rotation; // 视频帧旋转角度 {0, 90, 180, 270}
int64_t timestampUs; // 频帧时间戳,单位:微秒
};
/**
* @type api
* @brief 视频插件 API
*/
class IVideoPlugin {
public:
virtual bool init(const char* path) = 0;
virtual void uninit() = 0;
virtual bool setParameter(const char* param) = 0;
virtual const char* getParameter(const char* key) = 0;
virtual void release() = 0;
public:
virtual bool processVideoFrame(VideoPluginFrame* videoFrame) = 0;
};
}
/**
* @type type
* @brief 定义函数类型
*/
typedef bytertc::IVideoPlugin* (*createByteVideoPlugin)();
/**
* @type api
* @brief 创建插件
*/
extern "C" DLL_EXPORTS bytertc::IVideoPlugin* createVideoPlugin();
cpp
将 processVideoFrame 放在一个单独的线程中。如果 OpenGL 环境不能切换线程,建议在回调里进行初始化。
#pragma once
#include "IVideoPlugin.h"
class VeRTCFUPlugin : public bytertc::IVideoPlugin {
public:
VeRTCFUPlugin() {}
~VeRTCFUPlugin() {}
public:
bool init(const char* path) override;
void uninit() override;
bool setParameter(const char* param) override;
const char* getParameter(const char* key) override {} // unused
void release() override {} // unused
public:
bool processVideoFrame(bytertc::VideoPluginFrame* videoFrame) override;
private:
bool initEnv();
bool initOpenGL();
void makeCurrent();
void doneCurrent();
private:
static bool m_envInited;
bool m_inited = false;
bool m_loadBundles = false;
bool m_updateBundles = false;
};
cpp#include "veRTCFUPlugin.h"
bytertc::IVideoPlugin* createVideoPlugin()
{
return new VeRTCFUPlugin();
}
bool VeRTCFUPlugin::init(const char* path)
{
// Add codes to initialize the plugin.
return true;
}
void VeRTCFUPlugin::uninit()
{
// Add codes to release memory.
}
bool VeRTCFUPlugin::setParameter(const char* param)
{
// Add code to parse json string.
return true;
}
bool VeRTCFUPlugin::processVideoFrame(bytertc::VideoPluginFrame* videoFrame)
{
// Add code to process video frames
// Initialize OpenGL. Load model, and etc.
return true;
}
cpp
调用 Electron SDK 接口,注册和启动插件。
const rtcVideo= new veRTCVideo();
rtcVideo.startVideoCapture();
typescriptlet ret = rtcVideo.initializePluginManager();
typescript// 指定插件目录。即上一步骤中编译好的动态库文件存放路径。
let libPath = path.resolve(__static, 'vertc-mac-x86-64-fu-plugin/libveRTCFUPlugin.dylib');
let result = rtcVideo.registerPlugin({
id: 'fu-mac',
path: libPath
});
typescriptconst plugin = rtcVideo.getPlugin('fu-mac');
if (!plugin) {
console.warn('plugin is null.')
return;
}
typescriptlet ret = plugin.setEnabled(true);
typescript本文以第三方美颜插件为例,说明如何将数据传入插件,进行自定义视频处理。JSON 字符串的 key 和 value 需要与所使用的插件进行协定,本节步骤和代码均为示例。
let fuAuth = 'xxx';
plugin.setParameter(JSON.stringify({
"plugin.fu.authdata": JSON.parse(fuAuth)
}));
TypeScriptplugin.setParameter(JSON.stringify({"plugin.fu.bundles.load": [{
bundleName: "face_beautification.bundle",
bundleOptions: {
"filter_name": "origin",
"filter_level": 1.0,
...
}
},{
bundleName: "bg_segment.bundle",
bundleOptions: {
"picture_path": "",
}
}]}));
TypeScriptplugin.setParameter(JSON.stringify({"plugin.fu.bundles.update": {
bundleName: "face_beautification.bundle",
bundleOptions: {
// 滤镜
"filter_name": "lengsediao1",
"filter_level": 1.0,
// 美白
"color_level": 0.3, // 0.0-1.0
// 磨皮
"blur_level": 4.2, // 0.0-6.0
// 大眼
"eye_enlarging": 0.5, // 0.0-1.0
...
}
}}));
TypeScriptlet vbgPath = path.resolve(__static, 'vertc-mac-x86-64-fu-plugin/bg.png');
plugin.setParameter(JSON.stringify({"plugin.fu.bundles.update": {
bundleName: "bg_segment.bundle",
bundleOptions: {
"picture_path": vbgPath,
}
}}));
TypeScript调用 Electron SDK 接口,关闭插件,释放资源。
let ret = plugin.setEnabled(false);
typescriptlet result = rtcVideo.unregisterPlugin('fu-mac');
typescriptlet ret = rtcVideo.releasePluginManager();
typescript