功能描述
音视频 SDK 提供了获取 原始视频数据 功能来实现对视频的 编码前处理 和 解码后处理,即在音视频处理过程中,对采集和接收到的视频、频帧进行修改,并发布到房间中供远端用户订阅,实现特殊的播放效果。
原始视频数据主要分为:本地摄像头采集的原始数据、本地预览数据(即经过裁剪或加水印处理后未编码的数据)、远端用户的原始视频数据。
- 编码前处理:在编码前对SDK提供的原始视频数据(例如,本地摄像头采集的视频数据、本地预览数据)自行逐帧处理。
- 解码后处理:在解码后对SDK提供的原始视频数据(例如,接收到的远端用户视频数据)自行逐帧处理。
Native SDK 通过提供 IVideoFrameObserver
类,实现采集、修改原始视频数据功能。
实现方法
操作步骤
在使用原始数据功能前,请确保你已在项目中完成基本的实时音视频功能。
参考如下步骤,在你的项目中实现原始视频数据功能:
- 加入频道前调用
registerVideoFrameObserver
方法注册视频观测器,并在该方法中实现一个IVideoFrameObserver
类。 - 成功注册后,SDK 会在捕捉到每个视频帧时通过
onCaptureVideoFrame
、onPreEncodeVideoFrame
或onRenderVideoFrame
回调发送获取到的原始视频数据。 - 用户拿到视频数据后,根据场景需要自行进行处理。然后将处理过的视频数据再通过上述回调发送给 SDK。
API 调用时序
下图展示使用原始视频数据的 API 调用时序:
示例代码
你可以对照 API 时序图,参考下面的示例代码片段,在项目中实现原始视频数据功能:
#include <jni.h>
#include <android/log.h>
#include <cstring>
#include "IArRtcEngine.h"
#include "IArMediaEngine.h"
#include "video_preprocessing_plugin_jni.h"
class ARVideoFrameObserver : public ar::media::IVideoFrameObserver
{
public:
// 获取本地摄像头采集到的视频帧
virtual bool onCaptureVideoFrame(VideoFrame& videoFrame) override
{
int width = videoFrame.width;
int height = videoFrame.height;
memset(videoFrame.uBuffer, 128, videoFrame.uStride * height / 2);
memset(videoFrame.vBuffer, 128, videoFrame.vStride * height / 2);
return true;
}
// 获取远端用户发送的视频帧
virtual bool onRenderVideoFrame(unsigned int uid, VideoFrame& videoFrame) override
{
return true;
}
// 获取本地视频编码前的视频帧
virtual bool onPreEncodeVideoFrame(VideoFrame& videoFrame) override
{
return true;
}
};
class IVideoFrameObserver
{
public:
enum VIDEO_FRAME_TYPE {
FRAME_TYPE_YUV420 = 0, // 视频帧格式为 YUV 420
};
struct VideoFrame {
VIDEO_FRAME_TYPE type;
int width; // 视频帧的宽
int height; // 视频帧的高
int yStride; // YUV 数据中的 Y 缓冲区的行跨度
int uStride; // YUV 数据中的 U 缓冲区的行跨度
int vStride; // YUV 数据中的 V 缓冲区的行跨度
void* yBuffer; // YUV 数据中的 Y 缓冲区的指针
void* uBuffer; // YUV 数据中 U 缓冲区的指针
void* vBuffer; // YUV 数据中 V 缓冲区的指针
int rotation; // 该帧的旋转信息,可设为 0, 90, 180, 270
int64_t renderTimeMs; // 该帧的时间戳
};
public:
virtual bool onCaptureVideoFrame(VideoFrame& videoFrame) = 0;
virtual bool onRenderVideoFrame(unsigned int uid, VideoFrame& videoFrame) = 0;
virtual bool onPreEncodeVideoFrame(VideoFrame& videoFrame) { return true; }
};
同时,我们在 GitHub 提供一个开源的 Advanced-Video 示例项目。你可以前往下载,或参考 IARMediaEngine.h 文件中的代码。
开发注意事项
本文中使用的原始数据接口为 C++ 接口。如果你在 Android 平台开发,请参考如下步骤,使用 SDK 库的 JNI 和插件管理器注册视频数据观测器。
static ARVideoFrameObserver s_videoFrameObserver;
static ar::rtc::IRtcEngine* rtcEngine = NULL;
#ifdef __cplusplus
extern "C" {
#endif
int __attribute__((visibility("default"))) loadARRtcEnginePlugin(ar::rtc::IRtcEngine* engine)
{
rtcEngine = engine;
return 0;
}
void __attribute__((visibility("default"))) unloadARRtcEnginePlugin(AR::rtc::IRtcEngine* engine)
{
rtcEngine = NULL;
}
JNIEXPORT void JNICALL Java_org_ar_propeller_preprocessing_VideoPreProcessing_enablePreProcessing
(JNIEnv *env, jobject obj, jboolean enable)
{
if (!rtcEngine)
return;
ar::util::AutoPtr<ar::media::IMediaEngine> mediaEngine;
mediaEngine.queryInterface(rtcEngine, ar::AR_IID_MEDIA_ENGINE);
if (mediaEngine) {
if (enable) {
mediaEngine->registerVideoFrameObserver(&s_videoFrameObserver);
} else {
mediaEngine->registerVideoFrameObserver(NULL);
}
}
}
#ifdef __cplusplus
}
#endif
相关文档
如果你还想在项目中实现原始音频数据功能,请参考原始音频数据。