用大疆无人机拍摄完后,怎么少得了去查看一下自己的作品是否满意呢?这时候一个文件回看和管理的功能就至关重要了。本文主要就开发 DJIFileManger
中所涉及 DJI-SDK
的相关 API 进行简要的说明和分析。
1. 操作流程概览
首先我们要明确做一个文件回看和管理的功能有什么基本要素:
缩略图
预览图
查看文件各种属性值
下载原图或者原视频
删除
明确了这几点后,我们就可以去 DJI-SDK API 文档 中查找相关的 API 了,也可以通过官方的一个 Demo 来窥探部分相关 API 的操作。这里我们根据相关的 API 可以得出这样的一个操作流程:
下面我会根据这个基本的流程来进行讲解。
2. 文件操作前置准备
2-1. 相机模式切换及文件存储路径
我们需要把相机的模式切换为 MediaDownload
,在此模式下我们才可以进行文件相关的操作。
// 获取相机
let camera = DJISDKManager.product()?.camera
// 切换相机模式
camera?.setMode(.mediaDownload) { error in
// handle error
}
接着我们需要获取文件存储的路径,这里文件的存储路径有两种:SD卡和飞机内部存储。这里获取存储路径用到的 API 是 DJICamera
下的这个方法:
getStorageLocation(completion: @escaping (DJICameraStorageLocation, Error?) -> Void)
它会返回一个 DJICameraStorageLocation
的枚举。接下来我们就可以利用这个存储路径去刷新文件列表,以及获取文件列表的快照了。
2-2. 刷新文件列表及获取文件列表快照
首先我们需要刷新在存储空间上的文件,以获得最新的文件列表,这里用到的 API 是 DJIMediaManager
下的这个方法:
func refreshFileList(of storageLocation: DJICameraStorageLocation, withCompletion completion: DJICompletionBlock? = nil)
该方法所需要的一个参数 storageLocation
就是上一节获取的存储路径。在这个方法的回调中,即刷新列表成功后,我们就可以获取到文件列表的快照(FileListSnapshot)了,注意应该根据文件存储位置的不同而选择调用 DJIMediaManager
的下面这两个方法:
sdCardFileListSnapshot()
internalStoragefileListSnapshot()
这两个方法从命名上可以看出返回的是一个文件列表的快照(FileListSnapshot),而实际上它们返回的都是一个 DJIMediaFile
的数组, DJIMediaFile
这个类定义了文件相关的各种信息,有了 DJIMediaFile
我们就可以获取文件的缩略图、预览图还有原图等等数据了。
3. 文件操作
3-1. 获取缩略图和预览图
对于相机拍摄的每一个文件, DJISDK
提供了四种类型数据:
缩略图 thumbnail
预览图 preview
自定义信息 custom data
原始数据 data
这些数据我们都可以通过上面获得的 DJIMediaFile
调用相关 API 来获取。
// 缩略图
fetchThumbnail(completion: DJICompletionBlock?)
// 预览图
fetchPreview(completion: DJICompletionBlock?)
// 自定义信息
fetchCustomInformation(completion: DJICompletionBlock?)
// 原始数据
fetchData(withOffset: UInt, update: DispatchQueue, update: (Data?, Bool, Error?) -> Void)
DJISDK
还提供了一个叫 DJIFetchMediaTaskSchedule
的东西来获取文件数据
注意:
DJIFetchMediaTaskSchedule
并不能获取原始数据,要想获取原始数据必须用DJIMediaFile
的fetchFileDataWithOffset:updateQueue:updateBlock
方法。
guard let mediaTaskScheduler = camera?.mediaManager.taskScheduler else { return }
// 设置为 fasle,当执行过程中出错时,mediaTaskScheduler 不会暂停,而继续执行队列里面的任务。
mediaTaskScheduler.suspendAfterSingleFetchTaskFailure = false
mediaTaskScheduler.resume(completion: nil)
let fetchTask = DJIFetchMediaTask(file: mediaFile, content: .thumbnail) {
(mediaFile, fetchMediaTaskContent, error) in
//...
}
// moveTaskToEnd: 将任务放到队列末尾;moveTaskToNext:将任务放到队列开头
mediaTaskScheduler.moveTask(toEnd: fetchTask)
上面代码中的 .thumbnail
是 DJIFetchMediaTaskContent
的枚举,它包含了缩略图,预览图和自定义信息,传入相应的枚举值就可以获取相应的数据。
3-2. 下载和删除文件
- 下载原文件
下载原始文件,只有 DJIMeidaFile
的 fetchFileDataWithOffset:updateQueue:updateBlock
方法。
var fileData = Data()
var previousDataOffset: UInt = 0
mediaFile.fetchData(withOffset: 0, update: DispatchQueue.main) { (data, isFinish, error) in
if let error = error {
print("fetch media error: " + error.localizedDescription)
} else {
if let data = data {
fileData.append(data)
previousDataOffset = previousDataOffset + UInt(data.count)
}
if previousDataOffset == UInt(mediaFile.fileSizeInBytes) && isFinish {
completion(fileData)
}
}
}
updateQueue
指定了 updateBlock
在哪个队列中执行,updateBlock
会在执行过程中多次调用,在 updateBlock
中我们就可以做一些诸如前台显示下载进度的操作:
let progress = CGFloat(previousOffset) / CGFloat(mediaFile.fileSizeInBytes)
DJIMediaFile
的 fileSizeInBytes
属性可以获取源文件以字节为单位的大小,上面做一个除法就可以得到下载的进度值了。
当然,你也可以调用 DJIMediaFile
的下面这个方法来停止当前下载原文件的操作 :
stopFetchingFileData(completion: DJICompletionBlock?)
- 删除文件
要想删除文件,只需要调用 DJIMediaManager
的这个方法:
delete(_ files: [DJIMediaFile], withCompletion completion: (([DJIMediaFile], Error?) -> Void)? = nil)
这个方法传入的参数是一个 DJIMediaFile
的数组。
3-3. 获取文件属性值
文件的相关属性值,都可以直接从 DJIMediaFile
上获取相关的属性,下面列出一些常用的属性:
fileName | 文件名称 |
---|---|
mediaType | 文件类型(视频还是图片等等具体类型) |
fileSizeInBytes | 文件大小 |
timeCreated | 文件创建时间 |
durationInSeconds | 视频文件的时长 |
frameRate | 帧率 |
resolution | 分辨率 |
至此,一个基本的文件管理所需要的相关 API 就介绍完了,更详细的内容还请多查看 DJI-SDK API 文档 。由于笔者能力有限,文中如有错误还请各位读者不吝赐教。