You need to enable JavaScript to run this app.
导航

Native 端自定义视频渲染

最近更新时间2023.04.20 16:49:21

首次发布时间2021.12.14 15:51:23

适用场景

当你使用 RTC 实现实时音视频通信时,RTC 默认使用内部的渲染模块进行音视频渲染。然而在一些场景下,你可能会发现内部渲染模块无法满足需求,比如:

  • 音视频应用中已实现了视频采集和渲染模块,例如开发游戏应用。
  • 希望在视频渲染前对视频帧做一些额外的处理,比如存储为图片、增加视频特效等。

前提条件

你已经集成了 3.25 及以上版本的 RTC SDK,实现了基本的音视频通话。

功能说明

将 RTC SDK 在本地采集的视频图像或远端用户的视频图像通过自定义的渲染模块进行渲染。

说明:不同平台的实现步骤相同,但接口名称、参数名称可能略有差异。以下指南以 Android RTC SDK 为例,参考对应平台的 API 文档获取更多信息。

时序图

alt

步骤1:构建自定义渲染器

注意:由于硬件差异,通过 IVideoSink.onFrame 收到的视频帧可能会呈一定角度。你可以调用 RTCVideoFrame.getRotation 获取该角度,并在自定义渲染器中进行相应处理。

/**通过创建 ByteRTCVideoSinkDelegate 实例,添加自定义渲染逻辑,构建自定义渲染器。*/
    
@interface CustomVideoRenderView : UIView <ByteRTCVideoSinkDelegate>

@end

@implementation CustomVideoRenderView
/**
 * 视频帧回调
 * @param pixelBuffer 视频的 PixelBuffer
 * @param rotation 视频旋转角度,参看 ByteRTCVideoRotation
 * @param contentType 视频内部类型 参看 ByteRTCVideoContentType
 * @param extendedData 视频解码后获得的附加数据
 */
- (void)renderPixelBuffer:(CVPixelBufferRef _Nonnull)pixelBuffer
                 rotation:(ByteRTCVideoRotation)rotation
              contentType:(ByteRTCVideoContentType)contentType
             extendedData:(NSData * _Nullable)extendedData {
    // 在此进行自定义视频渲染
    CFRetain(pixelBuffer);
    dispatch_async(dispatch_get_main_queue(), ^{
        imageView.image = [UIImage imageWithCIImage:[CIImage imageWithCVImageBuffer:pixelBuffer]];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        switch (rotation) {
            case VideoRotation_0:
                imageView.transform = CGAffineTransformMakeRotation(0);
                break;
            case VideoRotation_90:
                imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
                break;
            case VideoRotation_180:
                imageView.transform = CGAffineTransformMakeRotation(M_PI);
                break;
            case VideoRotation_270:
                imageView.transform = CGAffineTransformMakeRotation(M_PI_2 + M_PI);
                break;
                
            default:
                break;
        }
        CFRelease(pixelBuffer);
    });
}
@end

步骤2:绑定自定义渲染器

获取到视频流信息以后,将视频流绑定到自定义渲染器。通过传入参数 requiredFormat 指定视频数据的像素格式。

渲染本地视频

调用 setLocalVideoSink 为本地媒体流指定自定义渲染器。
在开始采集之后,视频帧将通过 renderPixelBuffer/IVideoSink.onFrame 传递到自渲染模块。
image

[self.rtcVideo startVideoCapture];
CustomVideoRenderView *renderView = [[CustomVideoRenderView alloc] init];
[self.rtcVideo setLocalVideoSink:ByteRTCStreamIndexMain withSink:renderView withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];

渲染远端视频

调用 setRemoteVideoSink 为远端媒体流指定自定义渲染器。
绑定自定义渲染器所需的媒体流参数可以从 onUserPublishStream 回调获取。
用户进入房间,并订阅了远端视频流,视频帧到达后将通过 renderPixelBuffer/IVideoSink.onFrame 传递到自渲染模块。
image

- (void)rtcRoom:(ByteRTCRoom *)rtcRoom onUserPublishStream:(NSString *)userId type:(ByteRTCMediaStreamType)type {
    if (type == ByteRTCMediaStreamTypeVideo || type == ByteRTCMediaStreamTypeBoth) {
        ByteRTCRemoteStreamKey *streamKey = [[ByteRTCRemoteStreamKey alloc] init];
        streamKey.userId = userId;
        streamKey.streamIndex = ByteRTCStreamIndexMain;
        streamKey.roomId = self.roomID;
        CustomVideoRenderView *renderView = [[CustomVideoRenderView alloc] init];
        [self.rtcVideo setRemoteVideoSink:streamKey withSink:renderView withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];
    }
}

渲染公共流视频

调用 setPublicStreamVideoSink 为公共流指定自定义渲染器。
用户订阅了公共流,视频帧到达后,将通过 renderPixelBuffer/IVideoSink.onFrame 传递到自渲染模块。
更多关于公共流的信息详见发布和订阅公共流

/**
 * 设置公共流视频渲染
 * @param streamId 公共流ID
 */
- (void)setPublicStreamCustomVideoRender:(NSString *)streamId {
    CustomVideoRenderView *renderView = [[CustomVideoRenderView alloc] init];
    [self.rtcVideo setPublicStreamVideoSink:streamId withSink:renderView withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];
}

步骤3:解绑自定义渲染器

调用步骤2 中提到的相应接口,将 videoSink 设置为 null。解绑之后,视频帧将通过 SDK 内部渲染器进行渲染。

/**
 * 解绑本地自定义渲染
 */
- (void)unBindLocalCustomVideoRender {
    [self.rtcVideo setLocalVideoSink:ByteRTCStreamIndexMain withSink:nil withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];
}
/**
 * 解绑远端视频自定义渲染
 * @param streamKey 远端视频信息
 */
- (void)unBindRemoteCustomVideoRender:(ByteRTCRemoteStreamKey *)streamKey {
    [self.rtcVideo setRemoteVideoSink:streamKey withSink:nil withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];
}
/**
 * 解绑公共流视频自定义渲染
 * @param streamId 公共流ID
 */
- (void)unBindPublicStreamCustomVideoRender:(NSString *)streamId {
    [self.rtcVideo setPublicStreamVideoSink:streamId withSink:nil withPixelFormat:ByteRTCVideoSinkPixelFormatNV12];
}

API 参考

功能/平台AndroidiOSmacOSWindows
将本地视频源与外部渲染器绑定setLocalVideoSinksetLocalVideoSink:withSink:withPixelFormat:setLocalVideoSink:withSink:withPixelFormat:setLocalVideoSink
开始内部视频采集startVideoCapturestartVideoCapturestartVideoCapturestartVideoCapture
将远端视频源与外部渲染器绑定setRemoteVideoSinksetRemoteVideoSink:withSink:withPixelFormat:setRemoteVideoSink:withSink:withPixelFormat:setRemoteVideoSink
将公共流视频源与外部渲染器绑定setPublicStreamVideoSinksetPublicStreamVideoSink:withSink:withPixelFormat:setPublicStreamVideoSink:withSink:withPixelFormat:setPublicStreamVideoSink