dji-sdk / Onboard-SDK

DJI Onboard SDK Official Repository
https://github.com/dji-sdk/Onboard-SDK
Other
908 stars 634 forks source link

OSDK 4.1 request file list broken with firmware 03.00.0101 #847

Open vdebad opened 2 years ago

vdebad commented 2 years ago

I am trying to download image files from the Zenmuse H20T's SD-Card for image processing. When requesting file list, the call returns with the error "parseFileList, L710: File list total data len error !". See code at the end of this post for reproduction.

Aircraft: DJI Matrice M300 RTK

Firmware version: Aircraft: 03.00.0101 Remote control: 03.00.0102 Battery1: 01.02.05.31 Battery2: 01.02.05.31 Zenmuse H20T: 03.00.01.02

Here is the log:

Read App ID
User Configuration read successfully. 

[1288463.414]STATUS/1 @ getDroneVersion, L1702: ret = 0
[1288463.414]STATUS/1 @ parseDroneVersionInfo, L1122: Device Serial No. = 1ZNDH9G00C128M
[1288463.414]STATUS/1 @ parseDroneVersionInfo, L1124: Firmware = 3.4.8.93
[1288463.414]STATUS/1 @ functionalSetUp, L279: Shake hand with drone successfully by getting drone version.
[1288463.414]STATUS/1 @ legacyX5SEnableTask, L56: Legacy X5S Enable task created.
[1288464.414]STATUS/1 @ sendHeartbeatToFCTask, L1576: OSDK send heart beat to fc task created.
[1288464.614]STATUS/1 @ Control, L40: The control class is going to be deprecated.It will be better to use the FlightController class instead!
[1288464.614]STATUS/1 @ FileMgrImpl, L308: register download file callback handler successfully.
[1288464.637]STATUS/1 @ Firewall, L45: Firewall is initializing ...
[1288464.637]STATUS/1 @ Firewall, L65: osdk policy file updating(1) ......
[1288465.638]STATUS/1 @ Firewall, L73: osdk policy file updating(2) ......
[1288465.648]STATUS/1 @ RequestUploadPolicyFileHandle, L217: request upload policy file type:0
[1288465.648]STATUS/1 @ RequestUploadPolicyFileHandle, L234: Upload policy file info md5 checksum and version
[1288465.650]STATUS/1 @ RequestUploadPolicyFileHandle, L217: request upload policy file type:1
[1288465.650]STATUS/1 @ RequestUploadPolicyFileHandle, L254: request upload policy file data: 0 0 200
[1288465.652]STATUS/1 @ RequestUploadPolicyFileHandle, L217: request upload policy file type:1
[1288465.652]STATUS/1 @ RequestUploadPolicyFileHandle, L254: request upload policy file data: 1 200 175
[1288465.654]STATUS/1 @ RequestUploadPolicyFileHandle, L217: request upload policy file type:2
[1288465.654]STATUS/1 @ RequestUploadPolicyFileHandle, L282: request upload policy file success
[1288466.638]STATUS/1 @ firewallTask, L112: firewall task created ...
[1288466.663]STATUS/1 @ getDroneVersion, L1702: ret = 0
[1288466.663]STATUS/1 @ parseDroneVersionInfo, L1122: Device Serial No. = 1ZNDH9G00C128M
[1288466.663]STATUS/1 @ parseDroneVersionInfo, L1124: Firmware = 3.4.8.93
[1288466.669]STATUS/1 @ AdvancedSensing, L110: Start bulk channel for M300's liveview!
[1288466.669]STATUS/1 @ AdvancedSensing, L117: Create task for M300's liveview!
[1288466.670]STATUS/1 @ AdvancedSensing, L125: Start bulk channel for M300's perception
[1288466.670]STATUS/1 @ AdvancedSensing, L132: Create task for M300's advanced sensing!
[1288466.670]STATUS/1 @ AdvancedSensing, L135: Advanced Sensing init for the M300 drone
[1288466.670]STATUS/1 @ LiveViewImpl, L89: Finding if liveview stream is available now.
[1288467.470]STATUS/1 @ init, L254: Start advanced sensing initalization
[1288467.470]STATUS/1 @ activate, L1329: version 0x304085D

[1288467.493]STATUS/1 @ activate, L1367: Activation successful

