Monday, September 10, 2012

Building a custom camera app in Android

- Rameshkumar Subramanian
Its always my craze to work on the image processing application. Infact I was thinking of developing a camera application for android mobiles which i have already done for desktop. So, let me share what?, how? did i come up with the application through this post.

Steps to follow
  • Determine camera hardware exist of not.
  • Accessing the camera.
  • Verifying camera features.
  • Creating preview class
  • Capturing the picture and save it.
  • Releasing the camera.
To access the device camera we must declare camera permission and feature in the manifest file. For instance if we need to used auto-focus feature we have to declare auto-focus feature in manifest file before to use it.

<uses-permission android:name="android.permission.CAMERA"/>
Note: If you are using camera via internet, you don't need this.
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
Before launching our camera application in the device, check whether device is having camera hardware or not. If not, we do not want to install our application. In that case we can declare our manifest file as the following below.
<uses-permission android:name="android.permission.CAMERA" />

We can also detect camera hardware in the runtime

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
Camera.Open() method to helps to access the camera.

Verifying camera features
Once we got access to a camera, we can get further information about its capabilities using the Camera.getParameters() method and checking the returned Camera.Parameters object for supported capabilities.

Creating preview class

The first thing before we snap a pic is the view of it on the screen, its the SurfacePreview class that to help view the object from camera. The following codes implement the SufraceView class and SurfaceHolder.Callback function in order to handle the destroying and the creation of views.

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder surfaceHolder;
private Camera camera;

public CameraPreview(Context context, Camera camera) {
super(context);
this.camera = camera;

// SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}

public void surfaceDestroyed(SurfaceHolder holder) {
//Here we can releasing the camera preview activity
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.

if (surfaceHolder.getSurface() == null){
// preview surface does not exist
return;
}

// stop preview before making changes
try {
cmera.stopPreview();
} catch (Exception e){
}

// set preview size and make any resize, rotate or
// reformatting changes here

// start preview with new settings
try {
camera.setPreviewDisplay(mHolder);
camera.startPreview();

} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}

Capturing Picture

After implemented a preview class and view layout to display it, we are ready to start capturing images.
In order to retrieve the images, use the Camera.takePicture() method. This method has four callback function parameters that help to handle the picture. From that parameters we must implement the Camera.PictureCallback interface to receive the data and write it into file.

private PictureCallback mPicture = new PictureCallback() {

@Override
public void onPictureTaken(byte[] data, Camera camera) {

File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " +
e.getMessage());
return;
}

try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
}

Saving Media File
In saving media files there are two standard locations we should consider as a developer.
  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) This method returns the standard, shared and recommended location for saving pictures and videos. This directory is public, so other applications can easily discover, read, change and delete files saved in this location. If your application is uninstalled by the user, media files saved to this location will not be removed.
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) This method returns a standard location for saving pictures and videos which are associated with your application. If your application is uninstalled, any files saved in this location are removed. Security is not enforced for files in this location and other applications may read, change and delete them.
Releasing the Camera

Before the application exits we have to release camera instance and also media recorder.
@Override
protected void onPause() {
super.onPause();
releaseMediaRecorder(); // if you are using MediaRecorder, release it first
releaseCamera(); // release the camera immediately on pause event
}

private void releaseMediaRecorder(){
if (mMediaRecorder != null) {
mMediaRecorder.reset(); // clear recorder configuration
mMediaRecorder.release(); // release the recorder object
mMediaRecorder = null;
mCamera.lock(); // lock camera for later use
}
}

private void releaseCamera(){
if (mCamera != null){
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
Thats how i came up with the application.