1.1. Memory card video playback

Tuya smart camera support memory card recording. After the smart camera is inserted into the memory card, you can view the information and status of the memory card, and set the recording switch and mode. For details, please refer to Memory card management.

After the device saves the video recording in the memory card, it can play the video recording on the App side through the IPC SDK. Like the live video, it needs to connect to the p2p channel before starting playback. After the p2p channel is successfully connected, you can obtain the time information of the video clip recorded in the memory card on the device, and then play the video clip.

1.1.1. Video clip

The video clips saved in the memory card on the device can be as long as 10 minutes and as short as 10 seconds. The SDK supports viewing and playing video recordings on a day-by-day basis, and provides query on which days are saved with video recordings in a month, so that users can view them, and the query results are returned through the delegate method in TuyaSmartCameraDelegate.

Description

Query the days when video recordings are saved in a certain year and month.

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

Parameters

Parameter Description
year Year, such as: 2020
month Month, such as: 2

Description

Query all video clips in a certain month and day.

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

Parameters

Parameter Description
year Year, such as: 2020
month Month, such as: 2
day Day, such as: 22

Delegate Description

Callback for querying the date of video recording.

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

Parameters

Parameter Description
camera Camera object
days An array of days with video recordings, such as @[@(1), @(2)] indicates the current month of the query, there are video recordings on the 1st,2nd

Delegate Description

Callback when querying all video clips in a day.

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

Parameters

Parameter Description
camera Camera object
timeSlices Array of time information of video clips for the day, failure returns empty array

Video clip time data

The element type in timeSlices isNSDictionary.

Field (Constant defined in SDK) Type Description
kTuyaSmartPlaybackPeriodStartDate NSDate Video clip start date
kTuyaSmartPlaybackPeriodStopDate NSDate Video clip end date
kTuyaSmartPlaybackPeriodStartTime NSNumber Unix timestamp of video clip start time
kTuyaSmartPlaybackPeriodStopTime NSNumber Unix timestamp of video clip end time

1.1.2. Video playback

After successfully obtaining the video clips of a certain day, you can start playing the video. It is worth noting that if you stop playing the video recording and then play live video, or the p2p connection is disconnected, reconnect to the p2p channel, and then play the video recording again, you need to re-acquire the video clip of the day, otherwise it may appear exception.

Description

To start playing a certain video recording, the range of playTime is:[startTime, stopTime).

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

Parameters

Parameter Description
playTime In this video, the time point to start playing, using Unix timestamp
startTime Start time of this video, using Unix timestamp
stopTime End time of this video, using Unix timestamp

Description

Pause playback.

- (void)pausePlayback;

Description

Resume playback.

- (void)resumePlayback;

Description

Stop play.

- (void)stopPlayback;

Delegate Description

Video playback started successfully.

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

Delegate Description

Video playback paused.

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

Delegate Description

Video playback resumed.

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

Delegate Description

Video has stopped playing.

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

Delegate Description

Video playback finished,.

- (void)cameraPlaybackDidFinished:(id<TuyaSmartCameraType>)camera;

Continuous Play

Tuya smart camera has two recording modes, continuous recording and event recording. During continuous recording, the video recording will be a clip of 10 minutes, and all video clips are continuous, but if there is a video recording stop in the middle, there may be a gap between the video clips in the continuous recording mode. When recording events, the length of each video clip varies, and the interval between clips varies.

If the video recording segment of a certain day is continuous, then the next segment will be played automatically when the video is played. In other words, even if the start playback interface is called, the time of the first video clip of the day is passed in, and the video will be played until the last frame of the last video clip of the day, and the delegate method of the video playback finished will be called back.

If the video clip on a certain day is discontinuous (that is, there is a video clip B after a certain period of time after video clip A ends), the video is played to the disconnected position (that is, the last frame of video clip A is played), the video stream will automatically stop, and the SDK will not receive a callback when the video recording is over. In the latest Tuya smart camera firmware, when video clips are not continuous, each clip playback will call back the finished of video recording playback, so developers can play the next video after receiving the delegate method of the video playback finished. But if the device's firmware version is not the latest, the developer needs to use the time stamp in the frame information to determine whether the current frame is the last frame of the current video recording clip, determine whether the current video recording clip is playing finished, refer to Original video data.

Stop and Pause

Both pausePlayback and stopPlayback can achieve the purpose of stopping video playback. The difference between them is that after calling stopPlayback, resumePlayback cannot be called to resume playback. After you stop playing, if you want to continue the previous progress and start playing, you must save the time stamp of the last frame of video when you stop playing, and the time segment of the video recorded to play, and call the startPlayback method to resume playback.

