音视频传输过程中,我们可以对采集到的音视频数据进行前处理和后处理,获取想要的播放效果。
对于有自行处理音视频数据需求的场景,声网提供原始数据功能,你可以在将数据发送给编码器前进行前处理,对捕捉到的音频信号或视频帧进行修改;也可以在将数据发送给解码器后进行后处理,对接收到的音频信号或视频帧进行修改。
声网在 GitHub 上提供以下实现了原始视频数据功能的开源示例项目:
你可以下载体验并参考源代码。
SDK 通过提供 IVideoFrameObserver
类,实现采集及修改原始视频数据功能,主要实现步骤如下:
registerVideoFrameObserver
方法注册视频观测器,并在该方法中实现一个 IVideoFrameObserver
类。onCaptureVideoFrame
、onPreEncodeVideoFrame
或 onRenderVideoFrame
回调发送获取到的原始视频数据。.mm
文件中实现。.mm
文件的开头需要引入 C++ 头文件:
#import <AgoraRtcKit/IAgoraMediaEngine.h> #import <AgoraRtcKit/IAgoraRtcEngine.h>
下图展示使用原始视频数据的 API 调用时序:
你可以对照 API 时序图,参考下面的示例代码片段,在项目中实现原始视频数据功能。
初始化 AgoraRtcEngineKit
,并开启视频模块。
// Swift
// 初始化 AgoraRtcEngineKit
let config = AgoraRtcEngineConfig()
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
// 开启视频模块
agoraKit.enableVideo()
注册视频帧观测器。
// Swift
let videoType:ObserverVideoType = ObserverVideoType(rawValue: ObserverVideoType.captureVideo.rawValue | ObserverVideoType.renderVideo.rawValue | ObserverVideoType.preEncodeVideo.rawValue)
agoraMediaDataPlugin?.registerVideoRawDataObserver(videoType)
agoraMediaDataPlugin?.videoDelegate = self;
在 .mm
文件中调用 C++ 的 API 实现注册视频帧观测器的功能。
- (void)registerVideoRawDataObserver:(ObserverVideoType)observerType {
// 获取 Native SDK 的 C++ 句柄
agora::rtc::IRtcEngine* rtc_engine = (agora::rtc::IRtcEngine*)self.agoraKit.getNativeHandle;
// 创建 IMediaEngine 实例
agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
// 必须使用 IMediaEngine 实例调用 queryInterface 设置 agora::AGORA_IID_MEDIA_ENGINE 接口,否则无法使用 mediaEngine 执行 registerVideoFrameObserver
mediaEngine.queryInterface(rtc_engine, agora::AGORA_IID_MEDIA_ENGINE);
// 调用 registerVideoFrameObserver 注册视频帧观测器
mediaEngine->registerVideoFrameObserver(&s_videoFrameObserver);
s_videoFrameObserver.mediaDataPlugin = self;
}
加入频道。
// Swift
let result = agoraKit.joinChannel(byToken: KeyCenter.Token, channelId: channelName, info: nil, uid: 0) {[unowned self] (channel, uid, elapsed) -> Void in
self.isJoined = true
LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info)
}
通过回调获取原始视频数据。你可以根据需求对获取的数据进行处理。
// Swift
// 获取本地摄像头采集的原始视频数据,处理后再发送回 SDK
func mediaDataPlugin(_ mediaDataPlugin: AgoraMediaDataPlugin, didCapturedVideoRawData videoRawData: AgoraVideoRawData) -> AgoraVideoRawData {
return videoRawData
}
// 获取远端用户发送的原始视频数据,处理后再发送回 SDK
func mediaDataPlugin(_ mediaDataPlugin: AgoraMediaDataPlugin, willRenderVideoRawData videoRawData: AgoraVideoRawData, ofUid uid: uint) -> AgoraVideoRawData {
return videoRawData
}
在 .mm
文件中调用 C++ 的 API 实现获取原始视频数据的回调:
// 通过 onCaptureVideoFrame 回调获取本地摄像头采集的原始视频数据
virtual bool onCaptureVideoFrame(VideoFrame& videoFrame) override
{
if (!mediaDataPlugin && ((mediaDataPlugin.observerVideoType >> 0) == 0)) return true;
@autoreleasepool {
AgoraVideoRawData *newData = nil;
if ([mediaDataPlugin.videoDelegate respondsToSelector:@selector(mediaDataPlugin:didCapturedVideoRawData:)]) {
AgoraVideoRawData *data = getVideoRawDataWithVideoFrame(videoFrame);
newData = [mediaDataPlugin.videoDelegate mediaDataPlugin:mediaDataPlugin didCapturedVideoRawData:data];
modifiedVideoFrameWithNewVideoRawData(videoFrame, newData);
}
}
return true;
}
// 通过 onRenderVideoFrame 回调获取远端用户发送的原始视频数据
virtual bool onRenderVideoFrame(unsigned int uid, VideoFrame& videoFrame) override
{
if (!mediaDataPlugin && ((mediaDataPlugin.observerVideoType >> 1) == 0)) return true;
@autoreleasepool {
AgoraVideoRawData *newData = nil;
if ([mediaDataPlugin.videoDelegate respondsToSelector:@selector(mediaDataPlugin:willRenderVideoRawData:ofUid:)]) {
AgoraVideoRawData *data = getVideoRawDataWithVideoFrame(videoFrame);
newData = [mediaDataPlugin.videoDelegate mediaDataPlugin:mediaDataPlugin willRenderVideoRawData:data ofUid:uid];
modifiedVideoFrameWithNewVideoRawData(videoFrame, newData);
}
}
return true;
}
取消注册视频帧观测器。
// Swift
agoraMediaDataPlugin?.deregisterVideoRawDataObserver(ObserverVideoType(rawValue: 0))
在 .mm
文件中调用 C++ 的 API 实现取消注册视频帧观测器。
- (void)deregisterVideoRawDataObserver:(ObserverVideoType)observerType {
// 获取 Native SDK 的 C++ 句柄
agora::rtc::IRtcEngine* rtc_engine = (agora::rtc::IRtcEngine*)self.agoraKit.getNativeHandle;
// 创建 IMediaEngine 实例
agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
// 必须使用 IMediaEngine 实例调用 queryInterface 设置 agora::AGORA_IID_MEDIA_ENGINE 接口,否则无法使用 mediaEngine 执行 registerVideoFrameObserver
mediaEngine.queryInterface(rtc_engine, agora::AGORA_IID_MEDIA_ENGINE);
// 调用 registerVideoFrameObserver 注册视频帧观测器
mediaEngine->registerVideoFrameObserver(NULL);
s_videoFrameObserver.mediaDataPlugin = nil;
}
getNativeHandle
registerVideoFrameObserver
onCaptureVideoFrame
onRenderVideoFrame
onPreEncodeVideoFrame
.mm
文件中执行,完整代码可以参考 AgoraMediaDataPlugin.mm。registerVideoFrameObserver
方法前必须先调用 getNativeHandle
获取 C++ 句柄。IVideoFrameObserver
中的回调函数在同一个线程中报告。SDK 只会保证这些回调的顺序性。IVideoFrameObserver
中的回调函数中主动切换 OpenGL 的上下文,否则美颜可能失效。如果你还想在项目中实现原始音频数据功能,请参考原始音频数据。