Memory Card Recording

Last Updated on : 2024-06-27 07:56:12download

Recording to a memory card is a basic feature for smart cameras. This feature stores the audio, video, and image to the memory card in the specified directory and file structure, allowing for video search, playback, download, and deletion through the mobile app.

A connection to the peer-to-peer (P2P) channel is required before playback. Once the P2P channel is established, users can search for video clips on the camera’s memory card by time frame and play them. For more information, see Memory Card Management.

How it works

Memory Card Recording

Core methods

Demo Class Method Description Notes
ThingSmartCameraFactory +(id)cameraWithP2PType:deviceId:delegate:; Create a ThingSmartCameraType object. One device ID matches one ThingSmartCameraType object.
ThingSmartCameraType -(void)connectWithMode: Connect. When error -3 or -105 occurs, try reconnection.
-(void)queryRecordDaysWithYear:month: Query the dates when recordings exist. Invoke a callback through -(void)camera:didReceiveRecordDayQueryData: in ThingSmartCameraDelegate on a successful query.
-(void)queryRecordTimeSliceWithYear:month:day: Query video clips on the specified date. Invoke a callback through -(void)camera:didReceiveTimeSliceQueryData: in ThingSmartCameraDelegate on a successful query.
-(int)startPlayback:startTime:stopTime: Start playing the specified clip. Invoke a callback through -(void)cameraDidBeginPlayback: in ThingSmartCameraDelegate on success.
-(int)stopPlayback Stop playback. Invoke a callback through -(void)cameraDidStopPlayback: in ThingSmartCameraDelegate on success.
-(int)disConnect Disconnect.
ThingSmartCameraDelegate -(void)cameraInitFailed: Failed to create a ThingSmartCameraType object. The P2P type is not supported.
-(void)cameraDidConnected: Connected successfully.
-(void)camera:didReceiveRecordDayQueryData: Return the dates when recordings exist. An array of dates. For example, '@[@(1), @(2)]' indicates recordings are available for the first and second days of the specified month. An empty array is returned on failure.
-(void)camera:didReceiveTimeSliceQueryData: Return video clips on the specified date. The element type in timeSlices is NSDictionary.
-(void)cameraDidBeginPlayback: Playback starts successfully. The registered player will automatically play the clip.
-(void)camera:playbackTimeSlice:didFinishedWithStatus: Playback is finished. Status 1 indicates the specified clip has been played, while other values indicate all clips on that day have been played.
-(void)cameraDidStopPlayback: Playback is stopped successfully.
-(void)camera:didOccurredErrorAtStep:specificErrorCode: Return the failed playback action and error code. Errors might occur when you connect, query the dates when recordings exist, query video clips on the specified date, and start playback.

Important notes

  • Do not create two ThingSmartCameraType objects for the same device simultaneously to prevent resource errors.

  • During live streaming, you can switch to recording playback without disconnecting and reconnecting the P2P connection. However, you must stop live streaming before querying the video clips on the specified date. Otherwise, there might be flickering issues with the live streaming and recording playback screens. This can also occur when switching from recording playback to live streaming.

  • In the following scenarios, query the video clips on the specified date before playing them to avoid problems.

    • After stopping the recording playback, switch to live streaming and then return to playback.
    • After reconnecting to the previously lost P2P channel, start the recording playback.

Core code

Create a ThingSmartCameraType object

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

- (void)cameraInitFailed:(ThingSmartCameraErrorCode)errorCode;

Connect

- (void)connectWithMode:(ThingSmartCameraConnectMode)mode;

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

Query the dates when recordings exist in the specified month and year

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

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

Query video clips on the specified date

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

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

Start playback

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

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

Stop playback

- (void)stopPlayback;

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

Playback completion callback

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

Playback error

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

Disconnect

- (int)disConnect;

Example


//1. Create a ThingSmartCameraType object
- (void)cameraInit {
    // Initialize the device
    self.device = [ThingSmartDevice deviceWithDeviceId:devId];

    // delegate: ThingSmartCameraDelegate   Used to monitor the connection status of P2P channels
    self.camera = [ThingSmartCameraFactory     cameraWithP2PType:@(self.device.deviceModel.p2pType)     deviceId:self.device.deviceModel.devId delegate:self];

    // Initialize video preview page
    self.videoView = self.camera.videoView;

    // Add the video rendering view to the screen
   [self.view addSubview:self.videoView];
}