In addition, after successfully obtaining the time data of the video recording segment, whether it is playing the recording video or pausing playback, you can directly call startPlayback to replay another video recording segment without first calling stopPlayback to stop playback.

1.1.3. Flow chart

视频直播流程图

Example

ObjC

#define kTuyaSmartIPCConfigAPI @"tuya.m.ipc.config.get"
#define kTuyaSmartIPCConfigAPIVersion @"2.0"

- (void)startPlayback {
    if (self.connected) {
        [self.camera queryRecordTimeSliceWithYear:2020 month:2 day:12];
        return;
    }
    id p2pType = [self.deviceModel.skills objectForKey:@"p2pType"];
    [[TuyaSmartRequest new] requestWithApiName:kTuyaSmartIPCConfigAPI postData:@{@"devId": self.devId} version:kTuyaSmartIPCConfigAPIVersion success:^(id result) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            TuyaSmartCameraConfig *config = [TuyaSmartCameraFactory ipcConfigWithUid:[TuyaSmartUser sharedInstance].uid localKey:self.deviceModel.localKey configData:result];
            self.camera = [TuyaSmartCameraFactory cameraWithP2PType:p2pType config:config delegate:self];
            [self.camera connect];
        });
    } failure:^(NSError *error) {
                // Failed to get configuration information
    }];
}

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

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

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

#pragma mark - TuyaSmartCameraDelegate

- (void)cameraDidConnected:(id<TuyaSmartCameraType>)camera {
    self.connected = YES;
        [camera queryRecordTimeSliceWithYear:2020 month:2 day:12];
}

- (void)cameraDisconnected:(id<TuyaSmartCameraType>)camera {
    self.connected = NO;
    self.playbacking = NO;
}

- (void)camera:(id<TuyaSmartCameraType>)camera didReceiveTimeSliceQueryData:(NSArray<NSDictionary *> *)timeSlices {
        if (timeSlices.count == 0) {
        return;
    }
      // start playback with first video clip
    self.timeSlicesInCurrentDay = [timeSlices copy];
      self.timeSlicesIndex = 0;
    NSDictionary *timeSlice = timeSlices.firstObject;
    NSInteger startTime = [timeSlice[kTuyaSmartTimeSliceStartTime] integerValue];
    NSInteger stopTime = [timeSlice[kTuyaSmartTimeSliceStopTime] integerValue];

    NSInteger playTime = startTime;
    [camera startPlayback:playTime startTime:startTime stopTime:stopTime];
}

- (void)camera:(id<TuyaSmartCameraType>)camera ty_didReceiveVideoFrame:(CMSampleBufferRef)sampleBuffer frameInfo:(TuyaSmartVideoFrameInfo)frameInfo {
    NSInteger index = self.timeSlicesIndex + 1;
    if (index >= self.timeSlicesInCurrentDay.count) {
        return;
    }
    NSDictionary *currentTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:self.timeSlicesIndex];
    NSInteger stopTime = [currentTimeSlice[kTuyaSmartTimeSliceStopTime] integerValue];
      // if timestamp of current frame is equal or greater than the end time of current video clip, play next video
    if (frameInfo.nTimeStamp >= stopTime) {
        NSDictionary *nextTimeSlice = [self.timeSlicesInCurrentDay objectAtIndex:index];
        NSInteger startTime = [nextTimeSlice[kTuyaSmartTimeSliceStartTime] integerValue];
            NSInteger stopTime = [nextTimeSlice[kTuyaSmartTimeSliceStopTime] integerValue];
            NSInteger playTime = startTime;
            [camera startPlayback:playTime startTime:startTime stopTime:stopTime];
    }
}

- (void)cameraDidBeginPlayback:(id<TuyaSmartCameraType>)camera {
      self.playbacking = YES;
    self.playbackPaused = NO;
        [self.view addSubview:camera.videoView];
}

- (void)cameraDidPausePlayback:(id<TuyaSmartCameraType>)camera {
    self.playbackPaused = YES;
}

- (void)cameraDidResumePlayback:(id<TuyaSmartCameraType>)camera {
    self.playbackPaused = NO;
}

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

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

- (void)camera:(id<TuyaSmartCameraType>)camera didOccurredErrorAtStep:(TYCameraErrorCode)errStepCode specificErrorCode:(NSInteger)errorCode {
        if (errStepCode == TY_ERROR_CONNECT_FAILED) {
          // p2p connect failed
        self.connected = NO;
    }
    else if (errStepCode == TY_ERROR_START_PLAYBACK_FAILED) {
          // start playback failed
        self.playbacking = NO;
            self.playbackPaused = NO;
    }
      else if (errStepCode == TY_ERROR_PAUSE_PLAYBACK_FAILED) {
                // pause playback failed
    }
    else if (errStepCode == TY_ERROR_RESUME_PLAYBACK_FAILED) {
                // resume playback failed
    }
}

