You need to enable JavaScript to run this app.
文档中心
实时音视频

实时音视频

复制全文
下载 pdf
集成相关
自定义视频前处理相关
复制全文
下载 pdf
自定义视频前处理相关
如果你希望为你的 PC 端 RTC 应用接入第三方 SDK 进行视频处理,例如美颜,参考 Native 端自定义视频处理。根据所使用的第三方 SDK 的特性,你可能还需要考虑线程设计和视频帧格式对齐的问题。​
本文以接入美颜 SDK 为例,介绍在自定义视频处理时,线程设计和视频帧格式处理的最佳实践。​
线程设计​
RTC SDK 回调 processVideoFrame 的线程来自不同的线程,如果美颜 SDK 不支持在多个线程渲染,可能会导致渲染失败等问题,如下图:​
​图片​​
因此,你需要将美颜相关活动集中到一个线程中。​
​图片​​
  1. 在应用上启动新线程,例如 m_thread_process。​
  1. 调用 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 返回的视频帧格式会进行字节对齐,因此,在内存拷贝时,需要先判断对齐格式,再进行数据拷贝。​
​图片​​
​
//将 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;​
}​
​
​
最近更新时间:2026.09.28 09:46:49
这个页面对您有帮助吗?
有用
有用
无用
无用