用大疆无人机拍摄完后,怎么少得了去查看一下自己的作品是否满意呢?这时候一个文件回看和管理的功能就至关重要了。本文主要就开发 DJIFileManger 中所涉及 DJI-SDK 的相关 API 进行简要的说明和分析。

1. 操作流程概览

首先我们要明确做一个文件回看和管理的功能有什么基本要素:

  • 缩略图

  • 预览图

  • 查看文件各种属性值

  • 下载原图或者原视频

  • 删除

明确了这几点后,我们就可以去 DJI-SDK API 文档 中查找相关的 API 了,也可以通过官方的一个 Demo 来窥探部分相关 API 的操作。这里我们根据相关的 API 可以得出这样的一个操作流程:

DJI SDK 文件相关操作的 API 简析 - 图1 下面我会根据这个基本的流程来进行讲解。

2. 文件操作前置准备

2-1. 相机模式切换及文件存储路径

我们需要把相机的模式切换为 MediaDownload ,在此模式下我们才可以进行文件相关的操作。

  1. // 获取相机
  2. let camera = DJISDKManager.product()?.camera
  3. // 切换相机模式
  4. camera?.setMode(.mediaDownload) { error in
  5. // handle error
  6. }

接着我们需要获取文件存储的路径,这里文件的存储路径有两种:SD卡和飞机内部存储。这里获取存储路径用到的 API 是 DJICamera 下的这个方法:

  1. getStorageLocation(completion: @escaping (DJICameraStorageLocation, Error?) -> Void)

它会返回一个 DJICameraStorageLocation 的枚举。接下来我们就可以利用这个存储路径去刷新文件列表,以及获取文件列表的快照了。

2-2. 刷新文件列表及获取文件列表快照

首先我们需要刷新在存储空间上的文件,以获得最新的文件列表,这里用到的 API 是 DJIMediaManager 下的这个方法:

  1. func refreshFileList(of storageLocation: DJICameraStorageLocation, withCompletion completion: DJICompletionBlock? = nil)

该方法所需要的一个参数 storageLocation 就是上一节获取的存储路径。在这个方法的回调中,即刷新列表成功后,我们就可以获取到文件列表的快照(FileListSnapshot)了,注意应该根据文件存储位置的不同而选择调用 DJIMediaManager 的下面这两个方法:

  1. sdCardFileListSnapshot()
  2. internalStoragefileListSnapshot()

这两个方法从命名上可以看出返回的是一个文件列表的快照(FileListSnapshot),而实际上它们返回的都是一个 DJIMediaFile 的数组, DJIMediaFile 这个类定义了文件相关的各种信息,有了 DJIMediaFile 我们就可以获取文件的缩略图、预览图还有原图等等数据了。

3. 文件操作

3-1. 获取缩略图和预览图

对于相机拍摄的每一个文件, DJISDK 提供了四种类型数据:

  • 缩略图 thumbnail

  • 预览图 preview

  • 自定义信息 custom data

  • 原始数据 data

这些数据我们都可以通过上面获得的 DJIMediaFile 调用相关 API 来获取。

  1. // 缩略图
  2. fetchThumbnail(completion: DJICompletionBlock?)
  3. // 预览图
  4. fetchPreview(completion: DJICompletionBlock?)
  5. // 自定义信息
  6. fetchCustomInformation(completion: DJICompletionBlock?)
  7. // 原始数据
  8. fetchData(withOffset: UInt, update: DispatchQueue, update: (Data?, Bool, Error?) -> Void)

DJISDK 还提供了一个叫 DJIFetchMediaTaskSchedule 的东西来获取文件数据

注意: DJIFetchMediaTaskSchedule 并不能获取原始数据,要想获取原始数据必须用 DJIMediaFilefetchFileDataWithOffset:updateQueue:updateBlock 方法。

  1. guard let mediaTaskScheduler = camera?.mediaManager.taskScheduler else { return }
  2. // 设置为 fasle,当执行过程中出错时,mediaTaskScheduler 不会暂停,而继续执行队列里面的任务。
  3. mediaTaskScheduler.suspendAfterSingleFetchTaskFailure = false
  4. mediaTaskScheduler.resume(completion: nil)
  5. let fetchTask = DJIFetchMediaTask(file: mediaFile, content: .thumbnail) {
  6. (mediaFile, fetchMediaTaskContent, error) in
  7. //...
  8. }
  9. // moveTaskToEnd: 将任务放到队列末尾;moveTaskToNext:将任务放到队列开头
  10. mediaTaskScheduler.moveTask(toEnd: fetchTask)

上面代码中的 .thumbnailDJIFetchMediaTaskContent 的枚举,它包含了缩略图,预览图和自定义信息,传入相应的枚举值就可以获取相应的数据。

3-2. 下载和删除文件

  • 下载原文件

下载原始文件,只有 DJIMeidaFilefetchFileDataWithOffset:updateQueue:updateBlock 方法。

  1. var fileData = Data()
  2. var previousDataOffset: UInt = 0
  3. mediaFile.fetchData(withOffset: 0, update: DispatchQueue.main) { (data, isFinish, error) in
  4. if let error = error {
  5. print("fetch media error: " + error.localizedDescription)
  6. } else {
  7. if let data = data {
  8. fileData.append(data)
  9. previousDataOffset = previousDataOffset + UInt(data.count)
  10. }
  11. if previousDataOffset == UInt(mediaFile.fileSizeInBytes) && isFinish {
  12. completion(fileData)
  13. }
  14. }
  15. }

updateQueue 指定了 updateBlock 在哪个队列中执行,updateBlock 会在执行过程中多次调用,在 updateBlock 中我们就可以做一些诸如前台显示下载进度的操作:

  1. let progress = CGFloat(previousOffset) / CGFloat(mediaFile.fileSizeInBytes)

DJIMediaFilefileSizeInBytes 属性可以获取源文件以字节为单位的大小,上面做一个除法就可以得到下载的进度值了。

当然,你也可以调用 DJIMediaFile 的下面这个方法来停止当前下载原文件的操作 :

  1. stopFetchingFileData(completion: DJICompletionBlock?)
  • 删除文件

要想删除文件,只需要调用 DJIMediaManager 的这个方法:

  1. delete(_ files: [DJIMediaFile], withCompletion completion: (([DJIMediaFile], Error?) -> Void)? = nil)

这个方法传入的参数是一个 DJIMediaFile 的数组。

3-3. 获取文件属性值

文件的相关属性值,都可以直接从 DJIMediaFile 上获取相关的属性,下面列出一些常用的属性:

fileName 文件名称
mediaType 文件类型(视频还是图片等等具体类型)
fileSizeInBytes 文件大小
timeCreated 文件创建时间
durationInSeconds 视频文件的时长
frameRate 帧率
resolution 分辨率

至此,一个基本的文件管理所需要的相关 API 就介绍完了,更详细的内容还请多查看 DJI-SDK API 文档 。由于笔者能力有限,文中如有错误还请各位读者不吝赐教。