博主QQ:1356438802
QQ群:473383394——UVC&OpenCV47
在前一篇文章的基础上,对app增加 SurfaceView 实时预览功能,源码如下:
http://download.csdn.net/detail/luoyouren/9568587
package luo.uvc.jni;
import luo.uvc.jni.ImageProc.FrameInfo;
import android.R.bool;
import android.R.string;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback, Runnable
{
public static final String TAG = "UVCCameraPreview";
protected Context context;
private SurfaceHolder holder;
Thread mainLoop = null;
private boolean mIsOpened = false;
private boolean shouldStop = false;
public callback textCallback;
// The following variables are used to draw camera images.
private int winWidth = 0;
private int winHeight = 0;
private Rect rect;
private int dw, dh;
private float rate;
public CameraPreview(Context context)
{
super(context);
// TODO Auto-generated constructor stub
this.context = context;
Log.d(TAG, "CameraPreview constructed");
setFocusable(true);
holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
//注意:使用findViewById获取CameraPreview,会调用这个构造函数
public CameraPreview(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
Log.d(TAG, "CameraPreview constructed");
setFocusable(true);
holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
public void init()
{
int index = -1;
if (mIsOpened == false)
{
if (0 == ImageProc.connectCamera(index))
{
Log.i(TAG, "open uvc success!!!");
mIsOpened = true;
textCallback.setViewText("关闭UVC");
if (null != mainLoop)
{
shouldStop = false;
Log.i(TAG, "preview mainloop starting...");
mainLoop.start();
}
Toast.makeText(context.getApplicationContext(), "成功打开摄像头", Toast.LENGTH_SHORT).show();
} else
{
Log.i(TAG, "open uvc fail!!!");
mIsOpened = false;
Toast.makeText(context.getApplicationContext(), "摄像头打开失败", Toast.LENGTH_SHORT).show();
}
} else
{
uninit();
}
}
public void uninit()
{
if (null != mainLoop)
{
Log.i(TAG, mainLoop.isAlive() ? "mainloop is alive!" : "mainloop is not alive!");
if (mainLoop.isAlive())
{
shouldStop = true;
while (shouldStop)
{
try
{
Thread.sleep(100); // wait for thread stopping
} catch (Exception e)
{
}
}
}
}
if (mIsOpened)
{
mIsOpened = false;
ImageProc.releaseCamera();
textCallback.setViewText("打开UVC");
Log.i(TAG, "release camera...");
}
}
public boolean isOpen()
{
return mIsOpened;
}
@Override
public void run()
{
// TODO Auto-generated method stub
while (true && mIsOpened)
{
// get camera frame
FrameInfo frame = ImageProc.getFrame();
if (null == frame)
{
continue;
}
int w = frame.getWidth();
int h = frame.getHeigth();
Log.i(TAG, "frame.width = " + w + " frame.height = " + h);
// 根据图像大小更新显示区域大小
// 一般来说图像大小不会变化: 640x480
updateRect(w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(frame.getPixels(), 0, w, 0, 0, w, h);
// 刷surfaceview显示
Canvas canvas = getHolder().lockCanvas();
if (canvas != null)
{
// draw camera bmp on canvas
canvas.drawBitmap(resultImg, null, rect, null);
getHolder().unlockCanvasAndPost(canvas);
}
if (shouldStop)
{
shouldStop = false;
Log.i(TAG, "mainloop will stop!");
break;
}
}
Log.i(TAG, "mainloop break while!");
}
public void updateRect(int frame_w, int frame_h)
{
// obtaining display area to draw a large image
if (winWidth == 0)
{
winWidth = this.getWidth();
winHeight = this.getHeight();
if (winWidth * 3 / 4 <= winHeight)
{
dw = 0;
dh = (winHeight - winWidth * 3 / 4) / 2;
rate = ((float) winWidth) / frame_w;
rect = new Rect(dw, dh, dw + winWidth - 1, dh + winWidth * 3 / 4 - 1);
} else
{
dw = (winWidth - winHeight * 4 / 3) / 2;
dh = 0;
rate = ((float) winHeight) / frame_h;
rect = new Rect(dw, dh, dw + winHeight * 4 / 3 - 1, dh + winHeight - 1);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
{
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder arg0)
{
// TODO Auto-generated method stub
mainLoop = new Thread(this);
updateRect(512, 512);
// 将lena图像加载程序中并进行显示
Bitmap resultImg = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
// 刷surfaceview显示
Canvas canvas = getHolder().lockCanvas();
if (canvas != null)
{
// draw camera bmp on canvas
canvas.drawBitmap(resultImg, null, rect, null);
getHolder().unlockCanvasAndPost(canvas);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0)
{
// TODO Auto-generated method stub
}
}
总结三个问题:
1. 我的MTK Android平台只支持MJPEG格式的摄像头,不支持YUYV / h264等格式。比如使用了YUYV格式的摄像头,set format时不会报错,但是read frame时会select timeout。
2. 这个app预览时,画面有滞后,猜测有三个原因:帧率没有设置正确,但是确实set success了;JNI层调用MAT处理数据耗时;平台本身性能不够快。
3. 关闭预览后,再打开预览,会提示“摄像头打开失败”,从实际的log分析可以知道在autosetup_capture_mode_v4l2时设置V4L2_PIX_FMT失败,没有找到一种有效的视频格式,很诡异的问题?!