Memory Card Recording

Last Updated on : 2024-06-27 10:13:33download

The Smart Camera SDK supports recording to the memory card. With a memory card inserted in the camera, users can view card information and status, as well as set recording settings and modes.

Description

Users can view recorded footage from the smart camera’s memory card on the app through the Smart Camera SDK. 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.

See Memory Card Management for details.

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

Playback flow

Memory Card Recording

Video clip

The video clip stored on the camera’s memory card can range from 10 seconds to 10 minutes in length. The Smart Camera SDK enables users to view and play recordings by day, as well as retrieve the dates in a month when recordings are available. The query results are returned through the delegate method in ThingSmartCameraDelegate.

Query the month when recordings exist

API description

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

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

Parameters

Parameter Description
year The year, for example, 2020.
month The month, for example, February.

Delegate callback

The callback for querying dates with video recordings.

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

Parameters

Parameter Description
camera The camera object executing the query.
days 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.

Query video clips by date

API description

Query video clips on the specified date.

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

Parameters

Parameter Description
year The year, for example, 2020.
month The month, for example, February.
day The day, for example, 22.

Delegate callback

The callback for querying video clips on the specified date.

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

Parameters

Parameter Description
camera The camera object executing the query.
timeSlices An array of time information about the video clips on that day. An empty array is returned on failure.

timeSlices data type

The element type in timeSlices is NSDictionary.

Field (constant in SDK) Type Description
kThingSmartPlaybackPeriodStartDate NSDate The start time of the video clip.
kThingSmartPlaybackPeriodStopDate NSDate The end time of the video clip.
kThingSmartPlaybackPeriodStartTime NSNumber The start Unix timestamp of the video clip.
kThingSmartPlaybackPeriodStopTime NSNumber The end Unix timestamp of the video clip.

Video play

Once a video clip is retrieved, it can be played.

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

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

Start playback

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.

API description

Start playing a video clip, with playTime ranging from [startTime, stopTime).

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

Parameters

Parameter Description
playTime The Unix timestamp in the video clip to start playing from.
startTime The start Unix timestamp of the video clip.
stopTime The end Unix timestamp of the video clip.

Delegate method

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

Pause playback

API description

- (void)pausePlayback;

Delegate method

The video playback is paused.

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

Resume playback

API description

- (void)resumePlayback;

Delegate method

The video playback is resumed.

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

Stop playback

API description

- (void)stopPlayback;

Delegate method

The video playback is stopped.

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

Playback completion callback

The video playback is completed. status 1 indicates the specified clip has been played, while other values indicate all clips on that day have been played.

API description

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

Continuous playback

The Smart Camera SDK supports two recording modes: continuous recording and event recording.

  • In continuous recording mode, the camera records video in 10-minute clips. Interruptions during recording may result in gaps between these clips.

    If the video footage from a specific day is continuous, the next clip will play automatically during playback. Even if the start playback method is called with the timestamp of the first video clip of the day, the video will play until reaching the last frame of that day’s final clip before triggering the playback completion callback.

  • During event recording, the length and intervals of video clips vary.

    If the video footage from a specific day is not continuous, there may be a gap between the end of clip A and the start of clip B. When the playback reaches the point where the recording is interrupted, which is the last frame of clip A in this example, the video stream will automatically stop. The Smart Camera SDK will not receive a video playback completion callback. In the latest Smart Camera SDK, if video clips are not continuous, each clip can trigger a callback signaling the end of playback. This enables you to play the next clip upon receiving the callback to achieve an uninterrupted viewing experience. If you are not using the latest SDK, you can determine if the current frame is the last one of a clip by checking the timestamp in the frame information from Video Frame Data Callback.

Pause and stop

Both pausePlayback and stopPlayback can stop video playback, with the difference being:

  • After calling stopPlayback, you cannot continue playing with resumePlayback.
  • To resume playback from where the user left off, save the timestamp of the last frame when playback stops and call startPlayback to continue playing.
  • After retrieving the clip time data, you can directly call startPlayback to play another clip without needing to stop the current playback first, whether it is ongoing or paused.

Set playback speed

Some smart cameras support variable speed playback for videos stored on a memory card. After a P2P connection is created, you can query the playback speed options and set the speed during playback.