//2. Connect
- (void)cameraConnect {
    [self.camera connectWithMode:ThingSmartCameraConnectAuto];
}
// Connection successful
- (void)cameraDidConnected:(id<ThingSmartCameraType>)camera {
    self.connected = YES;
      // After the P2P connection is successful, query video footage for a specific day, with the date provided as needed.
     [self queryRecordDaysWithYear:self.year month:self.month];
}
// Connection failed
- (void)cameraDisconnected:(id<ThingSmartCameraType>)camera specificErrorCode:(NSInteger)errorCode {
    // P2P is passively disconnected, usually caused by network jitter.
    self.connected = NO;
}

//3. Query the dates in a specific year and month when video recordings were saved.
- (void)queryRecordDaysWithYear:(NSUInteger)year month:(NSUInteger)month {
    [self.camera queryRecordDaysWithYear:year month:month];
}
// Query callback
- (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;
    // If `days` is empty, there are no recordings.
    if (days.count == 0) {
        return;
    }
    [self queryRecordTimeSliceWithYear:self.year month:self.month day:[days.firstObject integerValue]];
}

//4. Query recordings for a specified date.
- (void)queryRecordTimeSliceWithYear:(NSUInteger)year month:(NSUInteger)month day:(NSUInteger)day {
    [self.camera queryRecordTimeSliceWithYear:year month:month day:day];
}
// Query callback
- (void)camera:(id<ThingSmartCameraType>)camera didReceiveTimeSliceQueryData:(NSArray<NSDictionary *> *)timeSlices {
    // If there is no recording for the day, no video will paly.
    if (timeSlices.count == 0) {
        return;
    }
    // Save the video recording list and start playing from the first one.
    self.timeSlicesInCurrentDay = [timeSlices copy];
    self.timeSlicesIndex = 0;
    NSDictionary *timeSlice = timeSlices.firstObject;
    NSInteger startTime = [timeSlice[kThingSmartTimeSliceStartTime] integerValue];
    NSInteger stopTime = [timeSlice[kThingSmartTimeSliceStopTime] integerValue];
      // Start playing from the first second of the first video clip.
    NSInteger playTime = startTime;
    [self startPlayback:playTime startTime:startTime stopTime:stopTime];
}

//5. Start playback
- (void)startPlayback:(NSInteger)playTime startTime:(NSInteger)startTime stopTime:(NSInteger)stopTime {
    [self.camera startPlayback:playTime startTime:startTime stopTime:stopTime];
}
// The callback for starting playback
- (void)cameraDidBeginPlayback:(id<ThingSmartCameraType>)camera {
      // The video playback starts.
    self.playbacking = YES;
    self.playbackPaused = NO;
}

//6. Stop playback
- (void)stopPlayback {
    [self.camera stopPlayback];
}

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

//7. Disconnect
- (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 there is no next video recording, then return.
    if (index >= self.timeSlicesInCurrentDay.count) {
        return;
    }
    NSDictionary *currentTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:self.timeSlicesIndex];
    NSInteger stopTime = [currentTimeSlice[kThingSmartTimeSliceStopTime] integerValue];
      // If the timestamp of the current video frame is greater than or equal to the end time of the current video clip, then play the next video clip.
    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 {
      // The video playback ends.
    self.playbacking = NO;
    self.playbackPaused = NO;
}

// Error callback
- (void)camera:(id<ThingSmartCameraType>)camera didOccurredErrorAtStep:(ThingCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode extErrorCodeInfo:(id<ThingSmartCameraExtErrorCodeInfo>)extErrorCodeInfo {
        if (errStepCode == Thing_ERROR_CONNECT_FAILED) {
          // P2P connection failed
        self.connected = NO;
    }
    else if (errStepCode == Thing_ERROR_START_PLAYBACK_FAILED) {
          // Failed to play video from the memory card
        self.playbacking = NO;
        self.playbackPaused = NO;
    }
}