android camera2 拍照流程
来源:互联网 发布:遥感大数据的特点 编辑:程序博客网 时间:2024/06/08 04:41
正文
camera2 API 的加入是从AndroidV5.0(21)开始的,因此我们使用Camera2应该是在Android 5.0(含5.0)之后。同时,对于Android6.0我们需要有动态权限的管理。这两点应该是使用Camera2使用前的最基本认知。Android 5.0对拍照API进行了全新的设计,新增了全新设计的Camera v2 API,这些API不仅大幅提高了Android系统拍照的功能,还能支持RAW照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。下面不做过多介绍了,直接开撸了。
Camera2包架构示意图
我们先来看看 camera2包架构示意图(不用一下子理解,只需要有个整体印象):
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。
下面是 camera2包中的主要类:
CameraManager:摄像头管理器。这是一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。另外,调用CameraManager的getCameraCharacteristics(String cameraId)方法即可获取指定摄像头的相关特性。
CameraCharacteristics:摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头所支持的各种特性。类似与原来的CameraInfo 。
CameraDevice:代表系统摄像头。该类的功能类似于早期的Camera类。而每个 CameraDevice 自己会负责建立 CameraCaptureSession 以及建立 CaptureRequest。
CameraCaptureSession:这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。
为了监听CameraCaptureSession的创建过程,以及监听CameraCaptureSession的拍照过程,Camera v2 API为CameraCaptureSession提供了StateCallback、CaptureCallback等内部类。
CameraRequest和CameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。
相机预览与拍照流程
如果你看不太懂流程图,没关系,待会儿我们通过代码就可以更好的理解了。首先,Google官方推荐的Camera2控制拍照的步骤大致如下。
1.用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
2.当摄像头被打开之后会回调接口mStateCallback.onOpened,程序即可获取CameraDevice —— 即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
- 3.不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。
- 4.通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。
- 5.调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。
相机的预览与拍照流程我们基本了解了。 - 6预览时,是将mSurfaceHolder.getSurface()作为目标,使用setRepeatingRequest()方法,
显示拍照结果时,是将mImageReader.getSurface()作为目标,使用capture()方法。
然后这里还有一个大招:Google官方Camera2拍照的demo的地址:点击跳转github
首先是我们的layout代码
首先权限不能忘:
uses-permission android:name=”android.permission.CAMERA” /
uses-feature android:name=”android.hardware.camera” /
uses-feature android:name=”android.hardware.camera.autofocus” /
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextureView android:id="@+id/texture" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/cancelButton" android:text="取消" android:visibility="invisible"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/captureButton" android:text="拍照" android:visibility="visible"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/saveButton" android:text="保存" android:visibility="invisible"/> </LinearLayout></LinearLayout>
下面是我的代码
public class customCarmeraActivity extends AppCompatActivity { private static final int REQUEST_CAMERA_PERMISSION = 1; private static final int STATE_PREVIEW = 1; private static final int STATE_WAITING_PRECAPTURE = 2; private int mState = STATE_PREVIEW; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); ///为了使照片竖直显示 static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } private CameraManager mCameraManagerm; private CameraDevice mCameraDevice; private String mCameraId; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private TextureView mTextureView; private ImageReader mImageReader; private File mFile; private CaptureRequest.Builder mPreviewBuilder; private CaptureRequest mPreviewRequest; private CameraCaptureSession mCaptureSession; private Semaphore mCameraOpenCloseLock = new Semaphore(1); /** * Starts a background thread and its {@link Handler}. */ private void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } /** * Stops the background thread and its {@link Handler}. */ private void stopBackgroundThread() { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } /** * * @TextureView.SurfaceTextureListener:public static interface * @onSurfaceTextureAvailable:Invoked when a TextureView's SurfaceTexture is ready for use. * @onSurfaceTextureSizeChanged:Invoked when the SurfaceTexture's buffers size changed. * @onSurfaceTextureDestroyed:Invoked when the specified SurfaceTexture is about to be destroyed. * @onSurfaceTextureUpdated:Invoked when the specified SurfaceTexture is updated through updateTexImage(). */ private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { openCamera(width, height); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { //configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture texture) { } }; /** * Shows a {@link Toast} on the UI thread. * * @param text The message to show */ private void showToast(final String text) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(customCarmeraActivity.this, text, Toast.LENGTH_SHORT).show(); } }); } /** * Capture a still picture. This method should be called when we get a response in * {@link #mCaptureCallback} from both {@link #lockFocus()}. */ private void captureStillPicture() { try { if (null == mCameraDevice) { return; } // This is the CaptureRequest.Builder that we use to take a picture. final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface());//拍照时,是将mImageReader.getSurface()作为目标 // Use the same AE and AF modes as the preview. captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // Orientation int rotation = getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { showToast("Saved: " + mFile); //Log.d("customCarmeraActivity", mFile.toString()); unlockFocus();//恢复预览 } }; mCaptureSession.stopRepeating(); mCaptureSession.abortCaptures(); mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { //Log.d("linc","mSessionCaptureCallback, onCaptureCompleted"); mCaptureSession = session; checkState(result); } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { Log.d("linc","mSessionCaptureCallback, onCaptureProgressed"); mCaptureSession = session; checkState(partialResult); } private void checkState(CaptureResult result) { switch (mState) { case STATE_PREVIEW: // We have nothing to do when the camera preview is working normally. break; case STATE_WAITING_PRECAPTURE: Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { //mState = STATE_PICTURE_TAKEN; captureStillPicture(); } else { //runPrecaptureSequence();//视频拍摄 } } break; } } }; private void createCameraPreviewSession() { try { SurfaceTexture texture = mTextureView.getSurfaceTexture(); //assert(texture != null); // We configure the size of default buffer to be the size of camera preview we want. texture.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight()); // This is the output Surface we need to start preview. Surface surface = new Surface(texture); mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewBuilder.addTarget(surface);//预览时,是将Surface()作为目标 mState = STATE_PREVIEW; mCameraDevice.createCaptureSession( Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { // The camera is already closed if (null == mCameraDevice) { return; } mCaptureSession = cameraCaptureSession; try { mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); mPreviewRequest = mPreviewBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); Log.e("linc","set preview builder failed."+e.getMessage()); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { Toast.makeText(customCarmeraActivity.this, "Camera configuration Failed", Toast.LENGTH_SHORT).show(); } },mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * * @CameraDevice.StateCallback:public static abstract class * @onOpened:This method is called when the camera is opened. We start camera preview here. * @onDisconnected:The method called when a camera device is no longer available for use. * @onError:The method called when a camera device has encountered a serious error. */ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } @Override public void onDisconnected(CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } }; private void getCameraId() { try { //Return the list of currently connected camera devices by identifier, including cameras that may be in use by other camera API clients for (String cameraId : mCameraManagerm.getCameraIdList()) { //Query the capabilities of a camera device CameraCharacteristics characteristics = mCameraManagerm.getCameraCharacteristics(cameraId); if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) { continue; } mCameraId = cameraId; return; } } catch (CameraAccessException e) { e.printStackTrace(); } } /** * Saves a JPEG {@link Image} into the specified {@link File}. */ private static class ImageSaver implements Runnable { /** * The JPEG image */ private final Image mImage; /** * The file we save the image into. */ private final File mFile; ImageSaver(Image image, File file) { mImage = image; mFile = file; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; try { output = new FileOutputStream(mFile); output.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } } } private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { //showToast("onImageAvailable: " ); mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); } }; //TODO 执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知: @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CAMERA_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1 //permission was granted, yay! Do the contacts-related task you need to do. //这里进行授权被允许的处理 } else { //permission denied, boo! Disable the functionality that depends on this permission. //这里进行权限被拒绝的处理 } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } /** * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}. */ private void openCamera(int width, int height) { //检查相机服务的访问权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { //Toast.makeText(this,"Lacking privileges to access camera service, please request permission first",Toast.LENGTH_SHORT).show(); Log.e("customCarmeraActivity.openCamera","Lacking privileges to access camera service, please request permission first"); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);//API21后,向用户请求相机使用权限,然后执行onRequestPermissionsResult回调 return; } getCameraId(); //assert(mCameraId != null); mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG,/*maxImages*/7); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); try { if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Time out waiting to lock camera opening."); } mCameraManagerm.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera opening.", e); } } /** * Closes the current {@link CameraDevice}. */ private void closeCamera() { try { mCameraOpenCloseLock.acquire(); if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } if (null != mCameraDevice) { mCameraDevice.close(); mCameraDevice = null; } if (null != mImageReader) { mImageReader.close(); mImageReader = null; } } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera closing.", e); } finally { mCameraOpenCloseLock.release(); } } /** * Unlock the focus. This method should be called when still image capture sequence is * finished. */ private void unlockFocus() { try { // Reset the auto-focus trigger mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); /* mCaptureSession.capture(mPreviewBuilder.build(), mCaptureCallback, mBackgroundHandler);*/ // After this, the camera will go back to the normal state of preview. mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * Lock the focus as the first step for a still image capture. */ private void lockFocus() { try { // This is how to tell the camera to lock focus. mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); // Tell #mCaptureCallback to wait for the lock. mState = STATE_WAITING_PRECAPTURE; mCaptureSession.capture(mPreviewBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.custom_carmera_layout); mCameraManagerm = (CameraManager)getSystemService(Context.CAMERA_SERVICE); mTextureView = (TextureView)findViewById(R.id.textureview); mFile = new File(getExternalFilesDir(null), "pic.jpg"); Button tackPictureBtn = (Button)findViewById(R.id.takePictureButton); tackPictureBtn.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { //Log.i("linc", "take picture"); lockFocus(); } }); } @Override protected void onResume() { super.onResume(); startBackgroundThread(); if(mTextureView.isAvailable()) { openCamera(mTextureView.getWidth(), mTextureView.getHeight()); } else{ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } } @Override protected void onPause() { super.onPause(); closeCamera(); stopBackgroundThread(); }}
- android camera2 拍照流程
- Android Camera2 拍照入门学习
- Android Camera2 拍照入门学习
- Android Camera2 拍照入门学习
- android camera2启动流程
- android camera2配置流程
- Android开发 之 Camera2之拍照
- Android camera2预览界面流程
- android camera2 API流程分析
- android camera2人脸识别流程分析
- Android Camera2拍照(一)——使用SurfaceView
- Android Camera2 拍照(二)——使用TextureView
- Android Camera2 拍照(四)——对焦模式
- Camera2拍照(备忘)
- android 手机拍照流程
- android camera2
- Android camera2
- 摄像头拍照功能5.0 Camera2
- npm命令总结汇总
- python django url导入
- 【.Net码农】【淘宝API】淘宝API代码c#实例(摘)
- Effective C++ Item2: Enum hack
- 交互设计师为什么需要具备产品思维
- android camera2 拍照流程
- Lopoper 退出循环的原理
- 分布式系统中生成全局唯一ID的方法
- 前端 HTML空格
- js字符串转日期,日期转字符串
- HashMap与ArrayMap(和SparseArray)的比较与选择
- JAVA操作EXCEL
- 各种开发架构技术图谱
- 集合乱序算法shuffle