功能介绍

实时音视频传输过程中,Agora SDK 通常会启动默认的音视频模块进行采集和渲染。在以下场景中,你可能会发现默认的音视频模块无法满足开发需求:

  • app 中已有自己的音频或视频模块
  • 希望使用非 Camera 采集的视频源,如录屏数据
  • 需要使用自定义的美颜库有或前处理库
  • 某些视频采集设备被系统独占。为避免与其它业务产生冲突,需要灵活的设备管理策略

基于此,Agora Native SDK 支持使用自定义的音视频源或渲染器,实现相关场景。本文介绍如何实现自定义视频采集和渲染。

实现方法

开始自定义采集和渲染前,请确保你已在项目中实现基本的通话或者直播功能,详见一对一通话互动直播

自定义视频采集

Agora Native SDK 目前提供 Push 方式和 MediaIO 两种方式实现自定义的视频源。其中:

  • Push 方式下,SDK 默认不会对采集传入的音频数据做消噪等处理。如有音频消噪需求,需要开发者自行实现。
  • MediaIO 方式下,自定义视频源可以搭配自定义渲染器使用,实现更为丰富的场景。

Push 方式

参考如下步骤,在你的项目中使用 Push 方式实现自定义视频源功能:

  1. joinChannel 前通过调用 setExternalVideoSource 指定外部视频采集设备。
  2. 指定外部采集设备后,开发者自行管理视频数据采集和处理。
  3. 完成视频数据处理后,再通过 pushExternalVideoFrame 发送给 SDK 进行后续操作。

API 时序图

参考下图时序在你的项目中自定义视频采集。

示例代码

参考下文代码在你的项目中自定义视频采集。

// 首先通知 SDK 现在开始使用外部视频源
rtcEngine.setExternalVideoSource(
    true,      // 是否使用外部视频源
    false,       // 是否使用 Texture作为输出
    true         // true 为使用推送模式,false 为拉取模式,但目前不支持
);

// 在获得视频数据的时候调用 Push 方法将数据传送出去
rtcEngine.pushExternalVideoFrame(new AgoraVideoFrame(
    // 在构造方法传入帧数据的参数,比如格式,宽高等
    // 具体的请查看 AgoraVideoFrame 类的说明
));

同时,我们在 GitHub 提供一个开源的 Agora-Video-Source-Android 示例项目。你可以前往下载,或参考 VideoChatViewActivity.java 文件中的源代码。

API 参考

MediaIO 方式

Agora 通过 MediaIO 提供 IVideoSource 接口和 IVideoFrameConsumer 类,你可以通过该类设置采集的视频数据格式,并控制外部视频的采集过程。

参考如下步骤,在你的项目中使用 MediaIO 方式实现自定义视频源功能:

  1. 实现 IVideoSource 类。Agora 通过 IVideoSource 类下的各回调设置视频数据格式,并控制采集过程:
    • 收到 getBufferType 回调后,在该回调的返回值中指定想要采集的视频数据格式;
    • 收到 onInitialize 回调后,保存该回调中的 IVideoFrameConsumer 对象。Agora 通过 IVideoFrameConsumer 对象发送和接收自定义的视频数据;
    • 收到 onStart 回调后,通过 IVideoFrameConsumer 对象向 SDK 发送视频帧;
    • 收到 onStop 回调后,停止使用 IVideoFrameConsumer 对象向 SDK 发送视频帧;
    • 收到 onDispose 回调后,释放 IVideoFrameConsumer 对象。
  2. 继承实现的 IVideoSource 类,构建一个自定义的视频源对象。
  3. 调用 setVideoSource 方法,将自定义的视频源对象设置给 RtcEngine。
  4. 根据场景需要,调用 startPreviewjoinChannel 等方法预览或发送自定义采集的视频数据。

API 时序图

参考下图时序使用 MediaIO 在你的项目中实现自定义视频采集。

示例代码

参考下文代码使用 MediaIO 在你的项目中实现自定义视频采集。

IVideoFrameConsumer mConsumer;
boolean mHasStarted;

// 先创建一个实现 VideoSource 接口的实例
VideoSource source = new VideoSource() {
    @Override
    public int getBufferType() {
        // 返回当前帧数据的 Buffer 类型,每种数据类型在 SDK 内部会经过不同的处理,所以必须与帧数据的类型保持一致
        // 若切换 VideoSource 的类型,必须重新创建另一个实例
        // 有三种类型:BYTE_BUFFER(1);BYTE_ARRAY(2);TEXTURE(3)
        return BufferType.BYTE_ARRAY;
    }

    @Override
     public boolean onInitialize(IVideoFrameConsumer consumer) {
        // Consumer 是由 SDK 创建的,在 VideoSource 生命周期中注意保存它的引用
        mConsumer = consumer;
    }

    @Override
     public boolean onStart() {
        mHasStarted = true;
    }

    @Override
      public void onStop() {
        mHasStarted = false;
    }

    @Override
     public void onDispose() {
        // 释放对 Consumer 的引用
        mConsumer = null;
    }
};

