自定义视频采集

更新时间: 2023/05/05 12:20:45

NERTC SDK 提供自定义视频采集功能,帮助您向 SDK 提供自定义的视频输入源数据,并由 SDK 进行编码推流。本文档为您介绍如何通过 setExternalVideoSourcepushExternalVideoFrame 方法实现自定义视频采集功能。

功能描述

一般情况下,App 采用默认设备采集视频数据,通常是本设备的摄像头模块。但在部分场景下可能需要使用自定义的视频源,例如:

  • 由第三方美颜 SDK 负责视频数据采集和视频数据的前处理,NERTC 负责视频数据编码和云端推流。
  • 某些视频采集设备被系统独占。为避免与其它业务产生冲突,需要灵活的设备管理策略。例如,直播过程中需要录制短视频。
  • 需要使用外部视频源,即非摄像头采集的数据,例如播放本地视频文件、屏幕共享、游戏直播等。

基于以上场景,NERTC SDK 支持使用自定义的视频源,以实现您在实际业务场景中的相关需求。

V5.3.0 及之后版本,子房间支持调用 INERtcChannel#setExternalVideoSourceINERtcChannel#pushExternalVideoFrame 设置外部视频输入。

技术原理

NERTC SDK 目前提供 Push 数据源的方式实现自定义的视频源。Push 方式下,SDK 会根据设置的视频 Profile 对数据源进行裁剪或缩放,来达到发送目标分辨率,同时视频的帧率由数据源自身控制。

在 Push 模式下进行视频源自定义时,视频数据的传输过程如下图所示。

自定义视频采集.png

