You need to enable JavaScript to run this app.
导航

Electron 端自定义视频处理

最近更新时间2023.10.08 15:56:24

首次发布时间2023.09.01 18:15:48

使用 Electron RTC SDK 时,在视频渲染和编码传输前,你可以自定义逻辑,对视频帧进行处理。

功能简介

适用场景

  • 美颜特效,接入自定义美颜插件。
  • 水印,接入自定义水印插件。

注意:
RTC SDK 和 CV SDK 进行了深度整合,联合提供强大且较下述方案更易集成的 RTC-CV 联合集成方案,强烈建议你使用联合方案

适用平台

macOS、Windows 下的 Electron 开发框架

适用范围

此功能在视频处理链路的位置如下:
alt

  • 适用于:内部摄像头采集视频流、外部自定义摄像头采集视频流
  • 不适用于:内部屏幕采集视频流、外部自定义屏幕采集视频流、静态图

集成步骤

你需要自行构建视频处理器插件,并编译成动态库文件。在 Electron 应用项目中,调用 Electron RTC SDK 插件相关接口,指定和使用自定义插件,完成视频自定义处理。

1. 开发视频处理器插件

你可以借助 插件示例项目.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 工程文件

  1. 引用视频插件接口头文件 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();
  1. 构建 IVideoPlugin 处理器。示例代码以美颜插件为例。
    视频帧的处理流程如图。

processor_flow_chart

将 processVideoFrame 放在一个单独的线程中。如果 OpenGL 环境不能切换线程,建议在回调里进行初始化。

  • veRTCFUPlugin.h
#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;
};
  • veRTCFUPlugin.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;
}
  1. 编译视频处理器插件。

2. 注册并启动插件

alt

调用 Electron SDK 接口,注册和启动插件。

  1. 创建引擎,开启视频采集。
const rtcVideo= new veRTCVideo();
rtcVideo.startVideoCapture();
  1. 初始化插件管理器
let ret = rtcVideo.initializePluginManager();
  1. 注册插件
// 指定插件目录。即上一步骤中编译好的动态库文件存放路径。
let libPath = path.resolve(__static, 'vertc-mac-x86-64-fu-plugin/libveRTCFUPlugin.dylib');
let result = rtcVideo.registerPlugin({
        id: 'fu-mac',
        path: libPath
      });
  1. 获取插件实例
const plugin = rtcVideo.getPlugin('fu-mac'); 
if (!plugin) {
    console.warn('plugin is null.')
    return;
}
  1. 启动插件
let ret = plugin.setEnabled(true);

3. 自定义视频处理

本文以第三方美颜插件为例,说明如何将数据传入插件,进行自定义视频处理。
JSON 字符串的 keyvalue 需要与所使用的插件进行协定,本节步骤和代码均为示例。

  1. 授权
let fuAuth = 'xxx';
plugin.setParameter(JSON.stringify({
          "plugin.fu.authdata": JSON.parse(fuAuth)
        }));  
  1. 加载美颜和贴纸模型
plugin.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": "", 
        }
      }]}));
  1. 更新美颜强度
plugin.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
          ...
        }
      }}));
  1. 更新贴纸路径
let 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, 
        }
      }}));

5. 释放插件

调用 Electron SDK 接口,关闭插件,释放资源。

  1. 关闭插件。插件卸载之前仍可以随时开启。
let ret = plugin.setEnabled(false);
  1. 卸载插件。之后可以安装其他插件。
let result = rtcVideo.unregisterPlugin('fu-mac');
  1. 释放插件管理器,之后无法启动所有插件。App 关闭时调用本接口释放。
let ret = rtcVideo.releasePluginManager();