视频直播

更新时间:2023-06-05 02:49:21下载pdf

涂鸦 IPC SDK 提供智能摄像机的实时视频播放、设备存储卡录像播放、对当前正在播放的视频截图、录制视频,与摄像机实时通话等基础能力。此外,还提供视频帧解码后的 YUV 数据,您可以对视频裸流数据进行二次开发。

播放流程

在创建 ThingSmartCameraType 实例对象后,就可以开始播放实时视频了。视频数据的传输和命令交互基于 P2P 通道实现。整体流程如下图所示:

视频直播

连接 P2P 通道

在开始播放视频之前,您需要先连接 P2P 通道。P2P 的连接状态需要您自行维护,IPC SDK 会负责下发命令和接收摄像机响应结果。

接口说明

开始连接 P2P 通道,参数可以指定优先选择的连接模式,通过局域网连接或者通过外网连接。如果指定局域网连接优先,但是 App 和设备没有在同一个局域网内建立 TCP 连接,或者设备不支持局域网连接优先,IPC SDK 会自动使用外网连接模式。

- (void)connectWithMode:(ThingSmartCameraConnectMode)mode;

connectWithMode: 方法可能会阻塞线程,建议在子线程调用。

参数说明

参数 说明
mode 优先使用的连接模式

ThingSmartCameraConnectMode

枚举值 说明
ThingSmartCameraConnectAuto 自动选择
ThingSmartCameraConnectFromInternet 外网连接优先
ThingSmartCameraConnectFromLocal 局域网连接优先

接口说明

断开 P2P 通道

- (void)disConnect;

代理回调

  • SDK 初始化失败:

    - (void)cameraInitFailed:(ThingSmartCameraErrorCode)errorCode;
    
  • P2P 通道成功连接:

    - (void)cameraDidConnected:(id<ThingSmartCameraType>)camera;
    
  • P2P 通道已断开,被动断开时(如网络环境较差,或者设备主动断开连接)才会调用,具体原因请参考 错误码

    - (void)cameraDisconnected:(id<ThingSmartCameraType>)camera specificErrorCode:(NSInteger)errorCode
    

播放实时视频

P2P 通道连接成功以后,就可以开始播放实时视频了。

接口说明

  • 开始播放实时视频:

    - (void)startPreview;
    
  • 开始播放实时视频,并指定清晰度:

    - (void)startPreviewWithDefinition:(ThingSmartCameraDefinition)definition;
    
  • 停止播放实时视频:

    - (void)stopPreview;
    
  • 获取视频渲染视图:

    - (UIView<ThingSmartVideoViewType> *)videoView;
    

代理回调

  • 视频直播已经成功开始播放:

    - (void)cameraDidBeginPreview:(id<ThingSmartCameraType>)camera;
    
  • 视频直播已经成功停止播放:

    - (void)cameraDidStopPreview:(id<ThingSmartCameraType>)camera;
    

视频渲染

视频成功播放后,IPC SDK 收到视频流将会自动渲染。您可以通过 camera 对象的 - (UIView<ThingSmartVideoViewType> *)videoView 方法获取渲染视图,将其添加到屏幕上,并设置布局属性。

协议说明

协议名 说明
ThingSmartVideoViewType 视频渲染器协议,根据摄像机实现方案不同,视频渲染的具体实现也有不同。

接口说明

  • 图像缩放属性:

    • 默认是 NO,如果视图的宽高比和视频图像的宽高比不一样,则会在图像的上下或者左右两侧留有黑边。
    • 设置为 YES,图像会拉伸铺满整个视图,可能会造成图像变形。

    IPC SDK 暂不提供全屏播放的能力。将 scaleToFill 属性设置为 YES 进行全屏模式时,横竖屏的旋转和布局的变化等请您自行维护。

    @property (nonatomic, assign) BOOL scaleToFill;
    
  • 清除当前的图像和缓存的视频帧:

    - (void)thing_clear;
    
  • 对当前渲染的图像截图:

    - (UIImage *)screenshot;
    

ThingSmartCameraType 对象提供了一个 autoRender 属性,默认是 YES,表示 IPC SDK 会自动渲染视频图像。如果您不希望自动渲染视频,可将此属性设置为 NO,然后可以从代理方法中获取到每一帧视频的 YUV 数据,并自主开发视频渲染,详细的接口在 裸流数据 章节中介绍。

错误回调

摄像机操作失败的所有错误反馈都将通过代理方法回调。

接口说明

