博客 : 相机开发 -- 实时帧数据处理
UI设计
# 方案:xml中添加两个按钮, 控制相机预览的开始和停止。 在代码中绑定对应的事件
# 方法编写
public void startPreview() {
/*初始化相机预览
* 1. 预览控件布局载入
* 2. 相机获取、初始化设置信息
* 3. 控件监听事件绑定 -- 设置菜单
*/
/*预览控件载入*/
final CameraPreview mPreview = new CameraPreview(this);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
/*相机获取 && 初始化设置*/
SettingsFragment.passCamera(mPreview.getCameraInstance());
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
SettingsFragment.setDefault(PreferenceManager.getDefaultSharedPreferences(this));
SettingsFragment.init(PreferenceManager.getDefaultSharedPreferences(this));
/*绑定监听事件 -- 设置菜单*/
Button buttonSettings = (Button) findViewById(R.id.button_settings);
buttonSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(R.id.camera_preview, new SettingsFragment()).addToBackStack(null).commit();
}
});
}
public void stopPreview() {
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.removeAllViews();
}
# 按钮绑定 [in onCreate()]
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonStartPreview = (Button) findViewById(R.id.button_start_preview);
buttonStartPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startPreview();
}
});
Button buttonStopPreview = (Button) findViewById(R.id.button_stop_preview);
buttonStopPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopPreview();
}
});
}
基本的帧数据获取和处理
# 概述:接口Camera.PreviewCallback 下的onPreviewFrame(byte[] data,Camera camera) 可获取帧数据
# 方案:为CameraPreview类增加Camera.PreviewCallback接口声明 并且实现对应方法 最后绑定接口
# 接口声明:public class CameraPreview extends SurfaceView
implements SurfaceHolder.Callback, Camera.PreviewCallback
# 方法实现:
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG, "processing frame");
/*
* 暂停0.5秒模拟帧数据处理
*/
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
# 接口绑定:在surfaceCreated()中getCameraInstance()代码前绑定
mCamera.setPreviewCallback(this);
// 效果 : 每当有预览帧生成则调用onPreviewFrame()方法
UI线程分离 -- 防止UI线程的阻塞
# 方案:借助HandlerThread实现。创建新线程,拥有自己的loop。
通过Handler.post()确保在新线程执行指定语句
# 线程设置 [in CameraPreview]
private class CameraHandlerThread extends HandlerThread {
/* 线程任务 : 执行 mCamer = Camera.open()
* 确保通过handler.post()在Runnable中执行
*/
Handler mHandler;
// 构造:线程启动 获取mHnadler
public CameraHandlerThread(String name) {
super(name);
start();//线程启动 并 创建一个 handler
mHandler = new Handler(getLooper());
}
//相机确认
synchronized void notifyCameraOpened() {
notify();
}
/* 相机开启
*/
void openCamera() {
/* 安全设置 --- notify-wait控制 [确认相机打开后openCamare()才返回]
* post()执行会立即返回 BUT Runnable会异步执行
* SO 可能执行post()后立即使用mCamera时仍为null
*/
mHandler.post(new Runnable() { // UI更新
@Override
public void run() {
openCameraOriginal(); // 封装相机开启功能
notifyCameraOpened(); // 相机确认
}
});
try {
wait();
} catch (InterruptedException e) {
Log.w(TAG, "wait was interrupted");
}
}
}
# 方法封装 [in CameraPreview ]
private void openCameraOriginal() {
try {
mCamera = Camera.open();
} catch (Exception e) {
Log.d(TAG, "camera is not available");
}
}
# 线程添加
public Camera getCameraInstance() {
if (mCamera == null) {
CameraHandlerThread mThread = new CameraHandlerThread("camera thread");
synchronized (mThread) {
mThread.openCamera();
}
}
return mCamera;
}
帧数据处理
# 方案:采用HandlerThread [利用Android的Message Queue异步处理帧数据]
- onPreviewFrame()调用时将帧数据封装为Message, 发送给HandlerThread
- HandlerThread在新线程中获取Message,对帧数据进行处理
发送Meaasge时间短,不会造成帧数据丢失问题。保证最后效果的实时性
# 新建ProcessWithHandlerThread类
/*
* 新建线程类:父类 - HandlerThread 接口 - Handler.Callback
*/
public class ProcessWithHandlerThread extends HandlerThread implements Handler.Callback {
private static final String TAG = "HandlerThread";
public static final int WHAT_PROCESS_FRAME = 1;
/* 构造 - 线程启动 */
public ProcessWithHandlerThread(String name) {
super(name);
start();
}
/* 信息处理 */
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case WHAT_PROCESS_FRAME:
byte[] frameData = (byte[]) msg.obj;//数据类型转换
processFrame(frameData);//数据处理封装
return true;
default:
return false;
}
}
/* 帧数据处理 */
private void processFrame(byte[] frameData) {
Log.i(TAG, "test");
}
}
# 线程实例化 [in CameraPreview]
/* 线程实例化
* 绑定接口
* 帧数据封装
* 信息发送
*/
/*成员变量添加*/
private static final int PROCESS_WITH_HANDLER_THREAD = 1;
private int processType = PROCESS_WITH_HANDLER_THREAD;
private ProcessWithHandlerThread processFrameHandlerThread;
private Handler processFrameHandler;
/*构造函数修改 - 末尾添加*/
switch (processType) {
case PROCESS_WITH_HANDLER_THREAD:
/* 线程实例化 */
processFrameHandlerThread = new ProcessWithHandlerThread("process frame");
processFrameHandler = new Handler(processFrameHandlerThread.getLooper(), processFrameHandlerThread);//接口绑定
break;
}
/*onPreviewFrame()修改*/
public void onPreviewFrame(byte[] data, Camera camera) {
switch (processType) {
case PROCESS_WITH_HANDLER_THREAD:
processFrameHandler.obtainMessage(ProcessWithHandlerThread.WHAT_PROCESS_FRAME, data).sendToTarget();//数据封装 && 消息传递
break;
}
}
完整代码 : https://github.com/zhantong/AndroidCamera-ProcessFrames