API description

Query the playback speed options after P2P is connected, returning an array of ThingSmartCameraPlayBackSpeed enum.

- (NSArray<NSNumber *> *)getSupportPlaySpeedList;

ThingSmartCameraPlayBackSpeed enum

Enum value Description
ThingSmartCameraPlayBackSpeed_05TIMES 0.5x
ThingSmartCameraPlayBackSpeed_10TIMES 1x
ThingSmartCameraPlayBackSpeed_20TIMES 2x
ThingSmartCameraPlayBackSpeed_40TIMES 4x
ThingSmartCameraPlayBackSpeed_80TIMES 8x
ThingSmartCameraPlayBackSpeed_160TIMES 16x
ThingSmartCameraPlayBackSpeed_320TIMES 32x

API description

Set the playback speed for videos played from the memory card.

- (void)speedPlayWithPlayBackSpeed:(ThingSmartCameraPlayBackSpeed)playBackSpeed;

Parameters

Parameter Type Description
playBackSpeed ThingSmartCameraPlayBackSpeed The desired playback speed, which must be supported by the camera.

Video download

Some smart cameras allow users to download video from the memory card to the mobile app.

Check support for video download

Check if the camera supports downloading video from the memory card. This method should be invoked after P2P is connected.

- (BOOL)isSupportPlaybackDownload;

Start download

API description

- (int)downloadPlayBackVideoWithRange:(NSRange)timeRange filePath:(NSString *)videoPath success:(void(^)(NSString *filePath))success progress:(void(^)(NSUInteger progress))progress failure:(void(^)(NSError *error))failure;

Parameters

Parameter Type Description
timeRange NSRange The range of videos to be downloaded.
videoPath NSString The file path to save the downloaded video that must include the .mp4 suffix.
success Block The download success callback.
progress Block The download progress callback, with parameters ranging from 0 to 100.
failure Block The download failure callback, returning a negative number.

Smart cameras have limitations in retrieving playback video files, so the range and starting point for video downloads must be within a specific video clip. The start time (timeRange.loc) and end time (timeRange.loc+timeRange.len) of the target video must fall within the time range of two specific video clips.

Pause download

API description

- (int)pausePlayBackDownloadWithResponse:(void (^)(int))callback;

Parameters

Parameter Type Description
callback Block The device response callback, 0 for success and any other value for failure.

Resume download

API description

- (int)resumePlayBackDownloadWithResponse:(void (^)(int))callback;

Parameters

Parameter Type Description
callback Block The device response callback, 0 for success and any other value for failure.

Stop video download

API description

- (int)stopPlayBackDownloadWithResponse:(void (^)(int errCode))callback;

Parameters

Parameter Type Description
callback Block The device response callback, 0 for success and any other value for failure.

Delete video

Some smart cameras allow users to delete videos from the memory card by date through the app. Currently, users can only delete full-day videos from a specific date.

Check support for video deletion

Check if the camera supports deleting videos from the memory card. This method should be invoked after P2P is connected.

- (BOOL)isSupportPlaybackDelete;

Delete video by date

API description

Delete videos from the memory card by date.

- (int)deletePlayBackDataWithDay:(NSString *)day onResponse:(void (^)(int errCode))callback onFinish:(void (^)(int errCode))finishedCallBack;

Parameters

Parameter Type Description
day NSString The target date, in the format yyyy-MM-dd.
callback Block The command sending callback. errCode, a negative number, indicates failure while any other value indicates success.
finishedCallBack Block The video deletion callback. errCode, a negative number, indicates failure while any other value indicates the videos for that day were deleted.

Example

Objective-C

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

- (void)startPlayback {
    if (self.connected) {
        [self.camera queryRecordTimeSliceWithYear:2020 month:2 day:12];
        return;
    }
    id p2pType = [self.deviceModel.skills objectForKey:@"p2pType"];
    [[ThingSmartRequest 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) {
        // Failed to get the configurations.
    }];
}

- (void)pausePlayback {
    [self.camera pausePlayback];
}

- (void)resumePlayback {
    [self.camera resumePlayback];
}

- (void)stopPlayback {
    [self.camera stopPlayback];
}

#pragma mark - ThingSmartCameraDelegate

