iOS 视频外部采集

视频外部采集

1、使用场景

当开发者业务中出现以下情况时,我们推荐使用 SDK 的外部采集功能:

  • 普通摄像头的采集无法满足需求。例如,包含了大量的原有业务。

  • 直播过程中,开发者需要使用摄像头完成的额外功能和 SDK 的默认逻辑有冲突,导致摄像头无法正常使用。例如,直播到一半,需要录制短视频。

  • 直播非摄像头数据。例如视频播放、屏幕分享、游戏直播等。

这是即构科技音视频SDK(ZegoLiveRoom SDK )高级功能系列第十篇。

2、功能简介

考虑到设备的独占问题,SDK 的视频外部采集采用的是面向对象的设计,帮助用户把原有采集代码封装成外部采集设备。

开发者通过实现 ZegoVideoCaptureFactory 和 ZegoVideoCaptureDevice 协议,可以把采集的数据传给 SDK 进行编码推流:

ZegoVideoCaptureFactory 是外部采集的入口,定义了创建、销毁 ZegoVideoCaptureDevice 的接口,向 SDK 提供管理 ZegoVideoCaptureDevice 生命周期的能力。需要调用 setVideoCaptureFactory 的地方必须实现该接口。

ZegoVideoCaptureDevice 定义基本的组件能力,包括 zego_allocateAndStart、zego_stopAndDeAllocate、zego_startCapture、zego_stopCapture,方便 SDK 在直播流程中进行交互。

请注意,SDK 会在适当的时机创建和销毁 ZegoVideoCaptureDevice,开发者无需担心生命周期不一致的问题。

3、步骤

外部采集的接口调用流程如下图所示:
在这里插入图片描述
3.1 创建外部采集工厂

下述代码展示了如何创建外部采集工厂。工厂保存了 ZegoVideoCaptureDevice 的实例,不会反复创建。

@interface ZegoVideoCaptureFactory : NSObject<ZegoVideoCaptureFactory>
@end

@implementation ZegoVideoCaptureFactory {
    ZegoVideoCaptureFromImage * g_device_;
}

- (id<ZegoVideoCaptureDevice>)zego_create:(NSString*)deviceId {
    if (g_device_ == nil) {
        g_device_ = [[ZegoVideoCaptureFromImage alloc]init];
    }
    return g_device_;
}

- (void)zego_destroy:(id<ZegoVideoCaptureDevice>)device {

}

@end

请注意:

大部分情况下,ZegoVideoCaptureFactory 会缓存 ZegoVideoCaptureDevice实例,开发者需避免创建新的实例,造成争抢独占设备(例如摄像头)。

开发者必须保证 ZegoVideoCaptureDevice 在 create 和 destroy 之间是可用的,请勿直接销毁对象。

3.2 设置外部采集工厂

开发者需要使用外部采集功能时,请在使用前调用 setVideoCaptureFactory: 设置外部采集工厂对象(此例中的对象为步骤 1 中所创建的 ZegoVideoCaptureFactory),该工厂对象不可为空。

setVideoCaptureFactory: 必须在 InitSDK 前调用。

请注意

  1. 如果用户释放了工厂对象,不再需要它时,请调用本接口将其设置为空。

  2. 若客户端使用开关控制是否使用外部采集,在开启外部采集后,需要先释放 SDK 对象(直接将对象置为 nil),再重新执行外部采集的设置。

if (bUse)
{ 
    if (g_factory == nil)
        g_factory = [[VideoCaptureFactoryDemo alloc] init];

    [ZegoLiveRoomApi setVideoCaptureFactory:g_factory];
}
else
{
    [ZegoLiveRoomApi setVideoCaptureFactory:nil];
}

3.3 创建外部采集设备

下述代码,以创建在内存中绘制图片的采集设备( ZegoVideoCaptureFromImage )为例,开发者可按各自的需求,参看实现步骤。

类定义

ZegoVideoCaptureFromImage 的类定义如下:

ZegoVideoCaptureFromImage.h

// 注意采集设备需要遵循 ZegoVideoCaptureDevice 协议
@interface ZegoVideoCaptureFromImage : NSObject<ZegoVideoCaptureDevice>

@end

ZegoVideoCaptureFromImage.m

@implementation ZegoVideoCaptureFromImage

@end

告知 SDK 当前采集数据的类型

由于采集的多样性,SDK 支持多种外部采集数据传递格式,所以开发者必须显示告知 SDK 当前采集设备使用何种数据传递类型。目前 SDK 支持的类型有:
在这里插入图片描述
后续示例代码都将以官方推荐的 CVPixelBuffer 类型来传输采集数据,用于演示 ZegoVideoCaptureFromImage 的使用:

