存储卡录像

更新时间:2024-06-17 10:20:15下载pdf

存储卡录像 是智能摄像机的基础功能。该功能负责管理存储卡,将音视频和图片等数据按特定目录和文件结构存储到存储卡上的通用文件系统,可以通过 IPC SDK 在 App 端对存储数据进行检索、回放、下载和删除等操作。

同实时视频直播一样,在开始回放前,您需要先连接上 P2P(Peer-to-peer)通道。P2P 通道连接成功后,用户可以查询到设备端存储卡中录制的视频片段时间信息,然后播放视频片段。关于存储卡管理,请查看 存储卡管理

流程说明

存储卡录像

核心接口

演示录屏 接口 描述 备注
ThingSmartCameraFactory +(id)cameraWithP2PType:deviceId:delegate:; 创建 ThingSmartCameraType 对象 一个设备 ID 对应一个 ThingSmartCameraType 对象
ThingSmartCameraType -(void)connectWithMode: 连接 -3 或 -105 建议重连
-(void)queryRecordDaysWithYear:month: 查询回放日期 查询成功后,通过 ThingSmartCameraDelegate 中的 -(void)camera:didReceiveRecordDayQueryData: 方法回调
-(void)queryRecordTimeSliceWithYear:month:day: 查询指定日期的回放录像片段 查询成功后,通过 ThingSmartCameraDelegate 中的 -(void)camera:didReceiveTimeSliceQueryData: 方法回调
-(int)startPlayback:startTime:stopTime: 开始播放指定回放片段 开始回放成功后,通过 ThingSmartCameraDelegate 中的 -(void)cameraDidBeginPlayback: 方法回调
-(int)stopPlayback 停止回放 停止回放成功后,通过 ThingSmartCameraDelegate 中的 -(void)cameraDidStopPlayback: 方法回调
-(int)disConnect 断开连接
ThingSmartCameraDelegate -(void)cameraInitFailed: 创建 ThingSmartCameraType 对象失败 P2P 类型不支持
-(void)cameraDidConnected: 连接成功
-(void)camera:didReceiveRecordDayQueryData: 返回有回放录像的日期 回放的日期数组,例如 '@[@(1), @(2)]' 表示查询的当月 1 号和 2 号有回放录像,失败返回空数组
-(void)camera:didReceiveTimeSliceQueryData: 返回指定日期的回放录像片段 timeSlices 中的元素类型是 NSDictionary
-(void)cameraDidBeginPlayback: 开始回放成功 开始回放成功后,注册的播放器会自动播放
-(void)camera:playbackTimeSlice:didFinishedWithStatus: 视频录像播放结束 status 为 1 时表示片段播放结束,其他值表示全天播放结束
-(void)cameraDidStopPlayback: 停止回放成功
-(void)camera:didOccurredErrorAtStep:specificErrorCode: 返回回放相关操作失败的步骤和错误码 连接,查询回放日期,查询回放片段,开始回放等操作

重点关注

  • 不要对同一个设备同时创建两个 ThingSmartCameraType 对象,否则会导致资源错误,释放出现异常情况。

  • 在实时视频播放时,如果想要切换到录像播放模式,不需要断开 P2P 连接再重新连接 P2P 通道。但是,需要先停止实时视频播放,再查询当天的视频录像开始播放。否则,会出现实时视频画面和视频录像画面串流闪烁的情况。视频录像回放切换实时视频播放的时候,也是如此。

  • 以下情况中,需要重新查询一下当天的视频录像片段,否则可能会出现播放异常:

    • 停止播放视频录像后,再进行实时视频播放,再次进行开始回放
    • P2P 连接断开,重新连接上 P2P 通道后,再次播放视频录像

核心代码

创建 ThingSmartCameraType 对象

+ (id<ThingSmartCameraType>)cameraWithP2PType:(id)type deviceId:(NSString *)devId delegate:(id<ThingSmartCameraDelegate>)delegate;

- (void)cameraInitFailed:(ThingSmartCameraErrorCode)errorCode;

连接

- (void)connectWithMode:(ThingSmartCameraConnectMode)mode;

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

查询某年某月中保存过视频录像的日期

- (void)queryRecordDaysWithYear:(NSUInteger)year month:(NSUInteger)month;

- (void)camera:(id<ThingSmartCameraType>)camera didReceiveRecordDayQueryData:(NSArray<NSNumber *> *)days;

查询指定日期的回放片段

- (void)queryRecordTimeSliceWithYear:(NSUInteger)year month:(NSUInteger)month day:(NSUInteger)day;

- (void)camera:(id<ThingSmartCameraType>)camera didReceiveTimeSliceQueryData:(NSArray<NSDictionary *> *)timeSlices;

开始回放

- (void)startPlayback:(NSInteger)playTime startTime:(NSInteger)startTime stopTime:(NSInteger)stopTime;

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

停止回放

- (void)stopPlayback;

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

播放结束回调

- (void)camera:(id<ThingSmartCameraType>)camera playbackTimeSlice:(NSDictionary *)timeSlice didFinishedWithStatus:(NSInteger)status;

回放相关操作失败

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

断开连接

- (int)disConnect;

示例代码


//1.创建 ThingSmartCameraType 对象
- (void)cameraInit {
    //初始化设备
    self.device = [ThingSmartDevice deviceWithDeviceId:devId];

    // delegate: ThingSmartCameraDelegate 用于监听 P2P 通道连接状态
    self.camera = [ThingSmartCameraFactory     cameraWithP2PType:@(self.device.deviceModel.p2pType)     deviceId:self.device.deviceModel.devId delegate:self];

    //初始化视频预览页面
    self.videoView = self.camera.videoView;

    //将视频渲染视图添加到屏幕上
   [self.view addSubview:self.videoView];
}

