提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
最近一直在学习Android 操作Camera的流程,由于camera1已经废弃,直接学习了Camera2 API的使用流程,Camera2 API相对于Camera1来讲增加了许多功能,使得对于相机的可操作性更高,让傻瓜式相机向专业相机转变,可以手动控制许多参数(曝光、对焦等)。
一、Camera2相关API介绍
1.Camera2流程
由上面这种谷歌官方的图我们可以看到几个关键的类:
- CameraManager
- CameraCharacteristic
- CameraDevice
- CaptureRequest
- CaptureResult
- CameraCaptureSession
2.Camera2 API介绍
2.1 CameraManager
CameraManager见名知意,相当于是一个相机的管理者,通过CameraManager构建的对象,我们可以知道一些相机的基本信息,这个信息就存储在CameraCharacteristic对象中。我们可以这样获取到相机的一些基本信息,就是一个CameraInfo类
CameraCharacteristics cameraInfo = mCameraManager.getCameraCharacteristics(cameraId);
除了获取Camera的属性信息外,CameraManager对象扮演的最重要的角色就是打开相机(openCamera),通过CameraManager这个管理者我们才能真正的拿到CameraDevice对象去操作相机。
/*这里的三个参数分别是
cameraId:需要打开的摄像头的id
mCameraDeviceStateCallback:一个CameraDevice的状态回调类,在这个类回调方法里,会告知Camera设备的打开状态,成功还是失败
Handler:openCamera的操作在哪个线程执行,null即为在主线程执行
*/
mCameraManager.openCamera(cameraId, mCameraDeviceStateCallback, null);
2.2 CameraCharacteristic
描述相机设备的属性类,获取方式通过CameraManager对象获得(见上),具体的属性可见官方链接,这里不多赘述
https://developer.android.google.cn/reference/android/hardware/camera2/CameraCharacteristics
2.3 CameraDevice
代表Camera设备,有了CameraDevice对象,就可以创建上层与Camera设备之间的一个会话,在Camera2中即为CameraCaptureSession
/*
List<Surface> outputs:Arrays.asList(mPreviewSurface,mMediaRecorder.getSurface()):代表要配置的流的数量(这里是一个预览流和拍照流)不理解的可以先看下SurafceView、Surface的介绍
mCaptureSessionStateCallback:CameraCaptureSession创建的对象的状态回调
Handler:createCaptureSession的操作在哪个线程执行,null即为在主线程执行
*/
mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface,mImagerReader.getSurface()),mCaptureSessionStateCallback,null);
2.4 CaptureCaptureSession
代表上层与底层之间的一个会话,通过这个会话我们可以下发指令给相机,让相机执行预览、拍照录像等操作,在mCameraDevice.createCaptureSession的回调中获得
private CameraCaptureSession.StateCallback mCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraCaptureSession = session;
Log.i(TAG, "onConfigured: ");
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.i(TAG, "onConfigureFailed: ");
}
};
2.5 CaptureRequest和CaptureResult
CaptureRequest代表相机捕获请求,当CaptureCaptureSession创建好之后,我们就可以使用这个会话来下发指令,表明我当前是需要预览、拍照、还是录像,CaptureRequest对象可以携带我们所需要设置的参数,比如是否自动对焦、自动曝光、自动白平衡等。CaptureRequest对象的创建使用了建造者模式,需要通过CaptureRequest.Builder来创建,通过调用build方法,
PreviewRequest = mCaptureRequestBuilder.build();
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自动连续对焦
mCaptureRequestBuilder.addTarget(mPreviewSurface); //预览流,底层回来的数据放在哪里
mCaptureRequestBuilder.addTarget(mImageReader.getSurface());拍照流
PreviewRequest = mCaptureRequestBuilder.build();
//这里是下发预览,如果是拍照可以调用capture()方法
mCameraCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureSessionCaptureCallback, null);
CaptureResut代表捕获请求返回的一些结果信息,从里面可以获取一些Metadata数据信息
二、Camera2操作相机预览步骤
1.添加相机、存储使用权限
在AndroidManifest.xml清单文件中添加权限
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
2.打开相机(openCamera)
mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//权限检查
return;
}
try {
//这里直接打开后摄
mCameraManager.openCamera("0", mStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
//CameraDevice打开状态的回调
mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
createCameraPreviewSession();//获取CamearDevice对象后,就可以创建Session
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
camera.close();
mCameraDevice = null;
}
};
3.创建相机捕获会话(CreateCaptureSession)
在上面获得了CameraDevice对象之后,调用了createCameraPreviewSession()的方法
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
mCaptureSession = cameraCaptureSession;
try {
//下发预览请求
mCaptureSession.setRepeatingRequest(mPreviewRequest, null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.e("MainActivity", "onConfigureFailed");
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
上面创建Session时的Arrays.asList(surface)代表要配置一条预览流,这里的surface对象可以通过我们在xml文件中使用TextureView获得:
这里的TextureView实际就是我们的预览窗口
mTextureView = findViewById(R.id.preview);
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
//在SurfaceTexture可以用的时候,利用SurfaceTexture创建Surface对象
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
surface = new Surface(texture);
//这边要注意,上面的创建session的动作一定要在SurfaceTexture可用之后,所以我们可以把openCamera的操作放在这个回调里
//ToDo openCamera
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
}
});
4.下发预览请求
下发预览请求我们放在创建Session的回调onConfigured中,
不过在这之前需要构建一个CaptureRequest对象
final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewRequest = previewRequestBuilder.build();
至此,就可以完成Camera的预览了
完整代码
package com.example.camerademo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import java.util.Arrays;
import java.util.concurrent.Executor;
public class MainActivity extends AppCompatActivity {
private CameraManager mCameraManager;
private CameraDevice.StateCallback mStateCallback;
private CameraDevice mCameraDevice;
private TextureView mTextureView;
private CameraCaptureSession mCaptureSession;
private Size mPreviewSize;
private CaptureRequest mPreviewRequest;
private Surface surface;
TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(1280, 720);
surface = new Surface(texture);
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextureView = findViewById(R.id.preview);
mTextureView.setSurfaceTextureListener(surfaceTextureListener);
applyForPermission();
initCamera();
}
private void applyForPermission() {
if (Build.VERSION.SDK_INT > 22) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
//说明已经获取到摄像头权限了
Log.i("MainActivity", "已经获取了权限");
}
} else {
Log.i("MainActivity", "这个说明系统版本在6.0之下,不需要动态获取权限。");
}
}
private void initCamera() {
mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
camera.close();
mCameraDevice = null;
}
};
}
public void openCamera(){
mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
try {
mCameraManager.openCamera("0", mStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void createCameraPreviewSession() {
try {
final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewRequest = previewRequestBuilder.build();
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
mCaptureSession = cameraCaptureSession;
try {
mCaptureSession.setRepeatingRequest(mPreviewRequest, null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.e("MainActivity", "onConfigureFailed");
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
protected void onStop() {
super.onStop();
closeCamera();
}
private void closeCamera() {
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextureView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TextureView>
</LinearLayout>