Camera module name: H20T.C
[1288468.495]STATUS/1 @ main, L212: Getting work mode of camera at payload index 0 (H20T)
[1288468.537]STATUS/1 @ main, L224: Current work mode:
[1288468.537]STATUS/1 @ main, L228: Capture mode. In this mode, the user can capture pictures.
[1288468.537]STATUS/1 @ main, L265: Getting shoot photo mode of camera at payload index 0 (H20T)
[1288468.557]ERRORLOG/1 @ main, L274: Could not get camera's shoot-photo mode using 'getShootPhotoModeSync'. Error code: 0xE0
[1288468.557]ERRORLOG/1 @ printErrorCodeMsg, L938: >>>>Error module   : Camera
[1288468.557]ERRORLOG/1 @ printErrorCodeMsg, L939: >>>>Error message  : Command not supported
[1288468.557]ERRORLOG/1 @ printErrorCodeMsg, L940: >>>>Error solution : Check the firmware or command validity
[1288468.557]STATUS/1 @ main, L312: Taking single picture with camera at payload index 0 (H20T)
[1288470.577]STATUS/1 @ main, L333: Setting camera's work mode to PLAYBACK
[1288470.598]ERRORLOG/1 @ main, L340: Could not set camera's work mode to playback using 'setModeSync(DJI::OSDK::PAYLOAD_INDEX_0, DJI::OSDK::CameraModule::WorkMode::PLAYBACK, 2)'. Error code: 0xD9
[1288470.598]ERRORLOG/1 @ printErrorCodeMsg, L938: >>>>Error module   : Camera
[1288470.598]ERRORLOG/1 @ printErrorCodeMsg, L939: >>>>Error message  : Unknown
[1288470.598]ERRORLOG/1 @ printErrorCodeMsg, L940: >>>>Error solution : ����
[1288470.598]STATUS/1 @ main, L349: Obtain read permission to camera memory from camera, blocking calls
[1288470.677]STATUS/1 @ fileListMonitorTask, L154: OSDK download monitor task created.
[1288470.768]STATUS/1 @ OnReceiveDataPack, L1076: [First pack] get the first pack
[1288470.769]STATUS/1 @ parseFileList, L706: ###data->amount = 24, data->len = 6560
[1288470.769]ERRORLOG/1 @ parseFileList, L710: File list total data len error !
[1288470.769]STATUS/1 @ SendAbortPack, L1137: SendAbortPack
[1288470.769]STATUS/1 @ fileMgrFileListCb, L125: Unknown file type.
[1288470.769]STATUS/1 @ fileMgrFileListCb, L129: Number of files: 0
[1288470.769]STATUS/1 @ fileListRawDataCB, L1008: Finish req filelist task, reset downloadState to be DOWNLOAD_IDLE
[1288477.705]STATUS/1 @ verify, L244: Verify subscription successful.
[1288477.734]STATUS/1 @ reset, L551: Reset Subscription Successful.

[1288478.159]STATUS/1 @ ~AdvancedSensing, L186: Destroy task for M300's liveview!
[1288478.160]STATUS/1 @ ~AdvancedSensing, L194: Destroy task for M300's advanced sensing!

Eventually related to #833

Identified problem:

In dji_file_mgr_impl.cpp in function parseFileList data->len is checked against maxDataFrameLen which is set to 1024, but length of data is obviously larger (6560 in this case). fullDataList.size() is 2.

const uint32_t maxDataFrameLen = 1024;  // link data frame max size

...

if (listdata->len > fullDataList.size() * maxDataFrameLen)
{
    DERROR("File list total data len error !");
    return pack;
}

Commenting the length check yields normal operation. At a frist glance at least. My guess: maxDataFrameLen changed in a firmware release but was not changed in dji_file_mgr_impl.cpp.

Could DJI please confirm this bug and validate my workaround? Is there a new maxDataFrameLen-value?

Code to reproduce

#include <pthread.h>

#include <dji_camera_image.hpp>
#include <dji_camera_manager.hpp>
#include <dji_linux_helpers.hpp>
#include <iostream>

// definition is not loaded from cmake automagically
// hence definition is done manually in
// c_cpp_properties.json
#ifdef OPEN_CV_INSTALLED
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
// using namespace cv;
#endif

// using namespace DJI::OSDK;
// using namespace DJI::OSDK::Telemetry;

