Ilias Tsagklis

About Ilias Tsagklis

Ilias Tsagklis is a senior software engineer working in the telecom domain. He is an applications developer in a wide variety of applications/services. Ilias is co-founder and Executive Editor at Java Code Geeks.

Android HTTP Camera Live Preview Tutorial

The Android SDK includes a Camera class that is used to set image capture settings, start/stop preview, snap pictures, and retrieve frames for encoding for video. It is a handy abstraction of the real hardware device. However, the SDK doesn’t provide any camera emulation, something that makes testing camera enabled applications quite difficult. In this tutorial, I am going to show you how to use a web camera in order to obtain a live camera preview.

What we are essentially going to do is setup a web camera to publish static images in a predefined URL and then grab these images from our application and present them as motion pictures. Note that this tutorial was inspired by a post named “Live Camera Previews in Android”.

Let’s start by creating an Eclipse project under the name “AndroidHttpCameraProject” and an Activity named “MyCamAppActivity”. As a first step, I am going to show you how to use the SDK camera. You can find many resources on how to manipulate the camera, one of them being the CameraPreview class that is included in the SDK’s samples. In short, we are going to extend the SurfaceView class and provide our implementation using the device’s camera. Then, we use the setContentView method of our Activity in order to set the activity content to that specific View. Let’s see what the two classes look like:

MyCamAppActivity

package com.javacodegeeks.android.camera;

import android.app.Activity;
import android.os.Bundle;
import android.view.SurfaceView;
import android.view.Window;

public class MyCamAppActivity extends Activity {
    
    private SurfaceView cameraPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        cameraPreview = new CameraPreview(this);
        
        setContentView(cameraPreview);
        
    }
    
}

CameraPreview

package com.javacodegeeks.android.camera;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder holder;
    private Camera camera;

    public CameraPreview(Context context) {
        super(context);
        holder = getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder2, int format, int w, int h) {
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(w, h);
        camera.setParameters(parameters);
        camera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder1) {
        try {
            camera = Camera.open();
            camera.setPreviewDisplay(holder1);
        } 
        catch (Exception e) {
            Log.i("Exception surfaceCreated()", "e=" + e);
            camera.release();
            camera = null;
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }
    
}

In our “CameraPreview” class, we implement the SurfaceHolder.Callback interface so that we receive information about changes to the underlying surface. The methods that have to be implemented are the following:

  • surfaceCreated: Called immediately after the surface is first created.
  • surfaceChanged: Called immediately after any structural changes (format or size) have been made to the surface.
  • surfaceDestroyed: Called immediately before a surface is being destroyed.

To use the Camera class, no constructor is available, but we rather use the open method which creates a new Camera object to access the first back-facing camera on the device. Then, we use the setPreviewDisplay method to set the Surface to be used for the live preview. Whenever a change occurs, we call the startPreview method in order to start capturing and drawing preview frames to the screen, after we have set the appropriate preview size (setPreviewSize) at the Camera.Parameters.

If we now launch the Eclipse configuration, we will come across a totally not-helpful image, as follows:

Now we are going to replace that surface view with a custom one that use static images from a web camera. First, let’s see the changes to our activity. The new version is the following:

MyCamAppActivity

package com.javacodegeeks.android.camera;

import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.view.SurfaceView;
import android.view.Window;

public class MyCamAppActivity extends Activity {
    
    private int viewWidth;
    private int viewHeight;
    
    private SurfaceView cameraPreview;
    
    private static final boolean useHttpCamera = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        calculateDisplayDimensions();
        
        if (useHttpCamera) {
            cameraPreview = new HttpCameraPreview(this, viewWidth, viewHeight);
        }
        else {
            cameraPreview = new CameraPreview(this);
        }
        setContentView(cameraPreview);
        
    }
    
    private void calculateDisplayDimensions() {
        Display display = getWindowManager().getDefaultDisplay();
        viewWidth = display.getWidth();
        viewHeight = display.getHeight();
    }    
    
}

We have added a boolean variable to define which version we want to use, the one with the “real” camera or the one that uses the web cam. The latter, uses a new class named “HttpCameraPreview” which also extends the SurfaceView class. This one requires to know the width and the height of the enclosed view, thus we use the WindowManager class to get the default Display and from that we calculate the dimensions.

Here is what the new class looks like:

HttpCameraPreview

package com.javacodegeeks.android.camera;

import android.content.Context;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class HttpCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    
    private static final String url = "http://10.0.2.2:8080";
    
    private CanvasThread canvasThread;
    
    private SurfaceHolder holder;
    private HttpCamera camera;
    
    private int viewWidth;
    private int viewHeight;

    public HttpCameraPreview(Context context, int viewWidth, int viewHeight) {
        super(context);
        holder = getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
        this.viewWidth = viewWidth;
        this.viewHeight = viewHeight;
        canvasThread = new CanvasThread();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder2, int format, int w, int h) {        
        try {            
            Canvas c = holder.lockCanvas(null);
            camera.captureAndDraw(c);
            
            if (c != null) {
                holder.unlockCanvasAndPost(c);
            }            
        } 
        catch (Exception e) {
            Log.e(getClass().getSimpleName(), "Error when surface changed", e);
            camera = null;
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        try {
            camera = new HttpCamera(url, viewWidth, viewHeight, true);
            canvasThread.setRunning(true);
            canvasThread.start();
        } 
        catch (Exception e) {
            Log.e(getClass().getSimpleName(), "Error while creating surface", e);
            camera = null;
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        camera = null;
        boolean retry = true;
        canvasThread.setRunning(false);
        while (retry) {
            try {
                canvasThread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }

    }
    
    private class CanvasThread extends Thread {
        
        private boolean running;
        
        public void setRunning(boolean running){
            this.running = running;
        }
        
        public void run() {
            
           while (running) {               
               Canvas c = null;
               try {
                   c = holder.lockCanvas(null);           
                   synchronized (holder) {
                       camera.captureAndDraw(c);
                   }
               }
               catch (Exception e) {
                   Log.e(getClass().getSimpleName(), "Error while drawing canvas", e);
               }
               finally {
                   if (c != null) {
                       holder.unlockCanvasAndPost(c);
                   }
               }
           }
        }
        
    }

}

In the constructor, we start a thread that will be responsible for updating the Canvas of the surface view, which we will describe in a while. We implement again the SurfaceHolder.Callback interface and the three methods describer above. Note that a custom “HttpCamera” object is used, which captures the still images and draws them to the Canvas. The lockCanvas method of the SurfaceHolder is used in order draw into the surface’s bitmap and the unlockCanvasAndPost method is invoked after the drawing has completed. The “HttpCamera” object uses a URL which is where the images get published. Note that the IP address is the 10.0.2.2, which actually is the machine hosting the Android emulator. We can not use the classic localhost address (127.0.0.1), since this points to the emulator itself. In the thread class, we have a loop in which we continuously draw on the underlying canvas. We use a boolean variable as a flag to indicate whether the process should continue or not.

Let’s now see what this “HttpCamera” class is all about:

HttpCamera

package com.javacodegeeks.android.camera;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;

public class HttpCamera {
    
    private static final int CONNECT_TIMEOUT = 1000;
    private static final int SOCKET_TIMEOUT = 1000;
    
    private final String url;
    private final Rect bounds;
    private final boolean preserveAspectRatio;
    private final Paint paint = new Paint();

    public HttpCamera(String url, int width, int height, boolean preserveAspectRatio) {
        this.url = url;
        bounds = new Rect(0, 0, width, height);
        this.preserveAspectRatio = preserveAspectRatio;
        
        paint.setFilterBitmap(true);
        paint.setAntiAlias(true);
    }

    private Bitmap retrieveBitmap() throws IOException {
        
        Bitmap bitmap = null;
        InputStream in = null;
        int response = -1;

        try {
            
            URL url = new URL(this.url);        
            HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
            
            httpConn.setConnectTimeout(CONNECT_TIMEOUT);
            httpConn.setReadTimeout(SOCKET_TIMEOUT);
            httpConn.setRequestMethod("GET");
            
            httpConn.connect();
            response = httpConn.getResponseCode();
            
            if (response == HttpURLConnection.HTTP_OK) {
                in = httpConn.getInputStream();
                bitmap = BitmapFactory.decodeStream(in);
            }
            
            return bitmap;
            
        } 
        catch (Exception e) {
            return null;
        }
        finally {
            if (in != null) try {
                in.close();
            } catch (IOException e) {
                /* ignore */
            }
        }
        
    }

    public boolean captureAndDraw(Canvas canvas) throws IOException {
        
        Bitmap bitmap = retrieveBitmap();
        
        if (bitmap == null) throw new IOException("Response Code: ");

        //render it to canvas, scaling if necessary
        if (bounds.right == bitmap.getWidth() && bounds.bottom == bitmap.getHeight()) {
            canvas.drawBitmap(bitmap, 0, 0, null);
        } else {
            Rect dest;
            if (preserveAspectRatio) {
                dest = new Rect(bounds);
                dest.bottom = bitmap.getHeight() * bounds.right / bitmap.getWidth();
                dest.offset(0, (bounds.bottom - dest.bottom)/2);
            } 
            else {
                dest = bounds;
            }
            canvas.drawBitmap(bitmap, null, dest, paint);
        }
        
        return true;
        
    }

}

In the constructor, we pass the URL to where the images from the camera should be found. This could be any web camera HTTP server. Additionally, we pass the view’s dimensions and whether the aspect ratio should be preserved (this should be true in most cases). In the “captureAndDraw” method, which is the public method invoked by the custom surface class, we perform HTTP GET requests to the provided URL and retrieve the images as Bitmap objects. We then adjust the dimensions of the image accordingly and draw it on the Canvas using the drawBitmap method.

Let’s now see the manifest file:

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.javacodegeeks.android.camera"
      android:versionCode="1"
      android:versionName="1.0">
      
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MyCamAppActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>   
    </application>
    
    <uses-sdk android:minSdkVersion="3" />
    
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.INTERNET" />

</manifest> 

The usual stuff here, just remember to add permissions for using the camera (android.permission.CAMERA) and creating internet connections (android.permission.INTERNET).

Before we test the application, we need to have images published on a web server. Some web cameras already provide that functionality, but in case yours does not, you can accomplish that using a pretty cool application named WebCam2000. Unfortunately, this is only for Windows machines. After you execute the program, it should automatically find your camera. If not, play a little with the options and make sure that in the “Video” menu, the “Microsoft WDM Image Capture” option is enabled. Then, make sure the “Enable Web Server” box is ticked and check that the server is working by pointing your browser to the corresponding URL:

http://localhost:8080/

If you now launch the Eclipse configuration, you will get a camera input to your emulator with the images periodically updated.

You can use this approach in order to provide a “mock camera” to your application and make it easier to test it. The Eclipse project created for this tutorial can be found here.

Enjoy and don’t forget to share!

Related Articles :
Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

3 Responses to "Android HTTP Camera Live Preview Tutorial"

  1. keith chiang says:

    hi,

    how do i edit the application such that i can view the webcam live view from my android phone?

    please email me at keithwb@gmail.com. thanks!

  2. Venkanna Babu says:

    this application  not running 

  3. Dipen says:

    Actually this project is running but is displaying blank screen and i am confused how it get images from a web cam without running an application on local machine. I don’t think this will get images just providing ip and port no.

Leave a Reply


+ 6 = eleven



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books
Get tutored by the Geeks! JCG Academy is a fact... Join Now
Hello. Add your message here.