c++cameraremote-controledsdkcanon-sdk

Canon EDSDK 13.11.10 not saving to host PC


I'm in the process of implementing a remote camera control app with Canons EDSDK. My camera is a Canon PowerShot SX 70 HS. So far everything seems to work, except for the functionality to save the taken picture to the host-PC.

My understanding of the needed workflow is the following:

  1. Initialize camera
  2. Rewrite object event handler and pass to EdsObjectEventHandler in order to modify the "save-image-behavior"
  3. Take photo
  4. Close streams and destroy all objects.

I have adapted my own initial version and tried out the solutions provided in these threads:

canon EDSDK saving image in my PC

Canon SDK - Downloading image to host PC

Below is the code...

...for the first thread:

#include <iostream>

#include "EDSDK.h"
#include "EDSDKTypes.h"
#include "EDSDKErrors.h"

EdsError getFirstCamera(EdsCameraRef* camera);
EdsError downloadImage(EdsDirectoryItemRef directoryItem);
EdsError EDSCALLBACK handleStateEvent(EdsStateEvent event, EdsUInt32 parameter, EdsVoid* context);
EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context);
EdsError EDSCALLBACK handlePropertyEvent(EdsPropertyEvent event, EdsPropertyID property, EdsUInt32 inParam, EdsVoid* context);


int main(int argc, char** argv)
{
    EdsError err;
    EdsCameraRef camera = NULL;
    bool isSDKLoaded = false;
    EdsCapacity capacity = { 0x7FFFFFFF, 0x1000, 1 };
    EdsInt32 saveTarget = kEdsSaveTo_Host;

    // Initialize SDK
    err = EdsInitializeSDK();
    if (err == EDS_ERR_OK)
    {
        isSDKLoaded = true;
    }

    // Get first camera
    if (err == EDS_ERR_OK)
    {
        err = getFirstCamera(&camera);
    }


    // Open session with camera
    err = EdsOpenSession(camera);

    // Set event handler
    if (err == EDS_ERR_OK)
        err = EdsSetObjectEventHandler(camera, kEdsObjectEvent_All, handleObjectEvent, NULL);
    
    if (err == EDS_ERR_OK)
        err = EdsSetPropertyEventHandler(camera, kEdsPropertyEvent_All, handlePropertyEvent, NULL);
    
    if (err == EDS_ERR_OK)
        err = EdsSetCameraStateEventHandler(camera, kEdsStateEvent_All, handleStateEvent, NULL);

    err = EdsSetPropertyData(camera, kEdsPropID_SaveTo, 0, 4, &saveTarget);

    err = EdsSetCapacity(camera, capacity);

    ///// Take picture
    err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    ////

    // Close session with camera
    if (err == EDS_ERR_OK)
    {
        err = EdsCloseSession(camera);
    }

    // Release camera
    if (camera != NULL)
    {
        EdsRelease(camera);
    }

    // Terminate SDK
    if (isSDKLoaded)
    {
        EdsTerminateSDK();
    }
}


EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context)
{
    std::cout << "I'm in the callback-function!!!\n"; // Never shows!
    EdsError err = EDS_ERR_OK;

    switch (event)
    {
    case kEdsObjectEvent_DirItemRequestTransfer:
        err = downloadImage(object);
        break;
    default:
        break;
    }

    // Object must be released
    if (object) {
        err = EdsRelease(object);
    }
    return err;
}

EdsError EDSCALLBACK handlePropertyEvent(EdsPropertyEvent event, EdsPropertyID property, EdsUInt32 inParam, EdsVoid* context)
{
    return EDS_ERR_OK;
}

EdsError EDSCALLBACK handleStateEvent(EdsStateEvent event, EdsUInt32 parameter, EdsVoid* context)
{
    return EDS_ERR_OK;
}


EdsError getFirstCamera(EdsCameraRef* camera)
{
    EdsError err;
    EdsCameraListRef cameraList = NULL;

    err = EdsGetCameraList(&cameraList);
    err = EdsGetChildAtIndex(cameraList, 0, camera);
    return err;
}

