Open dkapur17 opened 2 years ago
The step you are capturing shot is correct but fetch asset is wrong. You need to enterPlayback first and then refreh and read those assets. You can only refresh when you are in the playback mode.
Hey @DJI-William, thanks for your reply!
I have placed debug statements after every call to the SDK, so I know exactly which call is failing, and the one to refresh the list on the SD card seems to be working as expected when the camera is not in playback mode. The failure at all times occurs in the fetch step (which I call when the camera is in playback mode). I will however try your suggestion and get back to you ASAP.
Hey @DJI-William. I tried your proposed solution, but it still results in the error "Media download result: the server aborts the downloading". I've attached below my routine for fetching and returning the media by index. It is in Kotlin, but I've added comments to each of the steps to make it understandable:
// Storing the index in n
var n = idx
// Put the camera into playback mode and wait for coroutine to complete
val enterPlaybackError = suspendCoroutine<DJIError?> { cont ->
drone!!.camera!!.enterPlayback { error ->
cont.resume(error)
}
}
// If there is an error in entering playback mode, return
if(enterPlaybackError != null)
return CommandCompleted(false, "Enter Playback Error: " + enterPlaybackError.description)
// Refresh the file list on the SD Card and wait for the coroutine to complete
val refreshError = suspendCoroutine<DJIError?> { cont ->
drone!!.camera!!.mediaManager!!.refreshFileListOfStorageLocation(SettingsDefinitions.StorageLocation.SDCARD) { error ->
cont.resume(error)
}
}
// If there is an error in refreshing the file list, return
if(refreshError != null)
return CommandCompleted(false, "Unable to refresh file list: " + refreshError.description)
// If after refreshing, the state is still not UP_TO_DATE, something went wrong, so return
if(drone!!.camera!!.mediaManager!!.sdCardFileListState != MediaManager.FileListState.UP_TO_DATE)
return CommandCompleted(false, "Unable to refresh file list")
// Get the snapshot of all files
val mediaFiles = drone!!.camera!!.mediaManager!!.sdCardFileListSnapshot
// If the snapshot is null, return
if(mediaFiles == null)
return CommandCompleted(false, "Unable to fetch media files")
// Sort media files by time
mediaFiles!!.sortByDescending { it.timeCreated }
// Validate the provided index
if(n >= mediaFiles.size)
call.respond(CommandCompleted(false, "Index n ($n) exceeds total number of files in storage (${mediaFiles.size})"))
// Access the target file
val targetFile = mediaFiles[n]
// Create a byte buffer to push the media file bytes into
val fullMediaBuffer = ByteBuffer.allocate(targetFile.fileSize.toInt())
// Run the fetchFileByteData coroutine and wait for it to complete
// downloadError is a String if success, or a DJIError if fail
// Error seems to occur here, since the coroutine returns a DJIError object on failure
val downloadError = suspendCoroutine { cont ->
targetFile.fetchFileByteData(0, object: DownloadListener<String> {
override fun onStart() {
return
}
override fun onRateUpdate(p0: Long, p1: Long, p2: Long) {
return
}
// On receiving each chunk of the media file, place it into the allocated buffer
override fun onRealtimeDataUpdate(
p0: ByteArray?,
p1: Long,
p2: Boolean
) {
if(p0 != null)
fullMediaBuffer.put(p0)
return
}
override fun onProgress(p0: Long, p1: Long) {
return
}
// If success, then return a string
override fun onSuccess(p0: String?) {
cont.resume(p0)
}
// If failure, then return a DJIError
override fun onFailure(p0: DJIError?) {
cont.resume(p0)
}
})
}
// Exit playback mode and wait for coroutine to complete
val exitPlaybackError = suspendCoroutine<DJIError?> { cont ->
drone!!.camera!!.exitPlayback { error ->
cont.resume(error)
}
}
// If there is an error in exiting the playback mode, log the error
if(exitPlaybackError != null)
Log.d("DIAGNOSIS", exitPlaybackError.description)
// Check for status of download completion
// In case of error, return
if(downloadError is DJIError)
return CommandCompleted(false, "Error in downloading: " + downloadError.description)
// In case of success, convert the media in a base64 encoded string and return
else if(downloadError is String) {
val fullImageBytes = fullMediaBuffer.array()
val fullImageString = Base64.encodeToString(fullImageBytes, Base64.NO_WRAP)
return fullImageString
}
// Otherwise, return
else
CommandCompleted(false, "Error in downloading")
This routine works sometimes but doesn't work other times, and I can't figure out what is causing this inconsistency. Please let me know if something is unclear in the code. Really appreciate your help!
@DJI-William I think I've narrowed down the problem. It seems to be in the refresh step. I notice that whenever I get these errors, the mediaFiles
list (fetched using drone!!.camera!!.mediaManager!!.sdCardFileListSnapshot
) has just one item in it. This seems to be due to some problem in the call to refreshFileListOfStorageLocation()
. Looks like this call runs without throwing an exception, but it doesn't update the snapshot itself (or updates it to something incorrect). I inspected the single item in the list and it is a file with the name DJI_0000.jpg
, which I think must be some kind of error indication, since no such file exists in the SD Card. Is there any way to make this call work more consistently, or am I missing something?
I don't think your code is wrong. Anyway, Have you upgrade your aircraft firmware to the latest. Also try format your SD card first and test again. What happens if you use DJI FLY?
Yes, the firmware is up to date (01.05.0000). I can see all the images I've captured in the DJIFly app without any issues.
I noticed that the problem crops up when I call the routine for taking the image and then immediately call any of the fetch routines, it leads to an execution timeout/download abort. If I give it say 10 seconds and then call the fetch routine then it seems to run successfully. I haven't run enough tests to say for sure, but I noticed this pattern. Could it be because even after calling the startPhotoShoot
method, the actual procedure of capturing the image and then writing it to the SD Card is not completed when I call the fetch routine, which is why it fails? If this is the case, is there some functionality in the SDK using which I can wait until the image is saved to the SD Card before returning from the capture routine?
Of cource, after calling the startPhotoShoot, it does not mean the shoot action is completed. You should call setNewGeneratedMediaFileInfoCallback to setup a media file generated listener. When this callback has a return, it means the shoot action is finished and a new media file is generated. Then you can go enterPlayback and refresh the list.
@dkapur17 did you ever figure out a workaround for this issue? I'm seeing the same issue with my app. I have the same issue with refreshFileListOfStorageLocation() intermittently failing. Also sometimes, even before I get that far enterPlayback sometimes fails to complete
https://github.com/dji-sdk/Mobile-SDK-Android/issues/1188#issuecomment-1539153390
On the DJI Mini 2, the calls to
fetchPreview
,fetchThumbnail
andfetchFileByteData
often fail, yielding one of the two errors:Since DJI Mini's firmware doesn't support the
setMode
method on the camera, I'm using thesetFlatMode
. My mode changes are as such:To capture a shot
startShootPhoto
To fetch asset (thumbnail, preview or media)
I'm unsure if the modes are correct for the different stages because the documentation doesn't make this clear. What's interesting is that these routines work sometimes, but then after one failure, they just keep failing which makes me believe that the failure causes them to end up in some mode that causes the failure again, and so it's a feedback loop. However, I'm unable to figure out what that mode is since I make sure to exit playback mode in case the download/fetch of the asset fails or succeeds, and even make sure to exit playback mode at the start of both the routines just in case.
Any help is appreciated.