void show_rgb_image_cb(CameraRGBImage img, void* p)
{
    std::string name = std::string(reinterpret_cast<char*>(p));
    cout << "#### Got image from:\t" << name << endl;
#ifdef OPEN_CV_INSTALLED
    cv::Mat mat(img.height, img.width, CV_8UC3, img.rawData.data(),
                img.width * 3);
    cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR);
    cv::imshow(name, mat);
    cv::waitKey(1);
#endif
}

std::string fileTypeEnum2String(DJI::OSDK::MediaFileType type)
{
    switch (type)
    {
    case DJI::OSDK::MediaFileType::JPEG:
        return std::string("JPEG");
    case DJI::OSDK::MediaFileType::DNG:
        return std::string("DNG");
    case DJI::OSDK::MediaFileType::MOV:
        return std::string("MOV");
    case DJI::OSDK::MediaFileType::MP4:
        return std::string("MP4");
    case DJI::OSDK::MediaFileType::PANORAMA:
        return std::string("PANORAMA");
    case DJI::OSDK::MediaFileType::TIFF:
        return std::string("TIFF");
    case DJI::OSDK::MediaFileType::UL_CTRL_INFO:
        return std::string("UL_CTRL_INFO");
    case DJI::OSDK::MediaFileType::UL_CTRL_INFO_LZ4:
        return std::string("UL_CTRL_INFO_LZ4");
    case DJI::OSDK::MediaFileType::AUDIO:
        return std::string("AUDIO");
    default:
        return std::string("UNKNOWN");
    }
}

int writeStreamData(const char* fileName, const uint8_t* data, uint32_t len)
{
    FILE* fp = NULL;
    size_t size = 0;

    fp = fopen(fileName, "a+");
    if (fp == NULL)
    {
        printf("fopen failed!\n");
        return -1;
    }
    size = fwrite(data, 1, len, fp);
    if (size != len)
    {
        fclose(fp);
        return -1;
    }

    fflush(fp);
    fclose(fp);

    return 0;
}

void liveViewSampleCb(uint8_t* buf, int bufLen, void* userData)
{
    char filename[32] = "h20t_video.h264";
    writeStreamData(filename, buf, bufLen);
}

void fileMgrFileListCb(E_OsdkStat retCode, const FilePackage fileList,
                       void* userData)
{
    if (retCode != E_OsdkStat::OSDK_STAT_OK)
    {
        DERROR(
            "Error receiving file list. Error code: "
            "0x%1X",
            retCode);
    }
    else
    {
        switch (fileList.type)
        {
        case DJI::OSDK::FileType::MEDIA:
            DSTATUS("Media file.");
            break;
        case DJI::OSDK::FileType::COMMON:
            DSTATUS("Common file.");
            break;
        case DJI::OSDK::FileType::SPEAKER_AUDIO:
            DSTATUS("Audio file.");
            break;
        default:
            DSTATUS("Unknown file type.");
            break;
        }

        DSTATUS("Number of files: %d", fileList.media.size());

        for (std::size_t i = 0; i < fileList.media.size(); ++i)
        {
            std::string fileType = {

            };
            std::cout << "File " << i << ": "
                      << (fileList.media[i].valid ? "Valid" : "Invalid")
                      << " File at index " << fileList.media[i].fileIndex
                      << " named " << fileList.media[i].fileName << "("
                      << fileTypeEnum2String(fileList.media[i].fileType) << ")"
                      << endl;
        }
    }
}

