Here is some code showing how to use
libgphoto2 to do some tethered capturing using a DSLR camera connected to the computer's USB port, since I haven't seen much documentation on how to do this, and the samples from libgphoto2 don't work as-is if you want to do continuous capturing.
This code is mainly based on the
lunkwill-canon-capture.c example included with libgphoto2, but it has been adapted for continuous shooting by looking at the
gphoto2 code.
This code has been tested with a Nikon D700, but any camera with
tethered shooting support for libgphoto2 should work. Note that if you are using a Canon camera, you may have to enable the Canon-specific capture mode using the
canon_enable_capture function found in the
config.c file in the examples included with libgphoto2.
I'll just be showing how to capture pictures, so in case you want to use some of the other functions, like read or modify camera settings, please refer to the examples included with libgphoto2.
Also, please notice that for a lot of purposes it's easier to interface your code with gphoto2 through command line parameters or running the gphoto2 shell (with
gphoto2 --shell) and sending commands through standard input. The libgphoto2 approach gives you more control, but it's also easier to mess up and leave the camera in an inconsistent state that will require unplugging and plugging it again.
Let's start with includes and globals:
#include <stdio.h>
#include <fcntl.h>
#include <gphoto2/gphoto2-camera.h>
Camera *camera;
GPContext *context;
The pointers
camera and
context are used by libgphoto2 to interact with the camera.
Now we are going to define the callback functions that libgphoto2 is going to call when there's an error with the capturing or it has something to tell us. They are mandatory, and failing to register them will results in errors being thrown when we try to initialize the camera. We'll use them to just print their respective messages:
void error_func (GPContext *context, const char *format, va_list args, void *data) {
fprintf (stderr, "*** Contexterror ***\n");
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
}
void message_func (GPContext *context, const char *format, va_list args, void *data) {
vprintf (format, args);
printf ("\n");
}
The next thing to do will be to define our main function. At the beginning we will just initialize the camera and context:
int main (int argc, char *argv[]) {
gp_camera_new (&camera);
context = gp_context_new();
Next step will be to register the callbacks we defined previously:
// set callbacks for camera messages
gp_context_set_error_func(context, error_func, NULL);
gp_context_set_message_func(context, message_func, NULL);
Next we will detect cameras and choose the first one. In case you want to be fancier and let the user choose a different camera, check the
sample-multi-detect.c file in libgphoto2 examples.
//This call will autodetect cameras, take the first one from the list and use it
printf("Camera init. Can take more than 10 seconds depending on the "
"memory card's contents (remove card from camera to speed up).\n");
int ret = gp_camera_init(camera, context);
if (ret < GP_OK) {
printf("No camera auto detected.\n");
gp_camera_free(camera);
return 1;
}
Now we are ready to do some capturing. As an example of continuous capturing I just wrote a loop that captures ten shots as fast as possible. For your application you may want to pause for a certain time, or react to some user input, network buffer, etc.
// take 10 shots
char filename[256];
int const nShots = 10;
int i;
// do some capturing
for (i = 0; i < nShots; i++) {
snprintf(filename, 256, "shot-%04d.nef", i);
printf("Capturing to file %s\n", filename);
capture(filename);
}
This will generate consecutive file names for all the shots and do the capturing. The capture function contains all the juicy stuff, but we'll go to it in a while. Before that we'll finish writing the main function by closing the camera:
// close camera
gp_camera_unref(camera);
gp_context_unref(context);
return 0;
}
The capture function contains all the code necessary to take a shot and copy the result image to the computer's file system, while avoiding saturating the camera by sending it requests before it's done doing its work. First we'll define the variables needed.
int capture (const char *filename) {
int fd, retval;
CameraFile *file;
CameraFilePath camera_file_path;
// this was done in the libphoto2 example code, but doesn't seem to be necessary
// NOP: This gets overridden in the library to /capt0000.jpg
//snprintf(camera_file_path.folder, 1024, "/");
//snprintf(camera_file_path.name, 128, "foo.jpg");
fd will be the file descriptor we use to write to disk.
retval will hold the return value of libgphoto2 calls so we can check for errors.
file will hold a reference to the picture in the camera's file system, and
camera_file_path will tell us the path of the captured picture in the camera's file system (necessary to retrieve the captured picture).
Next we'll capture a picture:
// take a shot
retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
if (retval) {
// do some error handling, probably return from this function
}
printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
After running
gp_camera_capture, the
camera_file_path variable has been written. Next we'll open the file in the computer's file system and create the CameraFile object:
fd = open(filename, O_CREAT | O_WRONLY, 0644);
// create new CameraFile object from a file descriptor
retval = gp_file_new_from_fd(&file, fd);
if (retval) {
// error handling
}
Next we'll retrieve the picture from the camera and write it to disk:
// copy picture from camera
retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
GP_FILE_TYPE_NORMAL, file, context);
if (retval) {
// error handling
}
And finally remove the picture from the camera's file system and free the CameraFile object:
printf("Deleting\n");
// remove picture from camera memory
retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
context);
if (retval) {
// error handling
}
// free CameraFile object
gp_file_free(file);
Now, before we can return from the function and risk it being called again while the camera is still busy, we'll wait for events from the camera until we receive
GP_EVENT_CAPTURE_COMPLETE and there are no more events to process. This probably could be done more efficiently, since the program could be doing something else while waiting for events.
printf("Wait for events from camera\n");
while(1) {
retval = gp_camera_wait_for_event(camera, waittime, &type, &data, context);
if(type == GP_EVENT_TIMEOUT) {
break;
}
else if (type == GP_EVENT_CAPTURE_COMPLETE) {
printf("Capture completed\n");
waittime = 100;
}
else if (type != GP_EVENT_UNKNOWN) {
printf("Unexpected event received from camera: %d\n", (int)type);
}
}
return 0;
}
This is it. This should be all you need to do to tethered capturing using libgphoto2. I hope this is useful to somebody. Below I'll paste the entire source code. To compile it using gcc you can run:
gcc -o tethered tethered.c -lgphoto2
You will need the development files of libgphoto. In Ubuntu you can install them by running:
sudo apt-get install libgphoto2-2-dev
Here is the complete source code:
#include <stdio.h>
#include <fcntl.h>
#include <gphoto2/gphoto2-camera.h>
Camera *camera;
GPContext *context;
void error_func (GPContext *context, const char *format, va_list args, void *data) {
fprintf(stderr, "*** Contexterror ***\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
}
void message_func (GPContext *context, const char *format, va_list args, void *data) {
vprintf(format, args);
printf("\n");
}
int capture (const char *filename) {
int fd, retval;
CameraFile *file;
CameraFilePath camera_file_path;
// this was done in the libphoto2 example code, but doesn't seem to be necessary
// NOP: This gets overridden in the library to /capt0000.jpg
//snprintf(camera_file_path.folder, 1024, "/");
//snprintf(camera_file_path.name, 128, "foo.jpg");
// take a shot
retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
if (retval) {
// do some error handling, probably return from this function
}
printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
fd = open(filename, O_CREAT | O_WRONLY, 0644);
// create new CameraFile object from a file descriptor
retval = gp_file_new_from_fd(&file, fd);
if (retval) {
// error handling
}
// copy picture from camera
retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
GP_FILE_TYPE_NORMAL, file, context);
if (retval) {
// error handling
}
printf("Deleting\n");
// remove picture from camera memory
retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
context);
if (retval) {
// error handling
}
// free CameraFile object
gp_file_free(file);
// Code from here waits for camera to complete everything.
// Trying to take two successive captures without waiting
// will result in the camera getting randomly stuck.
int waittime = 2000;
CameraEventType type;
void *data;
printf("Wait for events from camera\n");
while(1) {
retval = gp_camera_wait_for_event(camera, waittime, &type, &data, context);
if(type == GP_EVENT_TIMEOUT) {
break;
}
else if (type == GP_EVENT_CAPTURE_COMPLETE) {
printf("Capture completed\n");
waittime = 100;
}
else if (type != GP_EVENT_UNKNOWN) {
printf("Unexpected event received from camera: %d\n", (int)type);
}
}
return 0;
}
int main (int argc, char *argv[]) {
gp_camera_new (&camera);
context = gp_context_new();
// set callbacks for camera messages
gp_context_set_error_func(context, error_func, NULL);
gp_context_set_message_func(context, message_func, NULL);
//This call will autodetect cameras, take the first one from the list and use it
printf("Camera init. Can take more than 10 seconds depending on the "
"memory card's contents (remove card from camera to speed up).\n");
int ret = gp_camera_init(camera, context);
if (ret < GP_OK) {
printf("No camera auto detected.\n");
gp_camera_free(camera);
return 1;
}
// take 10 shots
char filename[256];
int const nShots = 10;
int i;
// do some capturing
for (i = 0; i < nShots; i++) {
snprintf(filename, 256, "shot-%04d.nef", i);
printf("Capturing to file %s\n", filename);
capture(filename);
}
// close camera
gp_camera_unref(camera);
gp_context_unref(context);
return 0;
}