Last Updated on : 2024-08-15 01:34:46download
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.
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.
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
.
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. |
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. |
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.
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;
API description
- (void)pausePlayback;
Delegate method
The video playback is paused.
- (void)cameraDidPausePlayback:(id<ThingSmartCameraType>)camera;
API description
- (void)resumePlayback;
Delegate method
The video playback is resumed.
- (void)cameraDidResumePlayback:(id<ThingSmartCameraType>)camera;
API description
- (void)stopPlayback;
Delegate method
The video playback is stopped.
- (void)cameraDidStopPlayback:(id<ThingSmartCameraType>)camera;
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;
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.
Both pausePlayback
and stopPlayback
can stop video playback, with the difference being:
stopPlayback
, you cannot continue playing with resumePlayback
.startPlayback
to continue playing.startPlayback
to play another clip without needing to stop the current playback
first, whether it is ongoing or paused.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. |
Some smart cameras allow users to download video from the memory card to the mobile app.
Check if the camera supports downloading video from the memory card. This method should be invoked after P2P is connected.
- (BOOL)isSupportPlaybackDownload;
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.
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. |
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. |
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. |
Some smart cameras allow users to delete videos from the memory card by date or clip through the app SDK.
Check if the camera supports deleting videos from the memory card. This method should be invoked after P2P is connected.
- (BOOL)isSupportPlaybackDelete;
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. |
Before using this API, make sure that the camera supports the capability.
API description
Delete videos from the memory card by clip.
- (int)deletePlayBackDataWithFragments:(NSString *)json onResponse:(void (^)(int errCode))callback onFinish:(void (^)(int errCode))finishedCallBack;
Parameters
Parameter | Type | Description |
---|---|---|
json | NSString | The target clip, in the JSON format {"fragments":[{"startTime":"", "endTime":"" },{"startTime":"","endTime":""}]} . |
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 those clips were deleted. |
#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.
}
}
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.
}
}
Is this page helpful?
YesFeedbackIs this page helpful?
YesFeedback