Swift下DJI无人机照片下载求助:错误码-1007及方案合理性疑问
关于DJI无人机画面获取与下载错误的解决方案
一、先拍照再下载是不是最简单的方式?
其实不是——如果你的需求是获取无人机的最新画面(实时或近实时),直接获取相机的实时预览流(Live View)会更高效,不需要额外拍照和下载,也能避免存储占用和延迟。
具体来说,你可以通过DJICamera的startH264Stream(with:)方法获取H264编码的实时流,然后解码成UIImage;或者用更简便的DJIVideoPreviewer直接显示预览,再从预览中截取画面(如果需要UIImage的话)。这种方式的优势是:
- 几乎无延迟,实时获取画面
- 不需要消耗无人机的SD卡存储空间
- 不需要等待拍照和下载的耗时
当然,如果你的需求是获取已拍摄的高清照片,那拍照后下载是合理的,但如果只是要最新画面,实时流是更优解。
二、错误Code -1007的排查与解决
错误Settings parameters operation failed. (Code : -1007)通常意味着当前相机状态不允许执行你要的操作,常见原因和排查步骤:
- 相机当前处于忙碌状态:比如正在录像、连拍,或者正在处理之前的拍照请求。切换到
mediaDownload模式前,确保相机处于空闲状态(可以通过camera?.mode检查当前模式)。 - SD卡异常:SD卡未插入、损坏,或者正在被写入数据。可以通过
camera?.mediaManager?.sdCardState检查SD卡状态,确保是ready。 - 设备连接不稳定:无人机与遥控器、遥控器与手机的连接中断或信号弱,导致指令无法正常传递。检查连接状态,确保信号正常。
- 权限问题:部分DJI机型需要在DJI Fly/App中开启相关权限,或者你的App没有正确申请DJI SDK的权限(比如存储权限)。
三、你的下载代码的问题与优化
先看代码里的几个明显问题:
- 多余的错误判断:在
setMode的else块里,你又写了if error != nil,但这里error已经是nil(因为进入了else分支),这个判断完全无效。 - 不必要的延迟:
DispatchQueue.main.asyncAfter(deadline: .now() + 1)完全没必要,反而可能导致文件列表还没刷新完成就开始下载,引发错误。 - 分块数据未拼接:
file.fetchData(withOffset:)是分块返回数据的,你现在只把每次的data直接转成UIImage,这会导致图片不完整(因为每次返回的是部分数据),需要把所有分块数据拼接完成后再转UIImage。 - 循环下载所有JPEG:你要的是最新画面,应该只下载最新的那张照片,而不是所有JPEG,这样更高效。
优化后的下载代码示例
@IBAction func downloadLatestPhoto(_ sender: UIButton) { guard let camera = self.fetchCamera() else { self.status.text = "Failed to get camera instance" return } // 先检查相机当前状态,确保可以切换到mediaDownload模式 if camera.mode != .mediaDownload { camera.setMode(.mediaDownload, withCompletion: { [weak self] error in guard let self = self else { return } if let error = error { self.status.text = "Failed to switch mode: \(error.localizedDescription)" return } self.refreshAndDownloadLatestPhoto(camera: camera) }) } else { self.refreshAndDownloadLatestPhoto(camera: camera) } } private func refreshAndDownloadLatestPhoto(camera: DJICamera) { guard let mediaManager = camera.mediaManager else { self.status.text = "Media manager not available" return } // 先刷新SD卡文件列表 mediaManager.refreshFileList(completion: { [weak self] error in guard let self = self else { return } if let error = error { self.status.text = "Failed to refresh file list: \(error.localizedDescription)" return } // 获取SD卡中的所有文件,按创建时间倒序,取最新的JPEG guard let allFiles = mediaManager.sdCardFileListSnapshot() else { self.status.text = "No files on SD card" return } let jpegFiles = allFiles.filter { $0.mediaType == .JPEG } .sorted { $0.timeCreated > $1.timeCreated } guard let latestPhoto = jpegFiles.first else { self.status.text = "No JPEG photos found" return } self.status.text = "Downloading latest photo (created at \(latestPhoto.timeCreated))" // 拼接分块数据 var downloadedData = Data() latestPhoto.fetchData(withOffset: 0, update: DispatchQueue.main) { data, isComplete, error in if let error = error { self.status.text = "Download failed: \(error.localizedDescription)" return } if let data = data { downloadedData.append(data) } if isComplete { // 数据下载完成,转成UIImage if let image = UIImage(data: downloadedData) { self.status.text = "Latest photo downloaded successfully!" // 这里可以处理你的image,比如显示或存储 self.handleDownloadedImage(image) } else { self.status.text = "Failed to convert data to UIImage" } } } }) } private func handleDownloadedImage(_ image: UIImage) { // 在这里添加你的图片处理逻辑,比如显示到UIImageView }
四、实时获取画面的简化方案(替代拍照下载)
如果你的需求是获取最新的实时画面,推荐用DJIVideoPreviewer来实现,示例代码如下:
import DJISDK class ViewController: UIViewController { var videoPreviewer: DJIVideoPreviewer! var camera: DJICamera? override func viewDidLoad() { super.viewDidLoad() // 初始化视频预览器 videoPreviewer = DJIVideoPreviewer.instance() videoPreviewer.setView(self.view) // 设置预览显示的View videoPreviewer.start() // 获取相机实例 camera = self.fetchCamera() camera?.setVideoFeedCallback { [weak self] videoData in // 将视频数据传递给预览器 self?.videoPreviewer.push(videoData) } // 如果需要截取预览画面为UIImage // 可以在需要的时候调用: // let snapshot = videoPreviewer.snapshot() // if let image = snapshot { // // 处理截图 // } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) videoPreviewer.stop() } }
这种方式可以实时显示无人机画面,并且随时截取UIImage,比拍照下载高效得多。
内容的提问来源于stack exchange,提问作者Sam




