2016年6月22日 星期三

android-webcam


WebcamManager.java


package com.ford.openxc.webcam;

import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class WebcamManager extends Service {

private static String TAG = "WebcamManager";

private IBinder mBinder = new WebcamBinder();
private Webcam mWebcam;

public class WebcamBinder extends Binder {
    public WebcamManager getService() {
        return WebcamManager.this;
    }
}

@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service starting");

mWebcam = new NativeWebcam("/dev/video0"); //  check NativeWebcam.java
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service being destroyed");
mWebcam.stop();
}

@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service binding in response to " + intent);
return mBinder;
}

public Bitmap getFrame() {
if(!mWebcam.isAttached()) {
stopSelf();
}
return mWebcam.getFrame();
}
}

===================================================================
 NativeWebcam.java
===================================================================

package com.ford.openxc.webcam;
import java.io.File;
import android.graphics.Bitmap;
import android.util.Log;
public class NativeWebcam implements Webcam {
private static String TAG = "NativeWebcam";
private static final int DEFAULT_IMAGE_WIDTH = 640;
private static final int DEFAULT_IMAGE_HEIGHT = 480;
private Bitmap mBitmap;
private int mWidth;
private int mHeight;
private native int startCamera(String deviceName, int width, int height);
private native void processCamera();
private native boolean cameraAttached();
private native void stopCamera();
private native void loadNextFrame(Bitmap bitmap);
static {
System.loadLibrary("webcam"); // jni
}
// 建構子..........
public NativeWebcam(String deviceName, int width, int height) {
mWidth = width;
mHeight = height;
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
connect(deviceName, mWidth, mHeight);
}
// 建構子..........
public NativeWebcam(String deviceName) {
this(deviceName, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT);
}
private void connect(String deviceName, int width, int height) {
boolean deviceReady = true;
File deviceFile = new File(deviceName);
if(deviceFile.exists()) {
if(!deviceFile.canRead()) {
Log.d(TAG, "Insufficient permissions on " + deviceName +
" -- does the app have the CAMERA permission?");
deviceReady = false;
}
} else {
Log.w(TAG, deviceName + " does not exist");
deviceReady = false;
}
if(deviceReady) {
Log.i(TAG, "Preparing camera with device name " + deviceName);
startCamera(deviceName, width, height);
}
}
public Bitmap getFrame() {
loadNextFrame(mBitmap);
return mBitmap;
}
public void stop() {
stopCamera();
}
public boolean isAttached() {
return cameraAttached();
}
}



================================================================
Webcam.java
================================================================

package com.ford.openxc.webcam;
import android.graphics.Bitmap;
public interface Webcam {
public Bitmap getFrame();
public void stop();
public boolean isAttached();
}


==========================================================
WebcamPreview.java
==========================================================