注意事项

  • 自定义视频采集场景中,您需要自行实现视频数据的采集和处理。

  • 若您需要开启外部视频采集,需要在创建外部视频输入源之前调用 enableLocalVideo 方法关闭本地视频设备采集(若未打开则无需关心),再在创建外部视频输入源之后再次调用 enableLocalVideo 方法开启媒体传输通道。

  • 请先开启外部视频输入(setExternalVideoSource),再开启本地视频预览(NERtc#startVideoPreview )或开启媒体传输通道(enableLocalVideo )。

  • 当外部视频源输入作为主流或辅流时,内部引擎为启用状态,在切换房间(switchChannelWithToken)、主动离开房间(leaveChannel)、触发断网重连失败回调(onNERtcChannelDidDisconnectWithReason)或触发重新加入房间回调(onNERtcChannelRejoinChannel)后仍然有效。如果需要关闭该功能,请在下次通话前调用接口关闭该功能。

  • 视频主流和辅流通道分别只能传输一个视频输入源,例如:

    • 若您已经开启了屏幕共享(使用辅流通道),则外部输入的视频源只能使用主流通道。
    • 若您在 enableLocalVideo 中已通过主流传输本设备的摄像头数据,则外部输入的视频源只能使用辅流通道。

实现方法

API 调用时序

sequenceDiagram
    participant 应用层
    participant NERtcSDK
    应用层->>NERtcSDK: init 初始化 SDK
    应用层->>NERtcSDK: enableLocalVideo(false) 关闭本地视频设备采集
    应用层->>NERtcSDK: setLocalVideoConfig 设置视频参数
    rect rgb(191, 223, 255)
    应用层 ->> NERtcSDK: setExternalVideoSource 开启外部视频源数据输入
    end
    Note over 应用层, NERtcSDK: 请自行实现视频数据采集和处理
    应用层->>NERtcSDK: enableLocalVideo(true) 开启媒体传输通道
    应用层->>NERtcSDK: joinChannel 加入房间
    NERtcSDK-->>应用层: onNERtcEngineUserDidJoinWithUserID:userName: 
    rect rgb(191, 223, 255)
    应用层 ->> NERtcSDK: pushExternalVideoFrame 将视频数据发送给 NERtc SDK
    end

配置步骤

NERTC SDK 提供 Push 数据源的方式实现自定义的视频源。Push 方式下,NERTC SDK 会根据设置的视频 Profile 对数据源进行裁剪或缩放,来达到发送目标分辨率,同时视频的帧率由数据源自身控制。

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

  1. 关闭本地视频设备采集。

    调用 enableLocalVideo 方法,设置 enable 参数为 false

  2. 设置基本的视频参数。

    调用 setLocalVideoConfig 方法,设置基本的视频参数,特别是 widthheightframeRate 三个重要参数,要与真实的外部输入一致,否则会影响视频质量。

  3. 开启外部视频源数据输入。

    调用 setExternalVideoSource 方法,设置 enable 参数为 true,并设置 streamTypekNERtcStreamChannelTypeMainStream(主流) 或 kNERtcStreamChannelTypeSubStream(辅流)。

  4. 开启媒体传输通道。

    调用 enableLocalVideo 方法,设置 enable 参数为 true,并设置 streamTypekNERtcStreamChannelTypeMainStream(主流) 或 kNERtcStreamChannelTypeSubStream(辅流)。

  5. 指定外部采集设备后,您需要自行实现视频数据采集和处理,具体实现方法请参考 ExternalVideo 示例项目源码

  6. (可选)设置视频旋转方向。

    当设备方向变化后,您可以通过如下方式实现横竖屏切换:

    在将视频数据发送给 NERTC SDK 前,通过 NERtcVideoFrame 设置视频的方向。例如,设置 rotation 为 90,使视频帧顺时针旋转 90 度。

  7. 将外部视频帧的数据发送给 NERTC SDK。

    完成视频数据处理后,调用 pushExternalVideoFrame 方法将视频数据发送给 NERTC SDK 进行后续操作;调用此方法时,您需要通过 frame 参数传入外部视频帧的数据信息,并设置 streamTypekNERtcStreamChannelTypeMainStream(主流) 或 kNERtcStreamChannelTypeSubStream(辅流)。

    • setExternalVideoSourceenableLocalVideopushExternalVideoFrame 3 个接口中设置的视频通道 streamType 必须一致,同为主流或辅流,否则 NERTC SDK 会报错。

    • NERtcVideoFrame 格式为 NERtcVideoFrame.Format.TEXTURE_OES 时,请先调用 pushExternalVideoFrame 的线程完成 eglMakeCurrent 调用。 可以使用 GLHelper.initGLContext 完成初始化。

    • 如果设置的分辨率太高,可能会影响计费,请根据场景,合理设置 NERtcVideoFrame中的 heightwidth 值。典型场景的推荐参数请参见音视频参数配置推荐

示例项目源码

网易云信提供 自定义视频采集的示例项目源码 CustomCamera,您可以参考该源码实现自定义视频采集。

示例代码

objc//加入房间
- (void)joinChannel{
    NERtcEngine *coreEngine = [NERtcEngine sharedEngine];    

    NERtcVideoEncodeConfiguration *config = [[NERtcVideoEncodeConfiguration alloc] init];
    config.width = width;
    config.height = height;                     
    [coreEngine setLocalVideoConfig:config];    // 设置视频参数

    [coreEngine setExternalVideoSource:YES];    // 设置使用自定义数据源
    [coreEngine enableLocalVideo:YES];          // 开始视频

    //加入房间
    [coreEngine joinChannelWithToken:token channelName:channelName myUid:myUid completion:^(NSError * _Nullable error, uint64_t channelId, uint64_t elapesd, uint64_t uid) {
    }];
}

//推送视频帧,以CMSampleBufferRef为例
- (void)onRecvSampleBuffer:(CMSampleBufferRef)sampleBuffer{
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    OSType srcPixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
    switch (srcPixelFormat) {
        case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
        case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: {
            format = kNERtcVideoFormatNV12;
        }
            break;
        case kCVPixelFormatType_32BGRA:{
            format = kNERtcVideoFormatBGRA;
        }
            break;
        default:
            break;
    }
    NSInteger width = CVPixelBufferGetWidth(pixelBuffer);
    NSInteger height = CVPixelBufferGetHeight(pixelBuffer);
        
    uint64_t captureTimestampMs = CACurrentMediaTime()*1000;
    
    NERtcVideoFrame *frame = [[NERtcVideoFrame alloc] init];
    frame.width = (uint32_t)width;
    frame.height = (uint32_t)height;
    frame.rotation = self.inputRotation;
    frame.timestamp = captureTimestampMs;
    frame.buffer = (void *)pixelBuffer;
    frame.format = format;

    //在加入房间后,向SDK推送视频数据
    [NERtcEngine.sharedEngine pushExternalVideoFrame:frame]; // 推送自定义数据
}

API 参考

方法 功能描述
enableLocalVideo 开启或关闭本地视频采集。
setExternalVideoSource 开启外部视频源数据输入。
pushExternalVideoFrame 推送外部视频数据帧。
setLocalVideoConfig 设置视频参数

常见问题

  • 自定义视频源支持哪些数据格式?

    自定义视频源的数据格式支持 I420、NV12、BGRA。

  • 自定义视频采集的分辨率和setLocalVideoConfig中设置的视频分辨率不一致时,以哪个分辨率为准

    pushExternalVideoFrame 输入的视频分辨率和 setLocalVideoConfig 中设置的分辨率、画布比例不一致时,例如pushExternalVideoFrame的分辨率为640 x 480(4:3)、setLocalVideoConfig的分辨率为1280 x 720(16:9),NERTC SDK 的推流视频处理逻辑如下:

    1. 画布比例采用 setLocalVideoConfig 中设置的画布比例,本示例中为 16:9。
    2. 将自定义视频采集的分辨率(本示例中为 640 x 480)按照setLocalVideoConfig 中设置的画布比例(本示例中为 16:9)进行裁剪,得到一个新的分辨率(640 x 360)。
    3. 根据上一步得到的新分辨率和setLocalVideoConfig 中的分辨率,取两者中较小的分辨率作为推流分辨率。因此,本示例中最终的视频分辨率为 640 x 360。
此文档是否对你有帮助?
有帮助
去反馈
  • 功能描述
  • 技术原理
  • 注意事项
  • 实现方法
  • API 调用时序
  • 配置步骤
  • 示例项目源码
  • 示例代码
  • API 参考
  • 常见问题