Jan 29, 2022
In this tutorial, you will learn how to use the DJIMediaManager
to interact with the file system on 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 UX 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.
See this Github Page for an Objective C version.
We use Mavic Pro 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.
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.
To learn how to implement this feature, please check this tutorial Application Activation and Aircraft Binding.
Implementing DJI Go Style Default Layout
Importing DJI SDK and UX SDK with CocoaPods
To create a new project in Xcode, choose Single View Application template for your project and press "Next", then enter "MediaManagerDemo" in the Product Name field and keep the other default settings. Once the project is created, import the DJI SDK and DJI UX SDK.
You can check the 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 the 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, do SDK registration, update UI and show alert views to inform users when a DJI product is connected or disconnected. Also, you can learn how to implement the shoot photo and record video features 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! We can move forward now.
Working on the UI of the Application
Now, create a new file, choose the "Swift file" template and name it as "MediaManagerViewController". We will use it to implement the Media Manager features.
Open the Main.storyboard file and drag and drop a new "View Controller" object from the Object Library and set its "Class" value as MediaManagerViewController.
Drag and drop a new "Container View" object in the MediaManagerViewController and set its ViewController's "Class" value as DUXFPVViewController, which contains a DUXFPVView
and will show the video playback.
Drag and drop a UIImageView object on top of the "Container View" and hide it by default. We will use it to show the downloaded photo.
Drag and drop eleven UIButton objects, one UITextField, one UITableView and a UIActivityIndicatorView and place them in the following positions:
The layout of the UI elements is a bit complicated, for more details of the configuration, please check the Main.storyboard in this tutorial's Github Sample Project.
Lastly, place a UIButton on the bottom right corner of the DefaultLayoutViewController view and create a segue to the MediaManagerViewController when the user presses the button.
If everything goes well, you should see the whole storyboard layout like this:
Once you finish the above steps, open the "DefaultLayoutViewController.swift" file and replace the content with the following:
import Foundation
import DJISDK
import DJIWidget
class MediaManagerViewController : UIViewController, DJICameraDelegate, DJIMediaManagerDelegate, UITableViewDelegate, UITableViewDataSource {
In the code above, we implement the following things:
We define the IBOutlet properties for the UI elements, like UIButton, UITableView, UITextField, etc.
We implement the
viewDidLoad
method, and invoke theinitData
method to disable thedeleteBtn
,cancelBtn
,reloadBtn
andeditBtn
initially.We implement the IBAction methods for all the UIButtons. For the
backBtnAction
method, we invoke thepopViewControllerAnimated
method of UINavigationController to go back to theDefaultLayoutViewController
.For the
editBtnAction
method, we makemediaTableView
enter editing mode by invokingsetEditing:animated:
, enabledeleteBtn
, enablecancelBtn
and disableeditBtn
.For the
cancelBtnAction
method we disable the editing mode ofmediaTableView
, enable theeditBtn
button, disabledeleteBtn
and disablecancelBtn
. We will implement the other IBAction methods later.
Switching to Media Download Mode
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 DJIMediaManager
.
Now, create a property of type DJIMediaManager
and implement the viewWillAppear:
and viewWillDisappear:
methods as shown below:
In the
viewWillAppear
method, we invoke thefetchCamera
method of DemoUtility class to fetch the DJICamera object. Then check if the camera is nil, if not set its delegate asMediaManagerViewController
, also initialize themediaManager
and set its delegate asMediaManagerViewController
. Furthermore, invoke DJICamera'ssetMode:withCompletion:
method with theDJICameraMode.shootPhoto
param to switch the camera mode to media download mode.Similarly, in the
viewWillDisappear
method, we also invoke DJICamera'ssetMode:withCompletion:
method withDJICameraModeShootPhoto
to switch the camera mode to shoot photo mode. Then reset DJICamera and DJIMediaManager's delegates so when the user enters the MediaManagerViewController, the DJICamera will switch to media download mode automatically and when the user exits back to the DefaultLayoutViewController, the DJICamera will switch to shoot photo mode.
Refreshing Media File List
Once we have finished the steps above, we can start to fetch the media files list from the Camera SD card and show them on the tableView.
Create the following properties and initialize them in the initData
method:
Next, create two new methods: loadMediaList
and updateMediaList:
and invoke the loadMediaList
method at the bottom of viewWillAppear:
method and in the reloadBtnAction:
IBAction method:
The code above implements:
In the
loadMediaList
method, we first show theloadingIndicator
and check thefileListState
of theDJIMediaManager
. If the state isDJIMediaFileListStateSyncing
orDJIMediaFileListStateDeleting
, we show a log to inform users that the media manager is busy. For other values, we invoke therefreshFileList(ofStorageLocation: withCompletion:)
method ofDJIMediaManager
to refresh the file list from the SD card. In the completion closure, if there is no error, we get a copy of the current file list by invoking thesdCardFileListSnapshot
method ofDJIMediaManager
and initialize themediaFileList
variable. Then invoke theupdateMediaList:
method and pass themediaFileList
. Finally, hide theloadingIndicator
since the operation of refreshing the file list has finished.In the
updateMediaList:
method, we remove all the objects in themediaList
array and add new objects to it from themediaList
array. Next, create amediaTaskScheduler
variable and set it to thetaskScheduler
property ofDJIMediaManager
. Then, setDJIFetchMediaTaskScheduler
'ssuspendAfterSingleFetchTaskFailure
property tofalse
to the of to prevent it from suspending the scheduler when an error occurs during the execution. Moreover, invoke theresumeWithCompletion
method ofDJIFetchMediaTaskScheduler
to resume the scheduler, which will execute tasks in the queue sequentially.Create a for loop to loop through all theDJIMediaFile
variables in themediaList
array and invoke thetaskWithFile:content:andCompletion:
method ofDJIFetchMediaTaskScheduler
by passing thefile
variable andDJIFetchMediaTaskContentThumbnail
value to ask the scheduler to download the thumbnail of the media file.In the completion block, we invoke thereloadData
method ofUITableView
to reload everything in the table view. After that, invoke themoveTaskToEnd
method to push thetask
to the back of the queue and be executed after the executing task is complete.
Lastly, we enable the reloadBtn
and editBtn
buttons.
Once you finish the steps above, you should implement the following UITableView methods:
In the code above, we implement the following features:
Return
1
as the section number of the table view.Return the
count
value of themediaList
array as the number of rows in section.If the
UITableViewCell
is selected, set itsaccessoryType
asUITableViewCellAccessoryCheckmark
to show a checkmark on the right side of the table view cell, otherwise, set theaccessoryType
asUITableViewCellAccessoryNone
to hide the checkmark.
Next, get the DJIMediaFile
object in the self.mediaList
array by using the indexPath.row
index. Lastly, update the textLabel
, detailTextLabel
and imageView
properties of table view cell according to the DJIMediaFile
object. You can get the "dji.png" file from the tutorial's Github Sample project.
Add Helper files from Demo
Add the following files from the tutorial's final product to your project(TODO: link)
VideoPreviewerAdapter.swift
DecodeImageCalibrateLogic.swift
DJIScrollView.swift
DJIScrollView.xib
AlertView.swift
Now, to build and run the project, connect the demo application to a supported DJI product (Please check the Run Application for more details) and enter the MediaManagerViewController
, you should be able to see something similar to the following screenshot:
Downloading and Editing the Media Files
After showing all the media files in the table view, we can start to implement the features of downloading and deleting media files.
Now, continue to create the following properties:
Next, initialize the properties in the initData
method:
Moreover, implement the table View delegate method as shown below:
In the code above, we assign the selectedCellIndexPath
property to the indexPath
value. Then get the current selected currentMedia
object from the mediaList
array using the indexPath
param of this method. Reset the downloaded file data and data offset value(for tracking file download progress) if the currentMedia
object isn't the same as self.selectedMedia
property.
Once you finish the steps above, we continue to implement the downloadBtnAction:
method as shown below:
In the code above, we implement the following features:
We first create a variable
isPhoto
and assign its value by checking themediaType
of the selectedDJIMediaFile
. For more details on theDJIMediaType
enum, please check "DJIMediaFile.h".Next, if the
statusAlertView
is nil, we initialize it by invokingDJIAlertView
'sshowAlertViewWithMessage:titles:action:
method. Here we create an alertView with one button named "Cancel". If user presses the "Cancel" button of the alertView, we invokeDJIMediaFile
'sstopFetchingFileDataWithCompletion:
method to stop the fetch file task.Additionally, invoke
DJIMediaFile
'sfetchFileDataWithOffset:updateQueue:updateBlock:
method to fetch the media file's full resolution data from the SD card. The full resolution data could be an image or video. Inside the completion closure, if there is an error, update the message of thestatusAlertView
to inform users and dismiss the alert view after 2 seconds. If there is no error and the media file is a photo, initialize thefileData
property with the received data or appenddata
to it if already initialized. Only photo saving has been implemented here.
Next, accumulate the value of the previousOffset
property by adding the length of the data
param. Calculate the percentage of the current download progress and assign the value to the progress
variable. Also, update the message of statusAlertView
to inform users of the download progress. Furthermore, check if the download has completed and dismiss the alert view.
Lastly, if the media file is a photo, invoke the showPhotoWithData:
and savePhotoWithData:
methods to show the full resolution photo and save it to the iOS Photo Library.
You can check the implementations of the showPhotoWithData:
and savePhotoWithData:
methods below:
In the code above, we implement the following features:
In the
showPhotoWithData:
method, if thedata
is not nil, create aUIImage
object from it. Then check if the createdimage
is not nil and show it in thedisplayImageView
.Similarly, in the
savePhotoWithData:
method, we create aUIImage
object from thedata
param and invoke theUIImageWriteToSavedPhotosAlbum()
method to save the image to the photos album.In imageDidFinishSaving(error:), we first create a
String
object and set its value by checking for an error. Next, show thestatusAlertView
to inform the users of the message and dismiss the alert view when the users press on the Dismiss button.
Once you have finished the steps above, we can continue to implement the feature of deleting media files. Here we should implement the delegate methods of UITableView as shown below:
The code above implements:
In the
tableView:canEditRowAtIndexPath:
method, returntrue
to allow the user to delete the table view cell with a swipe gesture.In the
tableView:commitEditingStyle:forRowAtIndexPath:
method, we get thecurrentMedia
object from themediaList
array. Next, invoke thedeleteFiles:withCompletion:
method ofDJIMediaManager
to delete the selected media file. Inside the completion block, if there is an error, show an alert view to inform user of the error description. If not, remove the deleted media file from themediaList
array and invoke thedeleteRowsAtIndexPaths:withRowAnimation:
method ofmediaTableView
to remove the table view cell too.
Now build and run the project, connect the demo application to a Mavic Pro and enter the MediaManagerViewController
, try to download an image file from the SD card, display and save it to the photos album. Also, try to swipe right on the table view cell and delete the media file from the table view. If everything goes well, you should be able to see something similar to the following gif animation:
Working on the Video Playback
After you finish the steps above, you should know how to download and display the image media file using DJIMediaManager
. Now let's continue to implement the Video Playback features.
Now, implement the following IBAction methods:
In the code above, we implement the following features:
In the
playBtnAction:
method, we hide thedisplayImageView
image view. Then check theselectedMedia
'smediaType
object to see if the selected media file is a video. Set theplaceholder
string of thepositionTextField
to the video duration and invoke theplayVideo:withCompletion:
method ofDJIMediaManager
to start playing the video.In the
resumeBtnAction:
method, we invoke theresumeWithCompletion:
method ofDJIMediaManager
to resume the paused video.In the
pauseBtnAction:
method, we invoke thepauseWithCompletion:
method ofDJIMediaManager
to pause the playing video.In the
stopBtnAction:
method, we call thestopWithCompletion:
method ofDJIMediaManager
to stop the playing video.In the
moveToPositionAction:
method, we get the text value of thepositionTextField
and convert it to an NSUInteger valuesecond
. Then invoke themoveToPosition:withCompletion:
method ofDJIMediaManager
to skip to the input position in seconds from the start of the video. Inside the completion block, we clean up the text content of thepositionTextField
.
Lastly, we can show the video playback state info by implementing the following methods:
In the code above, we implement the following features:
At the bottom of the
initData
method, we initializestatusView
and hide it. For more details of theDJIScrollView
, please check the "DJIScrollView.swift" file in the tutorial's Github Sample project.In the
showStatusBtnAction:
method, show thestatusView
when the users press the Status button.Implement the delegate method of
DJIMediaManagerDelegate
. We create astateStr
NSMutableString variable and append different string values to it. LikefileName
,durationInSeconds
andvideoOrientation
of theDJIMediaFile
, for more details, please check the "DJIMediaFile" class. Lastly, invoke thewriteStatus
method ofDJIScrollView
to show thestateStr
NSMutableString in thestatusTextView
ofDJIScrollView
.In the
statusToString:
andorientationToString:
methods, return specific String values according to the values of theDJIMediaVideoPlaybackStatus
andDJICameraOrientation
enums.
Congratulations! You have finished all the features of this demo. Now build and run the project, connect the demo application to a Mavic Pro and enter the MediaManagerViewController
, try to play with the Video Playback features. If everything goes well, you should be able to see something similar to the following gif animation:
Summary
In this tutorial, you have learned how to use DJIMediaManager
to preview photos, play videos, download or delete files, you also learn how to get and show the video playback status info. By using the DJIMediaManager
, the users can get the metadata for all the multimedia files, and has access to each individual multimedia file. Hope you enjoy it!