- (void)cameraDidConnected:(id<ThingSmartCameraType>)camera {
    self.connected = YES;
      // Returns video clips stored on a certain date only after a P2P connection is created.
        [camera queryRecordTimeSliceWithYear:2020 month:2 day:12];
}

- (void)cameraDisconnected:(id<ThingSmartCameraType>)camera specificErrorCode:(NSInteger)errorCode {
    // A P2P connection is closed due to unstable network conditions.
    self.connected = NO;
    self.previewing = NO;
}


- (void)camera:(id<ThingSmartCameraType>)camera didReceiveTimeSliceQueryData:(NSArray<NSDictionary *> *)timeSlices {
      //  If no video clips are found, no playback occurs.
        if (timeSlices.count == 0) {
        return;
    }
      // Saves a list of video clips and starts playback from the first video clip.
    self.timeSlicesInCurrentDay = [timeSlices copy];
      self.timeSlicesIndex = 0;
    NSDictionary *timeSlice = timeSlices.firstObject;
    NSInteger startTime = [timeSlice[kThingSmartTimeSliceStartTime] integerValue];
    NSInteger stopTime = [timeSlice[kThingSmartTimeSliceStopTime] integerValue];
      // Starts playback from the first second of the first video clip.
    NSInteger playTime = startTime;
    [camera startPlayback:playTime startTime:startTime stopTime:stopTime];
}

- (void)camera:(id<ThingSmartCameraType>)camera thing_didReceiveVideoFrame:(CMSampleBufferRef)sampleBuffer frameInfo:(ThingSmartVideoFrameInfo)frameInfo {
    NSInteger index = self.timeSlicesIndex + 1;
      // Executes a callback if the next video clip does not exist.
    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 equal to or later than the end time of the current video clip, the next video clip will be played back.
    if (frameInfo.nTimeStamp >= stopTime) {
        NSDictionary *nextTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:index];
        NSInteger startTime = [nextTimeSlice[kThingSmartTimeSliceStartTime] integerValue];
            NSInteger stopTime = [nextTimeSlice[kThingSmartTimeSliceStopTime] integerValue];
            NSInteger playTime = startTime;
            [camera startPlayback:playTime startTime:startTime stopTime:stopTime];
    }
}

- (void)cameraDidBeginPlayback:(id<ThingSmartCameraType>)camera {
      // Starts playback of video footage.
      self.playbacking = YES;
    self.playbackPaused = NO;
    // Adds a rendered video view to a video screen.
        [self.view addSubview:camera.videoView];
}

- (void)cameraDidPausePlayback:(id<ThingSmartCameraType>)camera {
      // Playback of video footage is paused.
    self.playbackPaused = YES;
}

- (void)cameraDidResumePlayback:(id<ThingSmartCameraType>)camera {
       // Playback of video footage is resumed.
    self.playbackPaused = NO;
}

- (void)cameraDidStopPlayback:(id<ThingSmartCameraType>)camera {
      // Playback of video footage is stopped.
       self.playbacking = NO;
    self.playbackPaused = NO;
}

- (void)cameraPlaybackDidFinished:(id<ThingSmartCameraType>)camera {
      // Playback of video footage is finished.
    self.playbacking = NO;
    self.playbackPaused = NO;
}

