2011년 5월 21일 토요일

Android HTTP Camera Live Preview Tutorial

원문: http://www.javacodegeeks.com/2011/03/android-http-camera-live-preview.html

Android SDK에는 Camera class가 포함되어 있습니다. 이 Class는 하드웨어 Camera 디바이스를 추상화 하여 쉽게 조작할 수 있게 해줍니다. 하지만 SDK에서는 Camera enulation이 지원되지 않아 테스트해보는데 어려움이 많습니다. 본 문서에서는 Live Camera Preview 기능의 가이드를 제공합니다.

Eclipse을 이용하여 Android Project을 만듭니다.

AndroidMainfest
CAMERA, INTERNET 사용 권한이 있어야 합니다.

<?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>


MyCamAppActivity
useHttpCamera 설정에 따라 WebServer로 부터 CAM Image을 받아오는 HttpCameraPreview을 이용하거나 local Mobile기기의 Camera을 보여주는 CameraPreview을 이용하게 됩니다.
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();
}

}


HttpCameraPreview
WebCAM으로 부터 받은 Image을 보여주는 View입니다. url은 WebCAM 주소가 됩니다. 내부 Thead을 생성하여 주기적으로 새 Image을 생성합니다.

SurfaceHolder.Callback으로 부터 상속 받은 3개의 Callback Method을 구현하여야 하며 각각은 아래와 같습니다.
  • surfaceCreated: surface가 최초 생성 후 즉각 호출.
  • surfaceChanged: surface에 어떤 변화(format, size)가 있은 직후 호출
  • surfaceDestoryed: surface가 소멸(Destroyed)되기 바로 전에 호출

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);
}
}
}
}

}

}


HttpCamera
WebServer에 Connection하고 Image을 받아옵니다.
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;

}

}


CameraPreview
WebCAM과 연결하지 않을 때 로컬 Mobile의 Camera 화면을 보여주는 View 입니다.
HttpCameraPreview와 같이 3개의 Callback Method을 구현해 줍니다.
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;
}

}



본 예제를 실행하기 위해서는 Web Camera장비와 WebCam2000과 같은 Image publish Web Server가 필요합니다. 하지만 장비가 없다고 실망할 필요가 없습니다. 편법을 사용하시면 됩니다. Apache나 Tomcat 같은 Web Server을 설치하시고 아무 Image라도 Web에서 접근 가능하게 배포하십시오. 본인은 “hurukku.jsp” 이미지를 http://123.212.xxx.xxx:8080/bam/dashboard/hurukku.jpg 에 배포하였습니다. 그리고 HttpCameraPreview.java의 url을 위 주소로 바
꿔주시면 됩니다.


댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...