音视频传输过程中,我们可以对采集到的音视频数据进行前处理和后处理,获取想要的播放效果。
对于有自行处理音视频数据需求的场景,声网提供原始数据功能,你可以在将数据发送给编码器前进行前处理,对捕捉到的语音信号或视频帧进行修改;也可以在将数据发送给解码器后进行后处理,对接收到的语音信号或视频帧进行修改。
Native SDK 通过提供 IVideoFrameObserver
类,实现采集、修改原始视频数据功能。
我们在 GitHub 上提供一个开源的示例项目,你可以前往下载,或查看其中的源代码。
在使用原始数据功能前,请确保你已在项目中完成基本的实时音视频功能,详见一对一通话或互动直播。
参考如下步骤,在你的项目中实现原始视频数据功能:
registerVideoFrameObserver
方法注册视频观测器。onCaptureVideoFrame
、onPreEncodeVideoFrame
或 onRenderVideoFrame
回调发送获取到的原始视频数据。下图展示使用原始视频数据的 API 调用时序:
下图展示了原始视频数据的流转:
你可以通过 onCaptureVideoFrame
、onPreEncodeVideoFrame
或 onRenderVideoFrame
回调:
VideoFrame
获取原始视频数据VideoFrame
的原始视频数据进行处理并返回到 SDK 或进行自渲染registerVideoFrameObserver
注册视频观测器。// 注册视频观测器
BOOL CAgoraOriginalVideoDlg::RegisterVideoFrameObserver(BOOL bEnable,IVideoFrameObserver * videoFrameObserver)
{
// 创建使用 IMediaEngine 类为 template 的 AutoPtr 实例
agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
// AutoPtr 实例调用 queryInterface 方法,通过 IID 获取 IMediaEngine 实例的指针。
// AutoPtr 实例会通过箭头操作符访问 IMediaEngine 实例的指针并通过 IMediaEngine 实例调用 registerVideoFrameObserver。
mediaEngine.queryInterface(m_rtcEngine, agora::AGORA_IID_MEDIA_ENGINE);
int nRet = 0;
if (mediaEngine.get() == NULL)
return FALSE;
if (bEnable) {
// 注册视频观测器
nRet = mediaEngine->registerVideoFrameObserver(videoFrameObserver);
}
else {
// 取消注册视频观测器
nRet = mediaEngine->registerVideoFrameObserver(NULL);
}
return nRet == 0 ? TRUE : FALSE;
}
通过回调获取原始视频数据或者对原始视频数据进行前处理或后处理。
获取本地摄像头采集的原始视频数据:
// 通过 onCaptureVideoFrame 回调获取本地摄像头采集的原始视频数据,进行灰度处理,再发送回 SDK
bool CGrayVideoProcFrameObserver::onCaptureVideoFrame(VideoFrame & videoFrame)
{
int nSize = videoFrame.height * videoFrame.width;
memset(videoFrame.uBuffer, 128, nSize / 4);
memset(videoFrame.vBuffer, 128, nSize / 4);
return true;
}
// 通过 onCaptureVideoFrame 回调获取本地摄像头采集的原始视频数据,进行模糊处理,再发送回 SDK
bool CAverageFilterVideoProcFrameObserver::onCaptureVideoFrame(VideoFrame & videoFrame)
{
static int step = 1;
static bool flag = true;
if (flag)
{
step += 2;
}
else {
step -= 2;
}
if (step >= 151)
{
flag = false;
step -= 4;
}
else if (step <= 0) {
flag = true;
step += 4;
}
AverageFiltering((unsigned char *)videoFrame.yBuffer, videoFrame.width, videoFrame.height, step);
AverageFiltering((unsigned char *)videoFrame.uBuffer, videoFrame.width / 2, videoFrame.height / 2, step);
AverageFiltering((unsigned char *)videoFrame.vBuffer, videoFrame.width / 2, videoFrame.height / 2, step);
return true;
}
获取远端发送的原始视频数据:
// 通过 onRenderVideoFrame 回调获取远端发送的原始视频数据
bool CAverageFilterVideoProcFrameObserver::onRenderVideoFrame(unsigned int uid, VideoFrame & videoFrame)
{
return true;
}
使用 3.0.1 或之后版本的 SDK 时,请注意如下:
IVideoFrameObserver
中的回调函数在同一个线程中报告。SDK 只会保证这些回调的顺序性。IVideoFrameObserver
中的回调函数中主动切换 OpenGL 的上下文,否则美颜可能失效。如果你还想在项目中实现原始音频数据功能,请参考原始音频数据。