The Smallest Things…

For the last few weeks I’ve been banging my head against a problem. I need my Photo Booth application to actually take a photo. It seems like such a simple thing, but it has actually been one of the most difficult ones I’ve encountered. Like the Great PostScript Debacle and the Mystery of the GAsyncQueue Ref before it, I spent a good week banging my head against the wall. I even took a day off to write an entire Lua Camera Module just so I could shell out and call a command line utility to try to work around it.

The best part? If you’ve been following the blog, you know I’m kind of a crybaby about poor documentation. While libgphoto2 is certainly a repeat offender on this count, no amount of documentation could have prepared me for what was to come.

But first, let’s go over my now-working implementation.

Taking A Picture With Libgphoto2

To take a picture, we need 3 functions:

  • gint dmp_cm_camera_init()
  • gint dmp_cm_camera_finalize()
  • gint dmp_cm_camera_capture(gchar * location)

dmp_cm_camera_init

gint dmp_cm_camera_init() { context = gp_context_new(); gp_log_add_func(GP_LOG_ERROR, (GPLogFunc) dmp_cm_log_func, NULL); if (gp_camera_new(&camera) != GP_OK) { //error handling } if (gp_camera_init(camera, context) != GP_OK) { //error handling } return DMP_PB_SUCCESS; }

There are two main structs: Camera and GPContext. A Camera represents, shockingly, a camera attached to the system. A GPContext represents work to be done. Callback functions, data, and other things of that nature.

First we create a new context. Next we can add a log function to accept log messages from libgphoto2. In my experience, no matter what you do you will get a lot of useless garbage output from libgphoto2. For this reason, I recommend you don’t just let this spew to the console or some other user-facing output. At first, I was going to send this to the console queue, but I’ve since decided against using this feature. It is good to know about though in case you need it for troubleshooting.

After all of that is done, we need to create our camera object, and initialize libgphoto2.

dmp_cm_camera_finalize

gint dmp_cm_camera_finalize() { gp_camera_unref(camera); gp_context_unref(context); return DMP_PB_SUCCESS; }

Nothing particularly tricky there. We need to ensure we free our memory when we’re done, so we unref our camera and context. Having seen these two functions, you may be wondering to yourself: “Are we dealing with GObjects here?” Luckily for us, there is a simple test for this:

g_assert(G_IS_OBJECT(camera));

I’ll spare you the effort of running this test: the assertion fails. Too bad really, but it is what it is. Libgphoto2 just uses function names similar to GObject.

dmp_cm_camera_capture

gint dmp_cm_camera_capture(gchar * location) { CameraFile * file; CameraFilePath camera_file_path; gint fd; CameraEventType event_type; void * event_data; if (gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context) != GP_OK) { //error handling } if ((fd = g_open(location, O_CREAT | O_WRONLY, 0644)) == -1) { //error handling } do { gp_camera_wait_for_event(camera, 1000, &event_type, &event_data, context); if (event_type == GP_EVENT_CAPTURE_COMPLETE) break; } while(event_type != GP_EVENT_TIMEOUT); if (gp_file_new_from_fd(&file, fd) != GP_OK) { //error handling } do { gp_camera_wait_for_event(camera, 1000, &event_type, &event_data, context); } while(event_type != GP_EVENT_TIMEOUT); if (gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name, GP_FILE_TYPE_NORMAL, file, context) != GP_OK) { //error handling } if (gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name, context) != GP_OK) { //error handling } gp_file_free(file); do { gp_camera_wait_for_event(camera, 1000, &event_type, &event_data, context); } while(event_type != GP_EVENT_TIMEOUT); return DMP_PB_SUCCESS; }

This function is where the meat of the process is. First we need to do some housekeeping. We create a CameraFile pointer to represent the actual image file, and a CameraFilePath struct to represent the path to the file. We also create an int for use as a file descriptor, a CameraEventType and void pointer for our calls to gp_cmaera_wait_for_event

Next we call gp_camera_capture which triggers the camera to take a picture. After that is done, we’ll open a file descriptor to save the image. You’ll notice that the call to g_open is enclosed in parentheses. THIS STEP IS 100% MANDATORY Don’t omit it, you’ll be sorry. More on this in a bit.

Next, we wait for the camera to finish working. The camera uses an event system; it will emit events when things happen. After releasing the shutter, the camera has other work to do before it is “done taking the picture”. If you try to do the next step before the camera is ready libgphoto2 will spew garbage to your STDOUT and you’ll have to ctrl+c to fix it. To avoid this, we call gp_camera_wait_for_event while event_type != GP_EVENT_TIMEOUT || GP_EVENT_CAPTURE_COMPLETE Capture complete is obviously the event we care about, but it may have happened while we weren’t listening for it. In that case, we’ll settle for a timeout.

Next up is instantiating our CameraFile. We use our File descriptor that we just opened to call gp_file_new_from_fd. Unfortunately there is no gp_file_new_from_file_pointer which means that this call is POSIX only, and there’s no portable substitute.

After creating our CameraFile we download the image we just took by calling gp_camera_file_get and then delete the file from the camera using gp_camera_file_delete

Finally we make sure no events are pending, then return.

Why Are You Yelling At Me?

Good question. The block in question of course is

if ((fd = g_open(location, O_CREAT | O_WRONLY, 0644)) == -1) { //error handling }

Inside of that if block, I’m assigning a value and testing the result inside of the if statement. This operation is about a 2 out of 10 on the cleverness scale. Normally, you could omit the parentheses around (fd = g_open(location, O_CREAT | O_WRONLY, 0644). However, if we do it here, things go off the rails. Not right away, of course, but a few function calls later we get to:

if (gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name, GP_FILE_TYPE_NORMAL, file, context) != GP_OK) { //error handling }

As soon as gp_camera_file_get(...) is evaluated, this is spewed to the console:

mystic_runes

…and you have no choice but to kill the process.

Why does this happen? I have no idea. Why does enclosing the call to g_open in parenthesis fix it? Again, no idea. And it only happens here too. I just tried to modify the examples that come with libgphoto2 to reproduce the error and get that screenshot for this post, but it works fine there. Knowing my luck, if you download and build the program, it’ll work fine for you.

As long as it works, I guess…

One response to “The Smallest Things…”

  1. nexttonone says :

    On the parenthesis –it is priority of operations, merely, I suspect :)

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: