You need to enable JavaScript to run this app.
导航
自定义视频前处理相关
最近更新时间:2024.04.22 17:52:24首次发布时间:2024.02.21 12:43:47

如果你希望为你的 PC 端 RTC 应用接入第三方 SDK 进行视频处理,例如美颜,参考 Native 端自定义视频处理。根据所使用的第三方 SDK 的特性,你可能还需要考虑线程设计和视频帧格式对齐的问题。
本文以接入美颜 SDK 为例,介绍在自定义视频处理时,线程设计和视频帧格式处理的最佳实践。

线程设计

RTC SDK 回调 processVideoFrame 的线程来自不同的线程,如果美颜 SDK 不支持在多个线程渲染,可能会导致渲染失败等问题,如下图:

multithread

因此,你需要将美颜相关活动集中到一个线程中。
compactthread

  1. 在应用上启动新线程,例如 m_thread_process
  2. 调用 funcProcessVideoFrame,初始化美颜模型,处理来自 RTC SDK 回调的视频帧。
    processVideoFrame 函数负责将传入的视频帧存储起来,并通知 funcProcessVideoFrame 线程内通过 dealVideoFrame 进行自定义的视频帧处理操作。
{    //初始化变量
    std::thread* m_thread_process = nullptr;
    std::mutex   m_mutex_process;
    std::atomic<bool>         m_thread_run {true};
    std::condition_variable m_cond1, m_cond2;
    std::atomic<int>    m_process_num;
    bytertc::IVideoFrame* m_currentFrame = nullptr;
}

m_thread_process = new std::thread(funcProcessVideoFrame, this); //启动新线程

//创建引擎
m_video = bytertc::createRTCVideo(g_appid.c_str(), m_handler.get(), nullptr);
m_video->startAudioCapture();
m_video->startVideoCapture();
m_video->registerLocalVideoProcessor(this); //注册回调接口

m_room = m_video->createRTCRoom(roomid);
m_room->joinRoom();

//自定义帧处理
bytertc::IVideoFrame *processVideoFrame(bytertc::IVideoFrame * videoFrame)
{
    if (!videoFrame) return nullptr;

    std::unique_lock<std::mutex> lock(m_mutex_process);
    m_process_num++;
    m_currentFrame = videoFrame;
    m_cond1.notify_one();

    m_cond2.wait(lock, [this]() {return m_thread_run == false || m_process_num == 0; });
    return m_currentFrame;
}

void funcProcessVideoFrame()
{
    while (1)
    {
        if (m_thread_run == false) {
            return;
        }
        std::unique_lock<std::mutex> l(m_mutex_process);
        m_cond1.wait(l, [this]() {return m_thread_run == false || m_process_num > 0; });
        dealVideoFrame();//处理视频帧,美颜
        m_process_num--;
        m_cond2.notify_one();
    }
}

void dealVideoFrame(){ // 处理视频帧的具体实现
    //你可以在此进行自定义美颜处理
    //sdk->deal();
}

视频帧格式处理

RTC SDK 返回的视频帧格式会进行字节对齐,因此,在内存拷贝时,需要先判断对齐格式,再进行数据拷贝。

framestruct

//将 YUV frame 的数据拷贝到 buffer 中:
if (m_currentFrame->getPlaneStride(0) > width
        || m_currentFrame->getPlaneStride(1) > (width+1)/2
        || m_currentFrame->getPlaneStride(2) > (width+1)/2) {

int dst_offset = 0;
int src_offset = 0;
for(int i=0; i<height; i++) {
    memcpy(frameBuffer + dst_offset, m_currentFrame->getPlaneData(0) + src_offset, width);
    src_offset += m_currentFrame->getPlaneStride(0);
    dst_offset += width;
}
src_offset = 0;
for (int i=0; i<height/2; i++) {
      memcpy(frameBuffer + dst_offset, m_currentFrame->getPlaneData(1) + src_offset, width/2);
      src_offset += m_currentFrame->getPlaneStride(1);
      dst_offset += width/2;
}
src_offset = 0;
for (int i=0; i<height/2; i++) {
      memcpy(frameBuffer + dst_offset, m_currentFrame->getPlaneData(2) + src_offset, width/2);
      src_offset += m_currentFrame->getPlaneStride(2);
      dst_offset += width/2;
}