EdsError downloadImage(EdsDirectoryItemRef directoryItem)
{
    EdsError err = EDS_ERR_OK;
    EdsStreamRef stream = NULL;
    // Get directory item information
    EdsDirectoryItemInfo dirItemInfo;
    err = EdsGetDirectoryItemInfo(directoryItem, &dirItemInfo);

    // Create file stream for transfer destination
    if (err == EDS_ERR_OK)
    {
        err = EdsCreateFileStream(dirItemInfo.szFileName, kEdsFileCreateDisposition_CreateAlways, kEdsAccess_ReadWrite, &stream);
    }
    // Download image
    if (err == EDS_ERR_OK)
    {
        err = EdsDownload(directoryItem, dirItemInfo.size, stream);
    }
    // Issue notification that download is complete
    if (err == EDS_ERR_OK)
    {
        err = EdsDownloadComplete(directoryItem);
    }
    // Release stream
    if (stream != NULL)
    {
        EdsRelease(stream);
        stream = NULL;
    }
    return err;
}

and for the second thread:

#include <iostream>

#include "EDSDK.h"
#include "EDSDKTypes.h"
#include "EDSDKErrors.h"

static EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context)
{
    std::cout << "I'm in the callback-function!!!\n";  // NEVER PRINTS!
    EdsError err = EDS_ERR_OK;
    if (event == kEdsObjectEvent_DirItemRequestTransfer)
    {
        EdsStreamRef stream = NULL;
        EdsDirectoryItemInfo dirItemInfo;
        err = EdsGetDirectoryItemInfo(object, &dirItemInfo);
        err = EdsCreateFileStream(dirItemInfo.szFileName, kEdsFileCreateDisposition_CreateAlways, kEdsAccess_ReadWrite, &stream);
        err = EdsDownload(object, dirItemInfo.size, stream);
        err = EdsDownloadComplete(object);
        EdsRelease(stream);
        stream = NULL;
    }
    if (object)
        EdsRelease(object);

    return err;
}

void TakePhoto(EdsCameraRef camera)
{
    EdsError err = EDS_ERR_OK;
    EdsCameraListRef cameraList = NULL;
    EdsUInt32 count = 0;

    err = EdsInitializeSDK();
    err = EdsGetCameraList(&cameraList);
    err = EdsGetChildCount(cameraList, &count);
    if (count > 0)
    {
        err = EdsGetChildAtIndex(cameraList, 0, &camera);
        cameraList = NULL;
        err = EdsSetObjectEventHandler(camera, kEdsObjectEvent_All, handleObjectEvent, NULL);
        err = EdsOpenSession(camera);
        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    }
}

void Close(EdsCameraRef camera)
{
    EdsError err = EdsCloseSession(camera);
    EdsRelease(camera);
    EdsTerminateSDK();
}

int main() {
    EdsCameraRef camera = NULL;
    TakePhoto(camera);
    Close(camera);
    return 0;
}

However, as mentioned before: The images are nowhere to find on my host PC. This doesn't change, if i provide a full path to EdsCreateFileStream. The default behavior of saving the image in the directory in which the binary resides (using dirItemInfo.szFileName as the first argument to EdsCreateFileStream also doesn't work.

It could be, that that's because the newest solution i tried is from 2014 and the API changed in the meantime. I also looked for implementations on GitHub and looked in the official manual, but both were very opaque to me.

What really puzzles me is, that when i tried out a printf-style debugging (adding an output inside the handleObjectEvent, the print never showed up! Is this because of the callback-nature of the function? I must confess, that i haven't worked with Callbacks before.

All the versions provided below build and execute. Also i can here the camera clicking, but as stated before: I can't find the image on disk!

Are the callbacks even executed? Why does the print never show then? How can i ultimately modify the programs to save images to disk?


Solution

  • I can think of two reasons it doesn't work:

    1. this is a console application so it doesn't have a message pump. This means events aren't fired "automatically". You'll have to call EdsGetEvent repeatedly until you have received the event you need. In your case the ideal point would be after sending the TakePicture command.

    2. After sending the TakePicture command you immediately close down everything so there is no time for the event to fire. The TakePicture command returns pretty much immediately and doesn't wait until the whole photo-taking procedure is done (it does wait until focusing is done though). Luckily, this is also fixed with the EdsGetEvent loop I described above.

    So esentially:

        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    
        // eventHasFired is a boolean variable you can
        // set to true after you have downloaded the image.
        // or use a more sophisticated synchronization approach if necessary
        while (!eventHasFired)
        {
            EdsGetEvent();
            // you could add a little pause here to reduce CPU usage
        }
    
        if (err == EDS_ERR_OK)
        {
            err = EdsCloseSession(camera);
        }
    

    EDIT

    After discussing some possibilities in the comments and looking at the error-codes from the EDSDK we figured out, that the camera isn't able to autofocus when sending TakePicture the first time. Sending the command repeatedly in a while-loop provides a workaround:

    while (err != EDS_ERR_OK)
        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);