开发视频插件
本文展示如何使用声网 SDK 提供的 API 开发一个视频插件。
开发视频插件需要使用以下接口:
IExtensionVideoFilter
:实现视频数据的处理能力,包括接收、处理和返回处理完的视频帧。IExtensionProvider
:将视频数据的处理能力封装为插件。
前提条件
开发前,请确保你的开发环境满足以下要求:
- Xcode 9.0 或以上版本。
- iOS 9.0 或以上版本的设备。
准备开发环境
参考如下步骤将声网云市场的 API 添加到你的项目中:
- 下载视频 SDK v4.x,然后解压下载的 SDK 包。
- 打开 Xcode,进入 TARGETS > Project Name > General > Frameworks, Libraries, and Embedded Content 菜单,点击 + > Add Other... > Add Files,将安装包中的
/libs/AgoraRtcKit.framework
文件添加至你的项目中。确保 Embed 属性设置为 Do Not Embed。
开发视频插件
实现插件
视频插件通过 IExtensionVideoFilter
接口实现。该接口位于 NGIAgoraMediaNode.h
文件中。IExtensionVideoFilter
接口类包含如下方法:
getProcessMode
start
stop
getVideoFormatWanted
adaptVideoFrame
pendVideoFrame
deliverVideoFrame
setProperty
getProperty
getProcessMode
设置 SDK 与视频插件的交互模式。SDK 在加载插件时,会首先调用该方法向插件发送回调。收到该回调后,你需要在返回值中指定自己需要的交互模式。
virtual void getProcessMode(ProcessMode& mode, bool& independent_thread) = 0;
参数 | 描述 |
---|---|
mode | SDK 和插件互相传递视频数据的模式,支持设为以下值:
|
independent_thread | 是否为插件创建独立的线程:
|
你可以根据 YUV 运算的复杂度和是否使用 OpenGL 设置 mode
和 independent_thread
参数:
- 如果视频插件内部使用较复杂的 YUV 运算处理数据,推荐将
mode
和independent_thread
分别设为Async
和false;
如果视频插件内部使用简单的 YUV 运算处理数据,推荐将mode
和independent_thread
分别设为Sync
和false
。 - 如果视频插件内部使用了 OpenGL 处理数据,推荐将
mode
和independent_thread
分别设为Sync
和true
。
start
virtual int start(agora::agora_refptr<Control> control) = 0;
创建视频插件实例 IExtensionVideoFilter
后,会触发该回调。你可以在该回调中初始化 OpenGL。
SDK 会在该方法中传递一个 Control
对象,方便插件和 SDK 的后续交互。你需要保留收到的 Control
对象,并根据场景需要实现该对象中的相关方法:
stop
virtual int stop() = 0;
销毁视频插件实例 IExtensionVideoFilter
后,会触发该回调。你可以在该回调中释放 OpenGL。
getVideoFormatWanted
设置待处理视频数据的类型和格式。SDK 每向插件提供一帧待处理的视频数据,都会先调用该方法向插件发送回调。收到该回调后,你需要在返回值中指定这一帧的数据类型和格式。你可以给不同的视频帧指定不同的数据类型和格式。
virtual void getVideoFormatWanted(VideoFrameData::Type& type, RawPixelBuffer::Format& format) = 0;
参数 | 描述 |
---|---|
type | 视频数据的类型,目前仅支持设为 RawPixels ,代表原始数据。 |
format | 视频数据的格式,支持设为以下值:
|
adaptVideoFrame
处理视频帧。在同步模式下(mode
设为 Sync
),SDK 通过该方法与视频插件互相传递数据。SDK 通过 in
将待处理的视频数据传递给插件,插件通过 out
将处理后的数据返回给 SDK。
virtual ProcessResult adaptVideoFrame(agora::agora_refptr<IVideoFrame> in, agora::agora_refptr<IVideoFrame>& out) {
return ProcessResult::kBypass;
}
参数
参数名 | 描述 |
---|---|
in | 输入参数。待处理的视频帧。 |
out | 输出参数。处理后的视频帧。 |
返回值
处理该视频帧的结果:
Success
: 成功处理该视频帧。ByPass
: 跳过处理该视频帧,并将它传递到后续链路。Drop
: 丢弃该视频帧。
pendVideoFrame
提交待处理的视频帧。在异步模式下(mode
设为 Async
),SDK 通过该方法向视频插件提交待处理的视频帧。使用该方法后,插件必须通过 Control
类的 deliverVideoFrame
方法返回处理后的视频帧。
virtual ProcessResult pendVideoFrame(agora::agora_refptr<IVideoFrame> frame) {
return ProcessResult::kBypass;
}
参数
参数名 | 描述 |
---|---|
frame | 待处理的视频帧。 |
返回值
处理该视频帧的结果:
Success
: 成功处理该视频帧。ByPass
: 跳过处理该视频帧,并将它传递到后续链路。Drop
: 丢弃该视频帧。
setProperty
设置视频插件属性。App 开发者调用 setExtensionPropertyWithVendor
时,SDK 会调用该方法。你需要返回视频插件的属性。
int ExtensionVideoFilter::setProperty(const char *key, const void *buf, size_t buf_size)
参数 | 描述 |
---|---|
key | 插件属性的 key 。 |
buf | 插件属性 key 值对应的 buffer 地址,数据形式为 JSON 字符串。我们推荐使用第三方的 nlohmann/json library 开源库,帮助实现 C++ 的 struct 和 JSON 字符串之前的序列和反序列化。 |
buf_size | 插件属性 buffer 的内存大小。 |
getProperty
获取视频插件属性。App 开发者调用 getProperty
时,SDK 会调用该方法获取视频插件的属性。
int ExtensionVideoFilter::getProperty(const char *key, void *buf, size_t buf_size)
参数 | 描述 |
---|---|
key | 插件属性的 key 。 |
property | 插件属性指针。 |
buf_size | 插件属性 buffer 的内存大小。 |
示例代码
参考如下示例代码了解如何使用上述方法实现一个视频插件:
封装插件
封装插件通过 IExtensionProvider
接口类实现。该接口位于 NGIAgoraExtensionProvider.h
文件中。你需要先实现这个接口类,并至少实现如下方法:
IExtensionProvider
是插件动态库的提供者,由全局唯一的名字标识。每一个插件动态库可以提供一个或多个有不同功能的插件,如视频前处理插件库可以提供不同功能的视频插件(Video filter)。每个插件是一个具体的 Extension 对象。
enumerateExtensions
提供所有支持封装的插件的信息。SDK 在加载插件时,会调用该方法向插件发送回调。收到该回调后,你需要通过返回值提供所有支持封装的插件的信息。
virtual void enumerateExtensions(ExtensionMetaInfo* extension_list,
int& extension_count) {
(void) extension_list;
extension_count = 0;
}
参数 | 描述 |
---|---|
extension_list | 插件的信息,包括插件类型和插件名称。支持的视频插件类型有:
插件名由插件服务商指定,需要在
|
extension_count | 支持封装的插件的总数量。 |
其中,插件的信息定义如下:
如果你在 enumerateExtensions
方法中返回的插件类型为 VIDEO_PRE_PROCESSING_FILTER
或 VIDEO_POST_PROCESSING_FILTER
,则在 App 开发者初始化 RtcEngine
并创建 IExtensionVideoProvider
对象后,SDK 会调用 createVideoFilter
方法。
createVideoFilter
创建视频插件。SDK 调用该方法后,你需要返回 IExtensionVideoFilter
实例。
virtual agora_refptr<IExtensionVideoFilter> createVideoFilter(const char* name) {
return NULL;
}
成功创建 IExtensionVideoFilter
实例后,视频插件会在合适的时机通过 IExtensionFilter
类对输入的视频数据进行处理。
示例代码
参考如下示例代码了解如何使用上述方法封装视频插件:
打包视频插件
完成插件开发后,你需要对其进行注册、打包,并将最终的 .framework
或 .xcframework
文件,连同一个包含了插件名称、服务商名称和 Filter 名称的文件提交给声网进行验证。
插件通过宏 REGISTER_AGORA_EXTENSION_PROVIDER
进行注册,该宏位于 AgoraExtensionProviderEntry.h
文件中。
你需要在插件的入口使用这个宏。SDK 在加载插件时,该宏会自动向 SDK 注册你的插件。注意填入 PROVIDER_NAME
时不要填写标点符号。示例:
REGISTER_AGORA_EXTENSION_PROVIDER(ByteDance, agora::extension::ExtensionProvider);
示例项目
声网提供一个 iOS 插件开发的示例项目 SimpleFilter 供你参考。