//2.连接
- (void)cameraConnect {
    [self.camera connectWithMode:ThingSmartCameraConnectAuto];
}
//连接成功
- (void)cameraDidConnected:(id<ThingSmartCameraType>)camera {
    self.connected = YES;
      // 需要 P2P 连接成功后查询某天的视频录像片段,日期根据实际需求入参
     [self queryRecordDaysWithYear:self.year month:self.month];
}
//连接失败
- (void)cameraDisconnected:(id<ThingSmartCameraType>)camera specificErrorCode:(NSInteger)errorCode {
    // P2P 连接被动断开,一般为网络波动导致
    self.connected = NO;
}

//3.查询某年某月中保存过视频录像的日期
- (void)queryRecordDaysWithYear:(NSUInteger)year month:(NSUInteger)month {
    [self.camera queryRecordDaysWithYear:year month:month];
}
//查询回调
- (void)camera:(id<ThingSmartCameraType>)camera didReceiveRecordDayQueryData:(NSArray<NSNumber *> *)days {
    NSString *key = [NSString stringWithFormat:@"%ld-%ld", self.year, self.month];
    NSMutableDictionary *playbackDays = [self.playbackDaysInMonth mutableCopy];
    [playbackDays setObject:days forKey:key];
    self.playbackDaysInMonth = playbackDays;
    // 如果 days 为空,说明无回放
    if (days.count == 0) {
        return;
    }
    [self queryRecordTimeSliceWithYear:self.year month:self.month day:[days.firstObject integerValue]];
}

//4.查询指定日期的回放片段
- (void)queryRecordTimeSliceWithYear:(NSUInteger)year month:(NSUInteger)month day:(NSUInteger)day {
    [self.camera queryRecordTimeSliceWithYear:year month:month day:day];
}
//查询回调
- (void)camera:(id<ThingSmartCameraType>)camera didReceiveTimeSliceQueryData:(NSArray<NSDictionary *> *)timeSlices {
    // 如果当天没有视频录像,则不播放
    if (timeSlices.count == 0) {
        return;
    }
    // 保存视频录像列表,从第一个开始播放
    self.timeSlicesInCurrentDay = [timeSlices copy];
    self.timeSlicesIndex = 0;
    NSDictionary *timeSlice = timeSlices.firstObject;
    NSInteger startTime = [timeSlice[kThingSmartTimeSliceStartTime] integerValue];
    NSInteger stopTime = [timeSlice[kThingSmartTimeSliceStopTime] integerValue];
      // 从第一个视频片段的第一秒开始播放
    NSInteger playTime = startTime;
    [self startPlayback:playTime startTime:startTime stopTime:stopTime];
}

//5.开始回放
- (void)startPlayback:(NSInteger)playTime startTime:(NSInteger)startTime stopTime:(NSInteger)stopTime {
    [self.camera startPlayback:playTime startTime:startTime stopTime:stopTime];
}
//开始回放回调
- (void)cameraDidBeginPlayback:(id<ThingSmartCameraType>)camera {
      // 视频录像开始播放
    self.playbacking = YES;
    self.playbackPaused = NO;
}

//6.停止回放
- (void)stopPlayback {
    [self.camera stopPlayback];
}

- (void)cameraDidStopPlayback:(id<ThingSmartCameraType>)camera {
    self.playbacking = NO;
    self.playbackPaused = NO;
}

//7.断开连接
- (void)disconnect {
    [self.camera disConnect];
}

#pragma mark - ThingSmartCameraDelegate

- (void)camera:(id<ThingSmartCameraType>)camera thing_didReceiveVideoFrame:(CMSampleBufferRef)sampleBuffer frameInfo:(ThingSmartVideoFrameInfo)frameInfo {
    NSInteger index = self.timeSlicesIndex + 1;
      // 如果没有下一个视频录像,则返回
    if (index >= self.timeSlicesInCurrentDay.count) {
        return;
    }
    NSDictionary *currentTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:self.timeSlicesIndex];
    NSInteger stopTime = [currentTimeSlice[kThingSmartTimeSliceStopTime] integerValue];
      // 如果当前视频帧的时间戳大于等于当前视频片段的结束时间,则播放下一个视频片段
    if (frameInfo.nTimeStamp >= stopTime) {
        NSDictionary *nextTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:index];
        NSInteger startTime = [nextTimeSlice[kThingSmartTimeSliceStartTime] integerValue];
            NSInteger stopTime = [nextTimeSlice[kThingSmartTimeSliceStopTime] integerValue];
            NSInteger playTime = startTime;
            [self startPlayback:playTime startTime:startTime stopTime:stopTime];
    }
}

- (void)cameraPlaybackDidFinished:(id<ThingSmartCameraType>)camera {
      // 视频录像已结束播放
    self.playbacking = NO;
    self.playbackPaused = NO;
}

// 错误回调
- (void)camera:(id<ThingSmartCameraType>)camera didOccurredErrorAtStep:(ThingCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode extErrorCodeInfo:(id<ThingSmartCameraExtErrorCodeInfo>)extErrorCodeInfo {
        if (errStepCode == Thing_ERROR_CONNECT_FAILED) {
          // P2P 连接失败
        self.connected = NO;
    }
    else if (errStepCode == Thing_ERROR_START_PLAYBACK_FAILED) {
          // 存储卡录像播放失败
        self.playbacking = NO;
        self.playbackPaused = NO;
    }
}