package com.ford.openxc.webcam;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class WebcamPreview extends SurfaceView implements
SurfaceHolder.Callback, Runnable {
private static String TAG = "WebcamPreview";
private Rect mViewWindow;
private boolean mRunning = true;
private Object mServiceSyncToken = new Object();
private WebcamManager mWebcamManager;
private SurfaceHolder mHolder;
public WebcamPreview(Context context) {
super(context);
init();
}
public WebcamPreview(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
Log.d(TAG, "WebcamPreview constructed");
setFocusable(true);
mHolder = getHolder();
mHolder.addCallback(this);
}
@Override
public void run() {
while(mRunning) {
synchronized(mServiceSyncToken) {
if(mWebcamManager == null) {
try {
mServiceSyncToken.wait();
} catch(InterruptedException e) {
break;
}
}
Bitmap bitmap = mWebcamManager.getFrame();
Canvas canvas = mHolder.lockCanvas();
if(canvas != null) {
drawOnCanvas(canvas, bitmap);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
protected void drawOnCanvas(Canvas canvas, Bitmap videoBitmap) {
canvas.drawBitmap(videoBitmap, null, mViewWindow, null);
}
protected Rect getViewingWindow() {
return mViewWindow;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "Surface created");
mRunning = true;
getContext().bindService(new Intent(getContext(), WebcamManager.class),
mConnection, Context.BIND_AUTO_CREATE);
(new Thread(this)).start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface destroyed");
mRunning = false;
if(mWebcamManager != null) {
Log.i(TAG, "Unbinding from webcam manager");
getContext().unbindService(mConnection);
mWebcamManager = null;
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int winWidth,
int winHeight) {
Log.d("WebCam", "surfaceChanged");
int width, height, dw, dh;
if(winWidth * 3 / 4 <= winHeight) {
dw = 0;
dh = (winHeight - winWidth * 3 / 4) / 2;
width = dw + winWidth - 1;
height = dh + winWidth * 3 / 4 - 1;
} else {
dw = (winWidth - winHeight * 4 / 3) / 2;
dh = 0;
width = dw + winHeight * 4 / 3 - 1;
height = dh + winHeight - 1;
}
mViewWindow = new Rect(dw, dh, width, height);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
Log.i(TAG, "Bound to WebcamManager");
synchronized(mServiceSyncToken) {
mWebcamManager = ((WebcamManager.WebcamBinder)service).getService();
mServiceSyncToken.notify();
}
}
public void onServiceDisconnected(ComponentName className) {
Log.w(TAG, "WebcamManager disconnected unexpectedly");
synchronized(mServiceSyncToken) {
mRunning = false;
mWebcamManager = null;
mServiceSyncToken.notify();
}
}
};
}

==============================================================
jni/video_device.c
==============================================================

#include "video_device.h"
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "util.h"
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <malloc.h>
int open_device(const char* dev_name, int* fd) {
struct stat st;
if(-1 == stat(dev_name, &st)) {
LOGE("Cannot identify '%s': %d, %s", dev_name, errno, strerror(errno));
return ERROR_LOCAL;
}
if(!S_ISCHR(st.st_mode)) {
LOGE("%s is not a valid device", dev_name);
return ERROR_LOCAL;
}
*fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
if(-1 == *fd) {
LOGE("Cannot open '%s': %d, %s", dev_name, errno, strerror(errno));
if(EACCES == errno) {
LOGE("Insufficient permissions on '%s': %d, %s", dev_name, errno,
strerror(errno));
}
return ERROR_LOCAL;
}
return SUCCESS_LOCAL;
}
int init_mmap(int fd) {
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if(-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
if(EINVAL == errno) {
LOGE("device does not support memory mapping");
return ERROR_LOCAL;
} else {
return errnoexit("VIDIOC_REQBUFS");
}
}
if(req.count < 2) {
LOGE("Insufficient buffer memory");
return ERROR_LOCAL;
}
FRAME_BUFFERS = calloc(req.count, sizeof(*FRAME_BUFFERS));
if(!FRAME_BUFFERS) {
LOGE("Out of memory");
return ERROR_LOCAL;
}
for(BUFFER_COUNT = 0; BUFFER_COUNT < req.count; ++BUFFER_COUNT) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = BUFFER_COUNT;
if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) {
return errnoexit("VIDIOC_QUERYBUF");
}
FRAME_BUFFERS[BUFFER_COUNT].length = buf.length;
FRAME_BUFFERS[BUFFER_COUNT].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if(MAP_FAILED == FRAME_BUFFERS[BUFFER_COUNT].start) {
return errnoexit("mmap");
}
}
return SUCCESS_LOCAL;
}
int init_device(int fd, int width, int height) {
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
unsigned int min;
if(-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
if(EINVAL == errno) {
LOGE("not a valid V4L2 device");
return ERROR_LOCAL;
} else {
return errnoexit("VIDIOC_QUERYCAP");
}
}
if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
LOGE("device is not a video capture device");
return ERROR_LOCAL;
}
if(!(cap.capabilities & V4L2_CAP_STREAMING)) {
LOGE("device does not support streaming i/o");
return ERROR_LOCAL;
}
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;
if(-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
switch(errno) {
case EINVAL:
break;
default:
break;
}
}
}
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
return errnoexit("VIDIOC_S_FMT");
}
min = fmt.fmt.pix.width * 2;
if(fmt.fmt.pix.bytesperline < min) {
fmt.fmt.pix.bytesperline = min;
}
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if(fmt.fmt.pix.sizeimage < min) {
fmt.fmt.pix.sizeimage = min;
}
return init_mmap(fd);
}
int uninit_device() {
for(unsigned int i = 0; i < BUFFER_COUNT; ++i) {
if(-1 == munmap(FRAME_BUFFERS[i].start, FRAME_BUFFERS[i].length)) {
return errnoexit("munmap");
}
}
free(FRAME_BUFFERS);
return SUCCESS_LOCAL;
}
int close_device(int* fd) {
int result = SUCCESS_LOCAL;
if(-1 != *fd && -1 == close(*fd)) {
result = errnoexit("close");
}
*fd = -1;
return result;
}



Ref : https://github.com/openxc/android-webcam

沒有留言:

張貼留言