Camera2 API簡介
Android 從5.0(21)開始,引入了新的Camera API Camera2,原來的android.hardware.Camera被廢棄(下面稱爲Camera1),還有一個android.graphics.Camera,這個android.graphics.Camera不是用來照相的,是用來處理圖像的,可以做出3D的圖像效果之類的,之前的Camera1則由android.hardware.Camera代替。Camera2支持RAW輸出,可以調節曝光,對焦模式,快門等,功能比原先Camera強大。
這裏引用了管道的概念將安卓設備和攝像頭之間聯通起來,系統向攝像頭髮送 Capture 請求,而攝像頭會返回 CameraMetadata。這一切建立在一個叫作 CameraCaptureSession 的會話中。
- CameraManaer 攝像頭管理器,用於檢測攝像頭,打開系統攝像頭,調用CameraManager.getCameraCharacteristics(String)可以獲取指定攝像頭的相關特性
- CameraCharacteristics 攝像頭的特性
- CameraDevice 攝像頭,類似android.hardware.Camera也就是Camera1的Camera
- CameraCaptureSession 這個類控制攝像頭的預覽或者拍照,setRepeatingRequest()開啓預覽,capture()拍照,CameraCaptureSession提供了StateCallback、CaptureCallback兩個接口來監聽CameraCaptureSession的創建和拍照過程。
- CameraRequest和CameraRequest.Builder,預覽或者拍照時,都需要一個CameraRequest對象。CameraRequest表示一次捕獲請求,用來對照片的各種參數設置,比如對焦模式、曝光模式等。CameraRequest.Builder用來生成CameraRequest對象。
Camera2的簡單使用(使用SurfaceView)
主要步驟:
- 獲得攝像頭管理器CameraManager mCameraManager,mCameraManager.openCamera()來打開攝像頭
- 指定要打開的攝像頭,並創建openCamera()所需要的CameraDevice.StateCallback stateCallback
- 在CameraDevice.StateCallback stateCallback中調用takePreview(),這個方法中,使用CaptureRequest.Builder創建預覽需要的CameraRequest,並初始化了CameraCaptureSession,最後調用了setRepeatingRequest(previewRequest, null, childHandler)進行了預覽
- 點擊拍照按鈕,調用takePicture(),這個方法內,最終調用了capture(mCaptureRequest, null, childHandler)
- 在new ImageReader.OnImageAvailableListener(){}回調方法中,將拍照拿到的圖片進行展示
package org.hunter.a361camera.view;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import org.hunter.a361camera.R;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static android.os.Looper.getMainLooper;
/**
* Main UI for the statistics screen.
*/
public class CameraFragment extends Fragment {
public static final int REQUEST_CAMERA_CODE = 100;
public static final String PACKAGE = "package:";
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 SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ImageView iv_show;
private ImageView mCatture;
private CameraManager mCameraManager;//攝像頭管理器
private Handler childHandler, mainHandler;
private String mCameraID;//攝像頭Id 0 爲後 1 爲前
private ImageReader mImageReader;
private CameraCaptureSession mCameraCaptureSession;
private CameraDevice mCameraDevice;
/**
* 攝像頭創建監聽
*/
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {//打開攝像頭
mCameraDevice = camera;
//開啓預覽
takePreview();
}
@Override
public void onDisconnected(CameraDevice camera) {//關閉攝像頭
if (null != mCameraDevice) {
mCameraDevice.close();
CameraFragment.this.mCameraDevice = null;
}
}
@Override
public void onError(CameraDevice camera, int error) {//發生錯誤
Toast.makeText(getContext(), "攝像頭開啓失敗", Toast.LENGTH_SHORT).show();
}
};
public static CameraFragment newInstance() {
return new CameraFragment();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.camera_frag, container, false);
initVIew(root);
initListener();
return root;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
}
/**
* 初始化
*/
private void initVIew(View root) {
iv_show = (ImageView) root.findViewById(R.id.iv_show_camera2);
//mSurfaceView
mSurfaceView = (SurfaceView) root.findViewById(R.id.surface_view_camera2);
mCatture = (ImageView) root.findViewById(R.id.capture);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setKeepScreenOn(true);
// mSurfaceView添加回調
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) { //SurfaceView創建
// 初始化Camera
initCamera2();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView銷燬
// 釋放Camera資源
if (null != mCameraDevice) {
mCameraDevice.close();
CameraFragment.this.mCameraDevice = null;
}
}
});
}
/**
* 初始化Camera2
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void initCamera2() {
HandlerThread handlerThread = new HandlerThread("Camera2");
handlerThread.start();
childHandler = new Handler(handlerThread.getLooper());
mainHandler = new Handler(getMainLooper());
mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//後攝像頭
mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在這裏處理拍照得到的臨時照片 例如,寫入本地
@Override
public void onImageAvailable(ImageReader reader) {
//mCameraDevice.close();
// 拿到拍照照片數據
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由緩衝區存入字節數組
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
iv_show.setImageBitmap(bitmap);
}
}
}, mainHandler);
//獲取攝像頭管理
mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//申請WRITE_EXTERNAL_STORAGE權限
requestPermissions(new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_CODE);
//return;
} else {
//打開攝像頭
mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
try {
mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
} else {
// Permission Denied
}
}
}
/**
* 開始預覽
*/
private void takePreview() {
try {
// 創建預覽需要的CaptureRequest.Builder
final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// 將SurfaceView的surface作爲CaptureRequest.Builder的目標
previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
// 創建CameraCaptureSession,該對象負責管理處理預覽請求和拍照請求
mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③
{
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
if (null == mCameraDevice) return;
// 當攝像頭已經準備好時,開始顯示預覽
mCameraCaptureSession = cameraCaptureSession;
try {
// 自動對焦
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 打開閃光燈
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 顯示預覽
CaptureRequest previewRequest = previewRequestBuilder.build();
mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(getContext(), "配置失敗", Toast.LENGTH_SHORT).show();
}
}, childHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* 拍照
*/
private void takePicture() {
if (mCameraDevice == null) return;
// 創建拍照需要的CaptureRequest.Builder
final CaptureRequest.Builder captureRequestBuilder;
try {
captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
// 將imageReader的surface作爲CaptureRequest.Builder的目標
captureRequestBuilder.addTarget(mImageReader.getSurface());
// 自動對焦
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 自動曝光
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 獲取手機方向
int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
// 根據設備方向計算設置照片的方向
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
//拍照
CaptureRequest mCaptureRequest = captureRequestBuilder.build();
mCameraCaptureSession.capture(mCaptureRequest, null, childHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void initListener() {
mCatture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePicture();
}
});
}
}
佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surface_view_camera2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<org.hunter.a361camera.widget.PorterDuffViewImageView
android:id="@+id/capture"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:src="@mipmap/capture"/>
<org.hunter.a361camera.widget.PorterDuffViewImageView
android:id="@+id/iv_show_camera2"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="20dp"
android:layout_marginRight="20dp"
android:scaleType="centerCrop"/>
</RelativeLayout>
源代碼地址:https://github.com/gengqifu/361Camera。歡迎fork/star