Swift

func startPlayback() {
    if self.isConnected {
        self.camera.queryRecordTimeSlice(withYear: 2020, month: 2, day: 12)
        return
    }
    let p2pType = self.deviceModel.skills["p2pType"]!
    TuyaSmartRequest().request(withApiName: kTuyaSmartIPCConfigAPI, postData: ["devId": self.devId], version: kTuyaSmartIPCConfigAPIVersion, success: { result in
        guard let responder = result as? [AnyHashable:Any] else {
            return;
        }
        DispatchQueue.global().async {
            let config = TuyaSmartCameraFactory.ipcConfig(withUid: TuyaSmartUser.sharedInstance().uid, localKey: self.deviceModel.localKey, configData: responder)
            self.camera = TuyaSmartCameraFactory.camera(withP2PType: p2pType, config: config, delegate: self)
            self.camera.connect()
        }
    }) { _ in
                // Failed to get configuration information
    }
}

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

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

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

func cameraDidConnected(_ camera: TuyaSmartCameraType!) {
    self.isConnected = true
      // start playback with first video clip
    camera.queryRecordTimeSlice(withYear: 2020, month: 2, day: 12)
}

func cameraDisconnected(_ camera: TuyaSmartCameraType!) {
    self.isConnected = false
    self.isPlaybacking = false
}

func camera(_ camera: TuyaSmartCameraType!, didReceiveTimeSliceQueryData timeSlices: [[AnyHashable : Any]]!) {
    guard timeSlices.count > 0 else {
        return;
    }
      // start playback with first video clip
    self.timeSlices = timeSlices
    self.timesliceIndex = 0
    let video = timeSlices.first!
    let startTime = video[kTuyaSmartTimeSliceStartTime] as! Int
    let stopTime = video[kTuyaSmartTimeSliceStopTime] as! Int

    let playTime = startTime
    camera.startPlayback(playTime, startTime: startTime, stopTime: stopTime)
}

func camera(_ camera: TuyaSmartCameraType!, ty_didReceiveVideoFrame sampleBuffer: CMSampleBuffer!, frameInfo: TuyaSmartVideoFrameInfo) {
    let index = self.timesliceIndex + 1
    guard index < self.timeSlices.count else {
        return
    }
    let currentTimeSlice = timeSlices[self.timesliceIndex]
    let endTime = currentTimeSlice[kTuyaSmartTimeSliceStopTime] as! Int
    guard frameInfo.nTimeStamp >= endTime else {
        return
    }
      // if timestamp of current frame is equal or greater than the end time of current video clip, play next video
    let nextTimeSlice = timeSlices.first!
    let startTime = nextTimeSlice[kTuyaSmartTimeSliceStartTime] as! Int
    let stopTime = nextTimeSlice[kTuyaSmartTimeSliceStopTime] as! Int
    let playTime = startTime
    camera.startPlayback(playTime, startTime: startTime, stopTime: stopTime)
}

func cameraDidBeginPlayback(_ camera: TuyaSmartCameraType!) {
    self.isPlaybacking = true
    self.isPlaybackPaused = false
    self.view.addSubview(camera.videoView())
}

func cameraDidPausePlayback(_ camera: TuyaSmartCameraType!) {
    self.isPlaybackPaused = true
}

func cameraDidResumePlayback(_ camera: TuyaSmartCameraType!) {
    self.isPlaybackPaused = false
}

func cameraDidStopPlayback(_ camera: TuyaSmartCameraType!) {
    self.isPlaybacking = false
    self.isPlaybackPaused = false
}

func cameraPlaybackDidFinished(_ camera: TuyaSmartCameraType!) {
    self.isPlaybacking = false
    self.isPlaybackPaused = false
}

func camera(_ camera: TuyaSmartCameraType!, didOccurredErrorAtStep errStepCode: TYCameraErrorCode, specificErrorCode errorCode: Int) {
    if errStepCode == TY_ERROR_CONNECT_FAILED  {
          // p2p connect failed
        self.isConnected = false
    }else if errStepCode == TY_ERROR_START_PLAYBACK_FAILED {
          // start playback failed
        self.isPlaybacking = false
        self.isPlaybackPaused = false
    }else if errStepCode == TY_ERROR_PAUSE_PLAYBACK_FAILED {
                // pause playback failed
    }else if errStepCode == TY_ERROR_RESUME_PLAYBACK_FAILED {
                // resume playback failed
    }
}

During live video, if you want to switch to video playback mode, you do not need to disconnect the p2p connection and then reconnect to the p2p channel, but you need to stop the live video playback before you get the video recording of the day to start playing.

results matching ""

    No results matching ""