- (void)camera:(id<ThingSmartCameraType>)camera didOccurredErrorAtStep:(TYCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode;

参数说明

参数 说明
camera 发生错误的摄像机对象。
errStepCode 发生错误的操作。
errorCode 错误码,表示失败的原因,详情请参考 错误码 章节。

ThingCameraErrorCode 枚举

枚举值 说明
Thing_ERROR_NONE
Thing_ERROR_CONNECT_FAILED P2P 连接失败
Thing_ERROR_START_PREVIEW_FAILED 实时视频播放失败
Thing_ERROR_START_PLAYBACK_FAILED 存储卡视频播放失败
Thing_ERROR_PAUSE_PLAYBACK_FAILED 暂停存储卡视频播放失败
Thing_ERROR_RESUME_PLAYBACK_FAILED 恢复存储卡视频播放失败
Thing_ERROR_ENABLE_MUTE_FAILED 视频声音开关失败
Thing_ERROR_START_TALK_FAILED 开启对讲失败
Thing_ERROR_RECORD_FAILED 视频录制失败
Thing_ERROR_ENABLE_HD_FAILED 设置视频清晰度失败
Thing_ERROR_GET_HD_FAILED 获取视频清晰度失败
Thing_ERROR_QUERY_RECORD_DAY_FAILED 存储卡视频回放日期查询失败
Thing_ERROR_QUERY_TIMESLICE_FAILED 存储卡视频片段查询失败

示例代码

Objective-C:

#define kThingSmartIPCConfigAPI @"thing.m.ipc.config.get"
#define kThingSmartIPCConfigAPIVersion @"2.0"

- (void)startStream {
    if (self.connected) {
        [self.camera startPreview];
        return;
    }
    id p2pType = [self.deviceModel.skills objectForKey:@"p2pType"];
    [[TuyaSmartRequest new] requestWithApiName:kThingSmartIPCConfigAPI postData:@{@"devId": self.devId} version:kThingSmartIPCConfigAPIVersion success:^(id result) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            ThingSmartCameraConfig *config = [ThingSmartCameraFactory ipcConfigWithUid:[ThingSmartUser sharedInstance].uid localKey:self.deviceModel.localKey configData:result];
            self.camera = [ThingSmartCameraFactory cameraWithP2PType:p2pType config:config delegate:self];
            [self.camera connect];
        });
    } failure:^(NSError *error) {
                // 获取配置信息失败
    }];
}

#pragma mark - ThingSmartCameraDelegate

- (void)cameraDidConnected:(id<ThingSmartCameraType>)camera {
    self.connected = YES;
    // 需要 P2P 连接成功后再开始预览
        [camera startPreview];
}

- (void)cameraDisconnected:(id<ThingSmartCameraType>)camera specificErrorCode:(NSInteger)errorCode {
    // P2P 连接被动断开,一般为网络波动导致
    self.connected = NO;
    self.previewing = NO;
}

- (void)cameraDidBeginPreview:(id<ThingSmartCameraType>)camera {
        // 实时视频开始播放
    self.previewing = YES;
    // 将视频渲染视图添加到屏幕上
        [self.view addSubview:camera.videoView];
}

- (void)cameraDidStopPreview:(id<ThingSmartCameraType>)camera {
        // 实时视频停止播放
    self.previewing = NO;
}

// 错误回调
- (void)camera:(id<ThingSmartCameraType>)camera didOccurredErrorAtStep:(ThingCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode {
        if (errStepCode == Thing_ERROR_CONNECT_FAILED) {
        // P2P 连接失败
        self.connected = NO;
    }
    else if (errStepCode == Thing_ERROR_START_PREVIEW_FAILED) {
        // 实时视频播放失败
        self.previewing = NO;
    }
}

Swift:

func startStream() {
    if self.isConnected {
        self.camera.startPreview()
        return
    }
    let p2pType = self.deviceModel.skills["p2pType"]!
    ThingSmartRequest().request(withApiName: kThingSmartIPCConfigAPI, postData: ["devId": self.devId], version: kThingSmartIPCConfigAPIVersion, success: { result in
        guard let responder = result as? [AnyHashable:Any] else {
            return;
        }
        DispatchQueue.global().async {
            let config = ThingSmartCameraFactory.ipcConfig(withUid: ThingSmartUser.sharedInstance().uid, localKey: self.deviceModel.localKey, configData: responder)
            self.camera = ThingSmartCameraFactory.camera(withP2PType: p2pType, config: config, delegate: self)
            self.camera.connect()
        }
    }) { _ in
        // 获取配置信息失败
    }
}

func cameraDidConnected(_ camera: ThingSmartCameraType!) {
    self.isConnected = true
    // 需要 P2P 连接成功后再开始预览
    camera.startPreview()
}

func cameraDisconnected(_ camera: ThingSmartCameraType!, specificErrorCode: Int) {
    // P2P 连接被动断开,一般为网络波动导致
    self.isConnected = false
    self.isPreviewing = false
}

func cameraDidBeginPreview(_ camera: ThingSmartCameraType!) {
    // 实时视频开始播放
    self.isPreviewing = true;
    // 将视频渲染视图添加到屏幕上
    self.view.addSubview(camera.videoView())
}

func cameraDidStopPreview(_ camera: ThingSmartCameraType!) {
    // 实时视频停止播放
    self.isPreviewing = false
}

func camera(_ camera: ThingSmartCameraType!, didOccurredErrorAtStep errStepCode: ThingCameraErrorCode, specificErrorCode errorCode: Int) {
    if errStepCode == Thing_ERROR_CONNECT_FAILED {
        // P2P 连接失败
        self.isConnected = false
    }else if errStepCode == Thing_ERROR_START_PREVIEW_FAILED {
        // 实时视频播放失败
        self.isPreviewing = false
    }
}

App 进入后台的时候,需要停止视频播放。因为视频数据使用硬件解码 OpenGL 渲染,在后台的时候,继续播放,可能会造成 App 崩溃。另外,涂鸦摄像机一般最多支持同时连接 5 路 P2P 通道,即支持 5 个手机同时连接。因此建议 App 在后台停留一段时间后,主动断开 P2P 连接,以释放资源。