// The failure callback.
- (void)camera:(id<ThingSmartCameraType>)camera didOccurredErrorAtStep:(TYCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode {
        if (errStepCode == Thing_ERROR_CONNECT_FAILED) {
          // Failed to create a P2P connection.
        self.connected = NO;
    }
    else if (errStepCode == Thing_ERROR_START_PLAYBACK_FAILED) {
          // Failed to play back video footage stored on the SD card.
        self.playbacking = NO;
            self.playbackPaused = NO;
    }
      else if (errStepCode == Thing_ERROR_PAUSE_PLAYBACK_FAILED) {
                // Failed to pause playback.
    }
    else if (errStepCode == Thing_ERROR_RESUME_PLAYBACK_FAILED) {
                // Failed to resume playback.
    }
}

Swift

func startPlayback() {
    if self.isConnected {
        self.camera.queryRecordTimeSlice(withYear: 2020, month: 2, day: 12)
        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
        // Failed to get the configurations.
    }
}

func pausePlayback() {
    self.camera.pausePlayback()
}

func resumePlayback() {
    self.camera.resumePlayback()
}

func stopPlayback() {
    self.camera.stopPlayback()
}

func cameraDidConnected(_ camera: ThingSmartCameraType!) {
    self.isConnected = true
    // Returns video clips stored on a certain date only after a P2P connection is created.
    camera.queryRecordTimeSlice(withYear: 2020, month: 2, day: 12)
}

func cameraDisconnected(_ camera: ThingSmartCameraType!, specificErrorCode: Int) {
    // A P2P connection is closed due to unstable network conditions.
    self.isConnected = false
    self.isPreviewing = false
}

func camera(_ camera: ThingSmartCameraType!, didReceiveTimeSliceQueryData timeSlices: [[AnyHashable : Any]]!) {
    //  If no video clips are found, no playback occurs.
    guard timeSlices.count > 0 else {
        return;
    }
    // Saves a list of video clips and starts playback from the first video clip.
    self.timeSlices = timeSlices
    self.timesliceIndex = 0
    let video = timeSlices.first!
    let startTime = video[kThingSmartTimeSliceStartTime] as! Int
    let stopTime = video[kThingSmartTimeSliceStopTime] as! Int
    // Starts playback from the first second of the first video clip.
    let playTime = startTime
    camera.startPlayback(playTime, startTime: startTime, stopTime: stopTime)
}

func camera(_ camera: ThingSmartCameraType!, thing_didReceiveVideoFrame sampleBuffer: CMSampleBuffer!, frameInfo: ThingSmartVideoFrameInfo) {
    let index = self.timesliceIndex + 1
    // Executes a callback if the next video clip does not exist.
    guard index < self.timeSlices.count else {
        return
    }
    let currentTimeSlice = timeSlices[self.timesliceIndex]
    let endTime = currentTimeSlice[kThingSmartTimeSliceStopTime] as! Int
    guard frameInfo.nTimeStamp >= endTime else {
        return
    }
    // If the timestamp of the current video frame is equal to or later than the end time of the current video clip, the next video clip will be played back.

    let nextTimeSlice = timeSlices.first!
    let startTime = nextTimeSlice[kThingSmartTimeSliceStartTime] as! Int
    let stopTime = nextTimeSlice[kThingSmartTimeSliceStopTime] as! Int
    let playTime = startTime
    camera.startPlayback(playTime, startTime: startTime, stopTime: stopTime)
}

func cameraDidBeginPlayback(_ camera: ThingSmartCameraType!) {
    // Starts playback of video footage.
    self.isPlaybacking = true
    self.isPlaybackPaused = false
    // Adds a rendered video view to a video screen.
    self.view.addSubview(camera.videoView())
}

func cameraDidPausePlayback(_ camera: ThingSmartCameraType!) {
    // Playback of video footage is paused.
    self.isPlaybackPaused = true
}

func cameraDidResumePlayback(_ camera: ThingSmartCameraType!) {
    // Playback of video footage is resumed.
    self.isPlaybackPaused = false
}

func cameraDidStopPlayback(_ camera: ThingSmartCameraType!) {
    // Playback of video footage is stopped.
    self.isPlaybacking = false
    self.isPlaybackPaused = false
}

func cameraPlaybackDidFinished(_ camera: ThingSmartCameraType!) {
    // Playback of video footage is finished.
    self.isPlaybacking = false
    self.isPlaybackPaused = false
}

func camera(_ camera: ThingSmartCameraType!, didOccurredErrorAtStep errStepCode: TYCameraErrorCode, specificErrorCode errorCode: Int) {
    if errStepCode == Thing_ERROR_CONNECT_FAILED  {
        // Failed to create a P2P connection.
        self.isConnected = false
    }else if errStepCode == Thing_ERROR_START_PLAYBACK_FAILED {
        // Failed to play back video footage stored on the SD card.
        self.isPlaybacking = false
        self.isPlaybackPaused = false
    }else if errStepCode == Thing_ERROR_PAUSE_PLAYBACK_FAILED {
        // Failed to pause playback.
    }else if errStepCode == Thing_ERROR_RESUME_PLAYBACK_FAILED {
        // Failed to resume playback.
    }
}