If you come across any mistakes or bugs in this tutorial, please let us know by sending emails to dev@dji.com. Please feel free to send us Github pull request and help us fix any issues.
In this tutorial, you will learn how to use DJI Mobile SDK to access the media resources in the SD card of the aircraft's camera. By the end of this tutorial you will have an app that you can use to preview photos, play videos, download or delete files and so on.
In order for our app to manage photos and videos, however, it must first be able to take and record them. Fortunately, by using DJI iOS SDK SDK, you can implement shooting photos and recording videos functionalities easily with standard DJI Go UIs.
You can download the tutorial's final sample project from this Github Page.
We use Phantom 4 and iPad Air as an example to make this demo. For more details of customizing the layouts for iPhone devices, please check the tutorial's Github Sample Project. Let's get started!
Application Activation and Aircraft Binding in China
For DJI SDK mobile application used in China, it's required to activate the application and bind the aircraft to the user's DJI account.
If an application is not activated, the aircraft not bound (if required), or a legacy version of the SDK (< 4.1) is being used, all camera live streams will be disabled, and flight will be limited to a zone of 100m diameter and 30m height to ensure the aircraft stays within line of sight.
Now, let's create a new project in Xcode, choose Single View Application template for your project and press "Next", then enter "PlaybackDemo" in the Product Name field and keep the other default settings. Once the project is created, let's import the DJI SDK and DJI UX SDK.
You can check Getting Started with DJI UX SDK tutorial to learn how to import the DJISDK.framework and DJIUXSDK.framework into your Xcode project.
Importing the DJIWidget
You can check Creating a Camera Application tutorial to learn how to download and import the DJIWidget into your Xcode project.
Working on the MainViewController and DefaultlayoutViewController
You can check this tutorial's Github Sample Code to learn how to implement the MainViewController to do SDK registration and update UIs and show alert views to inform users when DJI product is connected and disconnected. Also, you can learn how to implement shooting photos and recording videos functionalities with standard DJI Go UIs by using DUXDefaultLayoutViewcontroller of DJI UX SDK from the Getting Started with DJI UX SDK tutorial.
If everything goes well, you can see the live video feed and test the shoot photo and record video features like this:
Congratulations! Let's move forward.
Implementing Playback Features
In order to preview, edit or download the photos or videos files from the DJICamera, you need to use the DJIPlaybackManager or DJIMediaManager of DJICamera. Here, we use DJIPlaybackManager to demonstrate how to implement it.
Switching to Playback Mode
Now, let's create a new file, choose the "Cocoa Touch Class" template and choose UIViewController as its subclass, name it as "PlaybackViewController". We will use it to implement the camera playback features.
Next, open the Main.storyboard file and drag and drop a new "View Controller" object from the Object Library and set its "Class" value as PlaybackViewController. Moreover, drag and drop a new "Container View" object in the PlaybackViewController and set its ViewController's "Class" value as DUXFPVViewController, which contains a DUXFPVView and will show the live video feed directly. Furthermore, drag and drop a UIButton on the upper left corner and edit its text to "Back".
Lastly, let's drag and place a UIButton on the bottom right corner of the DefaultLayoutViewController view and create a segue to show the PlaybackViewController when the user press the button.
If everything goes well, you should see the storyboard layout like this:
Once you finish the above steps, let's open the "DefaultLayoutViewController.m" file and replace the content with the followings:
In the code above, we create an IBOutlet property for the playbackBtn and set its image in the viewDidLoad method. You can get the "playback_icon_iPad.png" file from this tutorial's Github Sample Project.
Next, open the "PlaybackViewController.m" file and replace the content with the followings:
In the code above, we implement the following things:
In the viewWillAppear method, we firstly invoke the fetchCamera method of DemoUtility class to fetch the DJICamera object. Then invoke the setMode:withCompletion: method of DJICamera and pass the DJICameraModePlayback param to switch the camera mode to playback.
Similarly, in the viewWillDisappear method, we also invoke the setMode:withCompletion: method of DJICamera and pass the DJICameraModeShootPhoto param to switch the camera mode to shoot photo mode.
So when the user enter the PlaybackViewController, the DJICamera will switch to playback mode automatically, when user exit back to the DefaultLayoutViewController, the DJICamera will switch to shoot photo mode.
Previewing Single Files
Since we can switch to the Playback mode now, let's add two UISwipeGestureRecognizers to preview the previous and the next media files in the SD Card.
Open the PlaybackViewController.m file, implement the DJICameraDelegate and DJIPlaybackDelegate protocols and create two properties of UISwipeGestureRecognizer and name them swipeLeftGesture and swipeRightGesture in the class extension. Then initialize them in the initData method as follows:
The above code uses the goToNextSinglePreviewPage and goToPreviousSinglePreviewPage methods of DJICamera's playbackManager to preview the next and previous files. Since there are two types of the media files in the SD Card, Photo and Video, we'll have to implement video playback feature as well.
Open the Main.storyboard, drag a UIView object and position it on top of the viewController, then drag a UIButton to the view you just added as a subview and named "Stop". Next, drag a UIButton object to the center of the viewController, set its image as "playVideo"(You can get this image file from the project source code, in the Images.xcassets folder).
Here we hide the Stop and the playVideo buttons. Now let's go to PlaybackViewController.m file and create IBOutlets and IBActions for the newly added UIs:
Moreover, before implementing the IBAction methods, we'll add two new properties of the DJICameraSystemState class and the DJICameraPlaybackState class and named them as cameraSystemState and cameraPlaybackState respectively in the class extension as shown below:
These properties are used to save the current camera system state and the playback state. Now let's set the delegates of the DJICamera and DJIPlaybackManager at the bottoms of viewWillAppear and viewWillDisappear methods:
Next update the cameraSystemState property value and hide the playbackBtnsView based on DJICameraSystemState's mode in the - (void)camera:(DJICamera *)camera didUpdateSystemState:(DJICameraSystemState *)systemState delegate method:
-(void) camera:(DJICamera*)camera didUpdateSystemState:(DJICameraSystemState*)systemState { self.cameraSystemState = systemState; //Update camera system state
- (void)updateUIWithPlaybackState:(DJICameraPlaybackState *)playbackState { if (playbackState.playbackMode == DJICameraPlaybackModeSingleFilePreview) { if (playbackState.fileType == DJICameraPlaybackFileTypeJPEG || playbackState.fileType == DJICameraPlaybackFileTypeRAWDNG) { //Photo Type if (!self.playVideoBtn.hidden) { [self.playVideoBtn setHidden:YES]; } }elseif (playbackState.fileType == DJICameraPlaybackFileTypeVIDEO) //Video Type { if (self.playVideoBtn.hidden) { [self.playVideoBtn setHidden:NO]; } } }elseif (playbackState.playbackMode == DJICameraPlaybackModeSingleVideoPlaybackStart) { //Playing Video [self.playVideoBtn setHidden:YES]; }elseif (playbackState.playbackMode == DJICameraPlaybackModeMultipleFilesPreview){ [self.playVideoBtn setHidden:YES]; } }
As you can see, we have updated the cameraPlaybackState property's value in the - (void)playbackManager:(DJIPlaybackManager *)playbackManager didUpdatePlaybackState:(DJICameraPlaybackState *)playbackState delegate method, and have also updated the playVideoBtn's hidden state based on the DJICameraSystemState's mode and the DJICameraPlaybackState's playbackMode.
Finally, we can implement the IBAction methods as follows:
In the playVideoBtnAction and stopVideoBtnAction methods, we check if the fileType is video, then call the playVideo and the stopVideo methods of the DJICamera's playbackManager to start and stop playing the video. For more details of the implementations, please check the tutorial's Github Sample Project.
Once it's done, build and run the project. Try swiping left and right in the PlaybackViewController to navigate through your photos and videos. If you see the play button at the center of the screen, press it to play the video. You can check the following gif animation to get the idea of how to use it:
Previewing Multiple Files
Before we move forward, let's explain the Playback mode. There are multiple playback modes in the camera, and we can check the DJICameraPlaybackMode enum type in the DJICameraPlaybackState.h file as follows:
/** * A playback mode represents a task that the Playback manager is executing. */ typedefNS_ENUM (uint8_t, DJICameraPlaybackMode){ /** * Single file preview mode. */ DJICameraPlaybackModeSingleFilePreview = 0, /** * Single video playback start. */ DJICameraPlaybackModeSingleVideoPlaybackStart = 2, /** * Single video playback pause. */ DJICameraPlaybackModeSingleVideoPlaybackPause = 3, /** * Multiple file edit. */ DJICameraPlaybackModeMultipleFilesEdit = 4, /** * Multiple media file preview. */ DJICameraPlaybackModeMultipleFilesPreview = 5, /** * Download media files. */ DJICameraPlaybackModeDownload = 6, /** * Unknown playback mode. */ DJICameraPlaybackModeUnknown = 0xFF, };
As shown in the code above, we can preview files in two ways: Single Preview and Multiple Preview. We can also play videos, delete photos and videos and even download them.
We will learn how to preview multiple files here. Here is what Multiple Preview looks like:
You can preview at most eight files at the same time. Since the preview images are shown in the fpvPreviewView, you cannot interact with them yet. Let's add buttons and swipe gestures to interact with them.
First, we will create a new file named "DJIPlaybackMultiSelectViewController", which will be a subclass of UIViewController. Make sure the check box for Also create XIB file is selected when creating the file. Then open the "DJIPlaybackMultiSelectViewController.xib" file and, under the Size dropdown in the Simulated Metrics section, set its size to Freeform . In the view section, change the width to "1024" and height to "768". Take a look at the changes made below:
Then drag a UIView object to the viewController as subview and set its name to "Buttons View". Next set its frame as follows:
Moreover, drag eight UIButton objects to the "Buttons View" as subviews and position them as follows(You can check the demo project's DJIPlaybackMultiSelectViewController_iPad.xib file to get the details on how to setup these buttons's frame):
These buttons represent eight media files when you are in the Multiple Preview Mode. Pressing any of these buttons will enter Single Preview Mode.
Now let's open the DJIPlaybackMultiSelectViewController.h file and create two block properties as follows:
The first block is used to check the selected button action with index, the second one is used to check the swipe gesture action.
Then go to "DJIPlaybackMultiSelectViewController.m" file and create four UISwipeGestureRecognizer properties to represent the left, right, up and down swipe gestures. Additionally, create eight IBAction methods and link them to the UIButton objects in the "DJIPlaybackMultiSelectViewController.xib" file:
#pragma mark UIGestureAction Methods - (void)swipeLeftGestureAction:(UISwipeGestureRecognizer *)gesture { if (self.swipeGestureAction) { self.swipeGestureAction(UISwipeGestureRecognizerDirectionLeft); } }
- (void)swipeRightGestureAction:(UISwipeGestureRecognizer *)gesture { if (self.swipeGestureAction) { self.swipeGestureAction(UISwipeGestureRecognizerDirectionRight); } }
- (void)swipeUpGestureAction:(UISwipeGestureRecognizer *)gesture { if (self.swipeGestureAction) { self.swipeGestureAction(UISwipeGestureRecognizerDirectionUp); } }
- (void)swipeDownGestureAction:(UISwipeGestureRecognizer *)gesture { if (self.swipeGestureAction) { self.swipeGestureAction(UISwipeGestureRecognizerDirectionDown); } }
These four swipe gestures are for single and multiple files preview. Swipe left or right to preview files in Single Preview Mode, swipe up or down to preview files in Multiple Preview Mode. We invoke the swipeGestureAction block inside the swipe action method with a UISwipeGestureRecognizerDirection value.
Next, implement the IBAction methods for the eight UIButtons as follows:
#pragma mark UIButton Action Methods - (IBAction)selectFirstItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(0); } }
- (IBAction)selectSecondItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(1); } }
- (IBAction)selectThirdItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(2); } }
- (IBAction)selectFourthItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(3); } }
- (IBAction)selectFifthItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(4); } }
- (IBAction)selectSixthItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(5); } }
- (IBAction)selectSeventhItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(6); } }
- (IBAction)selectEighthItemBtnAction:(id)sender { if (self.selectItemBtnAction) { self.selectItemBtnAction(7); } }
We invoke the selectItemBtnAction block inside the IBAction methods with related button index. The index starts from 0 here because the file index counted in Playback Multiple Preview Mode starts from 0.
Now, go back to "PlaybackViewController.m" file. Since we have added the swipe left and swipe right gestures in the "DJIPlaybackMultiSelectViewController.m" file, let's delete the swipeLeftGesture and swipeRightGesture properties and their related codes in the "PlaybackViewController.m" file to refactor the code structure.
Then import the "DJIPlaybackMultiSelectViewController.h" header file and create a property of DJIPlaybackMultiSelectViewController named playbackMultiSelectVC. Next, we create a new method named initPlaybackMultiSelectVC and implement it in the viewDidLoad method:
So in the initPlaybackMultiSelectVC method, we init the playbackMultiSelectVC property first, and then we invoke the selectItemBtnAction block's setter method and implement the toggleFileSelectionAtIndex method of the DJICamera's playbackManager with selected index. This way, we can switch to Single Preview Mode from Multiple Preview Mode.
Furthermore, we invoke the swipeGestureAction block's setter method and implement the preview files feature based on the UISwipeGestureRecognizerDirection value.
Once this is done, go to Main.storyboard and drag a UIButton object to the playbackBtnsView as subView, naming it as Multi Pre and positioning it as follows:
Finally, create an IBAction method named multiPreviewButtonClicked and link it to the above UIButton in the Main.storyboard. Implement the method as shown below to enter Multiple Preview Mode:
Let's build and run the project and try to enter Multiple Preview Mode. Use the swipe up and down gestures to preview files. Switch to the Single Preview Mode by pressing any of the eight preview images. Here is a screenshot:
Deleting Photos and Videos
You can now preview photos and videos in Single Preview Mode and Multiple Preview Mode. But what if you want to delete a file you don't like? Let's implement the delete files feature!
Go to Main.storyboard and drag three UIButtons to the playbackBtnsView as subviews and named them Select, Select All and Delete. We hide the selectBtn and selectAllBtn buttons here. Then go to the PlaybackViewController.m file and create two IBOutlets for the "Select" and "Select All" buttons, and also the three IBAction methods for the three buttons as follows:
The above code implements the selectButtonAction method to enter and exit MultipleEditMode by calling the exitMultipleEditMode and enterMultipleEditMode methods of DJICamera's playbackManager. Then in selectAllBtnAction IBAction method, we use an if statement to check if all the files in the page are selected and invoke the selectAllFilesInPage and unselectAllFilesInPage methods of DJICamera's playbackManager.
Moreover, update the selectBtn and selectAllBtn buttons' hidden values in the following method:
- (void)updateUIWithPlaybackState:(DJICameraPlaybackState *)playbackState { if (playbackState.playbackMode == DJICameraPlaybackModeSingleFilePreview) {
Before implementing the deleteButtonAction method, let's create two new properties in the class extension as follows:
@property (strong, nonatomic) UIAlertView* statusAlertView; @property (assign, nonatomic) int selectedFileCount;
Here, we create an int property named selectedFileCount to count the number of files currently selected in the Multiple Preview Mode. We also create a UIAlertView property named as statusAlertView to show alerts when deleting files.
Create the following three methods to show, dismiss and update the alertView:
WeakRef(target); [self showAlertViewWithTitle:@"Delete The Current File?" message:nil okActionHandler:^(UIAlertAction *action) { WeakReturn(target); DJICamera* camera = [DemoUtility fetchCamera]; [camera.playbackManager deleteCurrentPreviewFile]; [target.selectBtn setTitle:@"Select" forState:UIControlStateNormal];
} cancelActionhandler:nil];
}
}
The deleteButtonAction action method updates the selectedFileCount property value with cameraPlaybackState's selectedFileCount value. It then checks the playbackMode value of cameraPlaybackState to show alertViews in the DJICameraPlaybackModeMultipleFilesEdit and DJICameraPlaybackModeSingleFilePreview mode, then in the action handler, we invoke the deleteAllSelectedFiles and deleteCurrentPreviewFile methods of DJICamera's playbackManager to delete files and update selectBtn's title.
Build and run the project, and try the select multiple files, delete single and multiple files features. Here's what it should look like:
Deleting a Single File:
Deleting Multiple Files:
Downloading And Saving Photos
1. Downloading Photos
Let's implement the download photo feature now. First, go to the Main.storyboard file and drag a UIButton object to the playbackBtnsView and name it "Download". Then position it on the right of "Delete" button.
Next, go to PlaybackViewController.m file and create the following property objects and IBAction method in the class extension:
Before moving forward, we need to first explain the following method in DJIPlaybackManager class:
/** * Downloads the currently selected media files. * Precondition: * The camera must enter multiple preview mode. * * @param prepareBlock Callback to prepare each file for download. * @param dataBlock Callback while a file is downloading. The dataBlock can be called multiple times for a file. The error argument in <code>DJIFileDownloadingBlock</code> is not used and should be ignored. * @param fileCompletionBlock Callback after each file have been downloaded. * @param finishBlock Callback after the downloading is finished. */ - (void)downloadSelectedFilesWithPreparation:(nullable DJIFileDownloadPreparingBlock)prepareBlock process:(nullable DJIFileDownloadingBlock)dataBlock fileCompletion:(nullable DJIFileDownloadCompletionBlock)fileCompletionBlock overallCompletion:(nullable DJICompletionBlock)finishBlock;
This method has three params, the first param prepareBlock is a file download preparing block. You can do some download initialization work here like showing an alertView to clarify the download file's file name, file size, etc. The second param dataBlock is a download data update block, you can append the downloaded data here and increase the downloaded size data. The third param fileCompletionBlock is a file download completion block, you can save the currently downloaded image to the Photo Album here. The last param finishBlock is a download completion block.
Important: We cannot update the download file status UI in the dataBlock block, since it will slow down the file download speed. So we should use the downloadedImageData property to append downloaded data and use the updateImageDownloadTimer to update the UI.
So let's create three new methods here to set up the updateImageDownloadTimer:
- (void)updateDownloadProgress:(NSTimer *)updatedTimer { if (self.downloadImageError) {
As you can see, we use the startUpdateTimer and stopTimer methods to start and stop the updateImageDownloadTimer. Then we implement the updateDownloadProgress selector method to update the statusAlertView's title and message value.
Next, create a new method name resetDownloadData to reset all the download related property values:
WeakRef(target); [self showAlertViewWithTitle:@"Download The Current File?" message:nil okActionHandler:^(UIAlertAction *action) { WeakReturn(target); [target downloadFiles]; } cancelActionhandler:nil]; }
}
In downloadButtonAction method, we update the statusAlertView's title and message. And create two new UIAlertControllers to ask users for permission to download files based on the cameraPlaybackState's playbackMode value. Also, in the action handler method, we invoke the downloadFiles method once the OK button of the alertView is pressed.
Lastly, implement the downloadFiles method as shown below:
In this method, we call the resetDownloadData method to reset data first. We check if the playbackMode is DJICameraPlaybackModeSingleFilePreview and update the selectedFileCount variable's value. Then we call the following method of the DJICamera's playbackManager:
In the first block prepareBlock, we call the startUpdateTimer method to start updateImageDownloadTimer. Then, we update the totalFileSize and targetFileName variables. Next, we show statusAlertView and update its title and message with the download image info.
In the second block dataBlock, we append the downloadedImageData with the downloaded image data and update the currentDownloadSize and downloadImageError variables' values.
In the third block completion, we increase the downloadedFileCount variable. We then create an UIImage object with downloadedImageData. Next, we reset downloadedImageData's data and currentDownloadSize's value. Moreover, we update statusAlertView with the image download info.
2. Saving Downloaded Photos to Photo Album
Now, we have implemented the download photos features, but if we want to save the downloaded photos to the iOS Photo Album?
To do this, we will create a new property of NSMutableArray class and name it "downloadedImageArray" and initialize it in the initData method, also resetting it in the resetDownloadData method as follows:
if (error != NULL) { // Show message when image saved failed [self updateStatusAlertContentWithTitle:@"Save Image Failed!" message:[NSString stringWithFormat:@"%@", error] shouldDismissAfterDelay:NO]; } else { // Show message when image successfully saved if (self.downloadedImageArray) { [self saveDownloadImage];
if (self.downloadedImageArray.count == 0) { [self showStatusAlertView]; [self updateStatusAlertContentWithTitle:@"Stored to Photos Album" message:@"" shouldDismissAfterDelay:YES]; }
} }
}
In the saveDownloadImage method, we check if the downloadedImageArray is empty and get its last UIImage, assigning it to the image variable. Then we use the UIImageWriteToSavedPhotosAlbum() method to save the image to the Photo Album and remove the last object from the downloadedImageArray.
Next, in the selector method, we check if an error has occurred and invoke the saveDownloadImage method until the downloadedImageArray is empty. At the same time, we update the statusAlertView with related titles and messages.
At the end, add the downloaded image object to downloadedImageArray, and call the stopTimer method and the saveDownloadImage method in the fileCompletionBlock block of the downloadFiles method:
Let's build and run the project. Try to download photos in Single Preview Mode and Multiple Preview Mode. Once it's finished, go to the Photo Album to check if the downloaded photos exist:
Selecting files and downloading them:
Download completion and photos being saved to the Photo Album:
Summary
In this tutorial, you have learned how to use DJI iOS SDK to preview photos and videos in Single Preview Mode and Multiple Preview Mode, how to enter multiple edit mode and select files for deleting. You also learned how to download and save photos to the iOS Photo Album. Hope you enjoy it!