// 将输出流切换到刚创建的 VideoSource 实例
rtcEngine.setVideoSource(source);

// 在得到视频帧数据之后,可以调用 Consumer 类的方法传送数据
// 必须根据帧数据的类型来选择用不同的方法
// 假设从视频源中得到的视频为 Data, 从 Android 相机中获取的帧类型可能是 NV21 和 TEXTURE_OES,假设当前类型为 byte array,即 NV21
if (mHasStarted && mConsumer != null) {
    mConsumer.consumeByteArrayFrame(data, AgoraVideoFrame.NV21, width, height, rotation, timestamp);
}

同时,我们在 GitHub 提供一个开源的 Custom-Media-Device-Android 示例项目。你可以前往下载,或参考 ViewSharingCapturer.java 文件中的源代码。

API 参考

相关文档

你也可以选择自己管理视频设备的生命周期,只是根据 Media Engine 的回调来打开和关闭视频帧的输入开关。对于开发者 App 之前已有自己的采集模块,需要集成 Agora SDK 以获得实时通信能力的使用场景下,这种方式更简单。详见 使用 Agora SDK 提供的组件自定义视频源 中的描述。

自定义视频渲染器

通过 MediaIO 采集到的视频数据,还可以搭配 Agora 的 IVideoSink 接口使用,实现自定义渲染功能。

参考如下步骤,在你的项目中使用 MediaIO 方式实现自定义渲染器功能:

  1. 实现 IVideoSink 类。Agora 通过 IVideoSink 类下的各回调设置视频数据格式,并控制渲染过程:
    • 收到 getBufferTypegetPixelFormat 回调后,在对应回调的返回值中设置你想要渲染的数据类型;
    • 根据收到的 onInitializeonStartonStoponDisposegetEglContextHandle 回调,控制视频数据的渲染过程;
    • 实现一个对应渲染数据类型的 IVideoFrameConsumer 对象,以获取视频数据。
  2. 继承实现的 IVideoSink 类,构建一个自定义的渲染器。
  3. 调用 setLocalVideoRenderersetRemoteVideoRenderer,用于本地渲染或远端渲染。
  4. 根据场景需要,调用 startPreviewjoinChannel 等方法预览或发送自定义渲染的视频数据。

API 调用时序

参考下图时序使用 MediaIO 在你的项目中实现自定义视频渲染。

示例代码

参考下文代码使用 MediaIO 在你的项目中实现自定义视频渲染。

IVideoSink sink = new IVideoSink() {
    @Override
    public boolean onInitialize () {
        return true;
    }

    @Override
    public boolean onStart() {
        return true;
    }

    @Override
    public void onStop() {

    }

    @Override
    public void onDispose() {

    }

    @Override
    public long getEGLContextHandle() {
        // 构造你的 Egl context
        // 返回 0 代表渲染器中并没有创建 Egl context
        return 0;
    }

    // 返回当前渲染器需要的数据 Buffer 类型
    // 若切换 VideoSink 的类型,必须重新创建另一个实例
    // 有三种类型:BYTE_BUFFER(1);BYTE_ARRAY(2);TEXTURE(3)
    @Override
    public int getBufferType() {
        return BufferType.BYTE_ARRAY;
    }

    // 返回当前渲染器需要的 Pixel 格式
    @Override
    public int getPixelFormat() {
        return PixelFormat.NV21;
    }

   // SDK 调用该方法将获取到的视频帧传给渲染器
   // 根据获取到的视频帧的格式,选择相应的回调
   @Override
   public void consumeByteArrayFrame(byte[] data, int format, int width, int height, int rotation, long timestamp) {

   // 渲染器在此渲染
   }
   public void consumeByteBufferFrame(ByteBuffer buffer, int format, int width, int height, int rotation, long timestamp) {


   // 渲染器在此渲染
   }
   public void consumeTextureFrame(int textureId, int format, int width, int height, int rotation, long timestamp, float[] matrix) {

   // 渲染器在此渲染
   }

}

rtcEngine.setLocalVideoRenderer(sink);

同时,我们在 GitHub 提供一个开源的 Custom-Media-Device-Android 示例项目。你可以前往下载,或参考 PrivateTextureHelper.java 文件中的源代码。

API 参考

相关文档

为了方便开发者集成和创建自定义的视频渲染器,Agora 也提供了一些辅助类和示例代码;开发者也可以直接使用这些组件,或者利用这些组件构建自定义的渲染器,详见下文的 使用 Agora SDK 提供的组件自定义渲染器

开发注意事项

自定义视频采集和渲染场景中,需要开发者具有采集或渲染视频的能力:

  • 自定义视频采集场景中,你需要自行管理视频数据的采集和处理。
  • 自定义视频渲染场景中,你需要自定管理视频数据的处理和显示。

相关文档

如果你还想在项目中实现自定义的音频采集和渲染功能,请参考文档自定义音频采集和渲染