int main(int argc, char** argv)
{
    /*! Setup the OSDK: Read config file, create vehicle, activate. */
    LinuxSetup linuxEnvironment(argc, argv, true /*enable advanced sensing*/);
    Vehicle* vehicle = linuxEnvironment.getVehicle();

    if (vehicle == NULL)
    {
        DERROR("Vehicle not initialized, exiting.");
        return -1;
    }

    // configure advanced sensing
    const char* acm_dev =
        linuxEnvironment.getEnvironment()->getDeviceAcm().c_str();
    vehicle->advancedSensing->setAcmDevicePath(acm_dev);

    // Get CameraManager handle
    CameraManager* cm = vehicle->cameraManager;

    // Initialize camera modules
    ErrorCode::ErrorCodeType ret =
        cm->initCameraModule(DJI::OSDK::PAYLOAD_INDEX_0, "H20T.C");

    if (ret != ErrorCode::SysCommonErr::Success)
    {
        DERROR("Init Camera module H20T.C failed.");
        ErrorCode::printErrorCodeMsg(ret);
    }

    // Initialize thermal camera modules
    ret = cm->initCameraModule(DJI::OSDK::PAYLOAD_INDEX_1, "H20T.T");

    if (ret != ErrorCode::SysCommonErr::Success)
    {
        DERROR("Init Camera module H20T.T failed.");
        ErrorCode::printErrorCodeMsg(ret);
    }

    std::string cameraName;
    if (cm->getCameraModuleName(DJI::OSDK::PAYLOAD_INDEX_0, cameraName)
        == ErrorCode::SysCommonErr::Success)
    {
        std::cout << "Camera module name: " << cameraName << std::endl;
    }

    if (true)  // camera control and photo
    {
        // Get camera work mode
        DSTATUS("Getting work mode of camera at payload index 0 (H20T)");

        DJI::OSDK::CameraModule::WorkMode mode;
        ret = cm->getModeSync(DJI::OSDK::PAYLOAD_INDEX_0, mode, 5);

        if (ret != ErrorCode::SysCommonErr::Success)
        {
            DERROR("Could not get camera's work mode. Error code: 0x%1X", ret);
            ErrorCode::printErrorCodeMsg(ret);
        }
        else
        {
            DSTATUS("Current work mode:");
            switch (mode)
            {
            case DJI::OSDK::CameraModule::WorkMode::SHOOT_PHOTO:
                DSTATUS(
                    "Capture mode. In this mode, the user can capture "
                    "pictures.");
                break;
            case DJI::OSDK::CameraModule::WorkMode::RECORD_VIDEO:
                DSTATUS(
                    "Record mode. In this mode, the user can record videos. ");
                break;
            case DJI::OSDK::CameraModule::WorkMode::PLAYBACK:
                DSTATUS(
                    "Playback mode. In this mode, the user can preview photos "
                    "and videos, and can delete files.");
                break;
            case DJI::OSDK::CameraModule::WorkMode::MEDIA_DOWNLOAD:
                DSTATUS(
                    "In this mode, the user can download media to the Mobile "
                    "Device.");
                break;
            case DJI::OSDK::CameraModule::WorkMode::BROADCAST:
                DSTATUS(
                    "In this mode, live stream resolution and frame rate will "
                    "be 1080i50 (PAL) or 720p60 (NTSC). In this mode videos "
                    "can be recorded. Still photos can also be taken only when "
                    "video is recording. The only way to exit broadcast mode "
                    "is to change modes to RECORD_VIDEO. Only supported by "
                    "Inspire 2.");
                break;
            case DJI::OSDK::CameraModule::WorkMode::WORK_MODE_UNKNOWN:
                DSTATUS("The camera's work mode is unknown.");
                break;
            }
        }

        // Get camera work mode
        if (mode == DJI::OSDK::CameraModule::WorkMode::SHOOT_PHOTO)
        {
            // Get shoot photo mode (seems not to be supported by M300)
            DSTATUS(
                "Getting shoot photo mode of camera at payload index 0 (H20T)");

            DJI::OSDK::CameraModule::ShootPhotoMode photoMode;
            ret = cm->getShootPhotoModeSync(DJI::OSDK::PAYLOAD_INDEX_0,
                                            photoMode, 5);

            if (ret != ErrorCode::SysCommonErr::Success)
            {
                DERROR(
                    "Could not get camera's shoot-photo mode using "
                    "'getShootPhotoModeSync'. Error code: 0x%1X",
                    ret);
                ErrorCode::printErrorCodeMsg(ret);
            }
            else
            {
                DSTATUS("Current shoot-photo mode:");
                switch (photoMode)
                {
                case DJI::OSDK::CameraModule::ShootPhotoMode::SINGLE:
                    DSTATUS("Camera takes a single photo. ");
                    break;
                case DJI::OSDK::CameraModule::ShootPhotoMode::HDR:
                    DSTATUS("Camera takes an HDR photo.");
                    break;
                case DJI::OSDK::CameraModule::ShootPhotoMode::BURST:
                    DSTATUS("Camera takes multiple photos at once.");
                    break;
                case DJI::OSDK::CameraModule::ShootPhotoMode::AEB:
                    DSTATUS("Automatic Exposure Bracketing (AEB) capture.");
                    break;
                case DJI::OSDK::CameraModule::ShootPhotoMode::INTERVAL:
                    DSTATUS(
                        "Camera takes a picture (or multiple pictures) "
                        "continuously at a set time interval.");
                    break;
                case DJI::OSDK::CameraModule::ShootPhotoMode::REGIONAL_SR:
                    DSTATUS("Vamera takes an regional photo. WTF?");
                    break;
                default:
                    DSTATUS("The camera's shoot-photo mode is unknown.");
                    break;
                }
            }

            // Shoot single photo
            DSTATUS(
                "Taking single picture with camera at payload index 0 (H20T)");

            ret = cm->startShootPhotoSync(
                DJI::OSDK::PAYLOAD_INDEX_0,
                DJI::OSDK::CameraModule::ShootPhotoMode::SINGLE, 5);

            if (ret != ErrorCode::SysCommonErr::Success)
            {
                DERROR(
                    "Could not take a picture. Error code: "
                    "0x%1X",
                    ret);
                ErrorCode::printErrorCodeMsg(ret);
            }

            sleep(2);

            // Request file list if shooting photo was successful
            if (ret == ErrorCode::SysCommonErr::Success)
            {
                DSTATUS("Setting camera's work mode to PLAYBACK");
                ret = cm->setModeSync(
                    DJI::OSDK::PAYLOAD_INDEX_0,
                    DJI::OSDK::CameraModule::WorkMode::PLAYBACK, 2);

                if (ret != ErrorCode::SysCommonErr::Success)
                {
                    DERROR(
                        "Could not set camera's work mode to playback using "
                        "'setModeSync(DJI::OSDK::PAYLOAD_INDEX_0, "
                        "DJI::OSDK::CameraModule::WorkMode::PLAYBACK, 2)'. "
                        "Error code: 0x%1X",
                        ret);
                    ErrorCode::printErrorCodeMsg(ret);
                }

                DSTATUS(
                    "Obtain read permission to camera memory from camera, "
                    "blocking calls");
                ret = cm->obtainDownloadRightSync(
                    DJI::OSDK::PAYLOAD_INDEX_0,
                    true /* obtain right to download */, 2);

                if (ret != ErrorCode::SysCommonErr::Success)
                {
                    DERROR(
                        "Could not obtain read permission for camera storage. "
                        "Error code: 0x%1X",
                        ret);
                    ErrorCode::printErrorCodeMsg(ret);
                }

                ret = cm->startReqFileList(DJI::OSDK::PAYLOAD_INDEX_0,
                                           fileMgrFileListCb, NULL);

                sleep(5);  // wait 5 seconds for callback

                if (ret != ErrorCode::SysCommonErr::Success)
                {
                    DERROR("Could not get file list. Error code: 0x%1X", ret);
                    ErrorCode::printErrorCodeMsg(ret);
                }
            }
        }
    }

    return 0;
}
dji-dev commented 2 years ago

