Skip to main content

What's the best way to handle the Android camera?

Bothie makes extensive use
of the Camera APIs.
This article is adapted from a response I gave on Reddit to the question "What's the best way to handle the camera?" in r/androiddev...

The Camera and Camera2 APIs are far from painless to use. If you've ever written an app that uses the camera (embedded in an activity or not), you've almost certainly come up against orientation issues, stretched previews, or weird quirks that change from manufacturer to manufacturer...

There are three good libraries out there that can save you from many common pitfalls:
Each has different strengths. If you don't have time to read this whole article, here's a quick rule of thumb:
  • If you want to capture photos in a full-screen preview, but you don't want to have to rely on the native camera app, then use CWAC-Cam2.
  • If you want to embed a preview into your Activity, use CameraKit. It's forked from the CameraView library, and offers a few more features.
The CWAC-Cam2 library excels at helping you take a photo (or video) using a fullscreen preview, without leaving your app - which can help when you wish to keep the image away from the public storage on the device. It responds to Intents that are constructed in a similar fashion to standard Camera request Intents.

The original CWAC-Cam was superb at coping with the old Camera API and the hoops you had to jump through to outwit the (sometimes bizarre) quirks introduced by each manufacturer. When the Camera2 API came out, Mark released CWAC-Cam2 to support it - and the library is steadily improving. It supports a variety of features, including flash modes, focus modes, and orientation normalization. Image orientation is a source of frustration for many Android developers who work with the camera. More on that later...

The CameraView library is clean, tidy and works for most simple cases. It is limited to still photography at present. You can embed a CameraView into your layout, and use it there - with only a small amount of supporting code in your Activity class. The library takes care of the proportions of the view, to prevent a stretched preview, and orients it correctly too. (the Camera2 API is supposed to "just take care of that").

CameraKit was originally forked from CameraView and offers a few more features - including tap-to-focus, cropped output, and video capture.

There are a couple of issues with the CameraView library that will very likely also affect CameraKit. These are interesting edge cases that relate to doing things like:
  • Stopping the CameraView and attempting to close the Activity. (This leads to the app crashing sometimes - as it seems that the CameraView needs time to close the Camera it's using safely.)
  • Stopping one CameraView and starting another in quick succession. (This also sometimes leads to the app crashing, and seems to be again because the first Camera isn't released as the second CameraView tries to start - and so it cannot claim a Camera of its own.)
These are pretty rare edge cases, but you should be aware of them if you're doing anything more complex than a simple preview inside an Activity. My workaround was to .postDelayed a Runnable to my CameraView that took the actions I wanted after the Camera was safely closed.

All three libraries are well maintained - although they're constantly playing catch-up, as each manufacturer manages to introduce some new utterly strange behaviour to their own camera. (Who remembers trying to understand why making a photo size request different to the preview size led to washed out images with no white-balance? Or which of the permitted preview sizes didn't lead to green static on certain phones?)

Make sure to grab a recent version of the library you choose when building your app, and update regularly.

A word of warning about orientation: When actually taking a photo, CameraView (and CameraKit) will return it oriented according to how the camera chip in the phone provided it - not how the preview was arranged. This can be confusing until you realise what's going on. You'll have to reorient the image yourself to get back on the road.

Your choices are:
  1. Read the orientation information from the image's EXIF data. Unfortunately, this is not always available. (In fact, on most phones I've worked with I don't remember being able to get that data.)
  2. Guess based on the orientation of the device itself when the photo was taken. This approach appears quite frequently in examples found online, and seems to be the preferred choice amongst developers.
  3. Fall back on educated guesswork. Take a look at the image in question - is it wider or taller than the preview's dimensions? If so, perhaps you should rotate it 90 degrees (or -90 degrees).
See this ImageOrientationHelper.java gist for some methods to help you read the EXIF data in your image and rotate a bitmap accordingly. It relies on Drew Noakes' metadata-extractor library to read the tags from your image. To add it to your project using gradle, you can use the following formula in your module's build.gradle file:

    compile 'com.drewnoakes:metadata-extractor:+'

One final word: You may also find that photos taken through the front-facing camera are flipped around the vertical axis (left and right don't match the preview!) To flip it back, you could adjust the Matrix you're using: matrix.postScale(-1.0f, 1.0f); should mirror the image for you, although you'll also have to apply a matrix.postTranslate(imageWidth, 0.0f); to put it back to the same position.

I hope that's helpful!

Popular posts from this blog

Google Play Services with Android Studio

Edit: This post is extremely deprecated -- with prejudice! It was written in an era when Google Play Services were not well integrated with Android Studio project work, and Android Studio itself was in its infancy.

This is a very quick guide to incorporating Google Play Services with your new Android Studio project.

Edit: [16:20 22/05/2013] I'll investigate the runtime NoClassDefFound error reported in the comments, and follow up later!

Edit: [23:17 27/05/2013] I'm coming to the conclusion that - as many have already pointed out - you really do need to include the entire library project in your solution. I'll post an update once I've fully tested this. 

In the meantime, please consider the advice below to be deprecated!

The first thing to say is: I fully expect the advice and guidance about how to work in Android Studio to change over time. Android Studio is in early access preview right now, and I'll bet my bottom dollar (is that a thing?) that over time it becomes m…

Wriggler

It was 1998 and I was 17. My tool of choice was QBasic and this is a game I wrote based on a concept I stole from another game called Wriggler.

The original Wriggler is a race game through a maze of bugs and creepy crawlies - played against the computer. My game would have been a race, had I gotten around to writing the computer player.

Instead it pits you, a plucky young worm (comprised of 4 lines and a blob), against an army of anatomically incorrect spiders in your mission to see a duck and solve a single puzzle. Also there are some chocolate bars.

I fired it up once again to make a playthrough video. The game features some pretty old-school beep/boop sound effects, which really hit me right in the nostalgias!

Warning, this playthrough video contains spoilers for the puzzle... 😂


I don't suppose there's much to be learned from my code, but you can see it all on Github if that's your groove and you're welcome to have a tinker. I had some success running it with DOSBox