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:
EdsObjectEventHandler
in order to modify the "save-image-behavior"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?
I can think of two reasons it doesn't work:
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.
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);