Agent comment from DJI SDK in Zendesk ticket #56781:

尊敬的开发者,感谢您联系DJI 大疆创新 由于github不是我们主要的咨询渠道,您的问题可能跟进不及时。我们建议您通过填写表单( https://djisdksupport.zendesk.com/hc/zh-cn/requests/new )向我们反馈问题。或者您也可以在论坛发帖,与其它开发者交流。论坛链接:https://djisdksupport.zendesk.com/hc/zh-cn/community/topics

Dear developer, thank you for contacting DJI. Since github is not our main consultation channel, your questions may not be followed up in time. We recommend that you fill in the form (https://djisdksupport.zendesk.com/hc/en-us/requests/new) to report problems to us. Or you can post in the forum to communicate with other developers. Forum link: https://djisdksupport.zendesk.com/hc/zh-cn/community/topics

°°°

dji-dev commented 2 years ago

Agent comment from kyle.cai in Zendesk ticket #56781:

Dear developer ,

Thank you for contacting DJI.

Sorry for the inconvenience. We have noticed this problem. After upgrading the new firmware, download only works with less than 3 photos in the SD card. We have submitted this bug and we will fix it later. If this function blocks your development, it is recommended to temporarily roll back the version.

Thank you for your understanding and support, hope you have a nice day.

Best Regards, DJI SDK Support Team

°°°

vdebad commented 2 years ago

A temporary fix is to comment checking of frame-length in dji_file_mgr_impl.cppin function parseFileList line 596 to 599:

Comment this code block:

  if (listdata->len > fullDataList.size() * maxDataFrameLen) {
    DERROR("File list total data len error !");
    return pack;
  }

Please beware: I know file listing is working afterwards, but I cannot guarantee, that this fix does not break any other features, so be careful!