- (ZegoVideoCaptureDeviceOutputBufferType)zego_supportBufferType {
    return ZegoVideoCaptureDeviceOutputBufferTypeCVPixelBuffer;
}

请注意 zego_supportBufferType 是optional的,但是在使用 ZegoVideoCaptureDeviceOutputBufferTypeGlTexture2D
ZegoVideoCaptureDeviceOutputBufferTypeEncodedFrame 时必须实现。

初始化资源

开发者初始化资源在 zego_allocateAndStart 中进行。

开发者在 zego_allocateAndStart 中获取到 client (SDK 内部实现的、同样实现 ZegoVideoCaptureClientDelegate 协议的客户端),用于通知 SDK 采集结果。

SDK 会在 App 第一次预览 / 推流 / 拉流时调用 zego_allocateAndStart。除非 App 中途调用过 zego_stopAndDeAllocate,否则 SDK 不会再调用 zego_allocateAndStart。

#pragma mark - ZegoVideoCaptureDevice

- (void)zego_allocateAndStart:(id<ZegoVideoCaptureClientDelegate>)client {
    client_ = client;
    is_take_photo_ = false;
}

释放资源

开发者释放资源在 zego_stopAndDeAllocate 中进行。

建议同步停止采集任务后清理 client 对象,保证 SDK 调用 zego_stopAndDeAllocate 后,没有残留的异步任务导致野指针 crash。

- (void)zego_stopAndDeAllocate {
    [client_ destroy];   // 必须 destroy client
    client_ = nil;
}

请注意,开发者必须在 zego_stopAndDeAllocate 方法中调用 client 的 destroy 方法,否则会造成内存泄漏。

启动采集

推流成功后,开发者需要在 zego_startCapture 中,采集数据并传递给 SDK 的 client 对象。

这里演示的是启动一个定时器,按照帧率定时触发发送数据。

// SDK 推流成功后,调用 zego_startCapture 通知外部采集设备把采集到的图像传给 client 对象。
- (int)zego_startCapture {
    if(m_oState.capture) {
        // * already started
        return 0;
    }

    m_oState.capture = true;

    dispatch_async(dispatch_get_main_queue(), ^{
        [g_fps_timer invalidate];
        int fps = m_oSettings.fps > 0 ?: 15;
        fps = 15;
        NSLog(@"%s, fps: %d", __func__, fps);

        // 启动一个定时器,按照帧率定时触发发送数据
        g_fps_timer = [NSTimer scheduledTimerWithTimeInterval:1.0/fps target:self selector:@selector(handleTick) userInfo:nil repeats:YES];

        if (pb) {
            CVPixelBufferRelease(pb);
            pb = NULL;
        }
    });
    return 0;
}

定时器触发后,外部采集设备在内存中绘制图像,并通过 client 的 onIncomingCapturedData:withPresentationTimeStamp: 方法,把数据传给 SDK 进行编码推流。

- (void)handleTick {
    if (pb) {
        CVPixelBufferRelease(pb);
        pb = NULL;
    }

    if (!pb) {
        UIImage *img = [UIImage imageNamed:@"[email protected]"];
        pb = [self pixelBufferFromCGImage:img.CGImage];
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    unsigned long long t = (unsigned long long)(tv_now.tv_sec) * 1000 + tv_now.tv_usec / 1000;

    CMTime pts = CMTimeMakeWithSeconds(t, 1000);

    // SDK 接受外部采集的视频帧数据
    [client_ onIncomingCapturedData:pb withPresentationTimeStamp:pts];
}

采集的图像数据存放在 CVPixelBufferRef 结构体中。

  • 颜色格式推荐使用:kCVPixelFormatType_32BGRA 和
    kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange。

  • 时间戳推荐使用:原始采集 API 返回的时间戳(如果是摄像头,可直接透传;如果是其他方式,可以使用系统当前时间,精度至少为毫秒)。

停止采集

开发者需要在 zego_startCapture 中停止外部采集设备的采集工作。

这里演示的是停止定时器。

// SDK停止推流时,调用 zego_stopCapture 通知外部采集设备停止工作
- (int)zego_stopCapture {
    if(!m_oState.capture) {
        // * capture is not started
        return 0;
    }

    m_oState.capture = false;

    dispatch_async(dispatch_get_main_queue(), ^{
        [g_fps_timer invalidate];
        g_fps_timer = nil;
    });
    return 0;
}

上述的示例代码均可以在 Demo 中的 ZegoVideoCaptureFromImage.h 和 ZegoVideoCaptureFromImage.m 找到,具体细节不再赘述。

猜你喜欢

转载自blog.csdn.net/sinat_20146421/article/details/84454989
今日推荐