Android 5.0发布后,相机也有很大的改动,最近做了一个Camera的demo,希望对大家有所帮助。
一、Camera2新特性
1、原生支持RAW照片输出
RAW无损格式照片的优点自不必多说,它为摄影师提供了更大后期空间。支持RAW照片输出意味着以后Android用户也可在手机上对RAW文件进行处理,获得在JPEG上无法体验的效果,包括更优秀的曝光、降噪以及白平衡后期修正。
2、突发拍摄模式
改用新API后,制约拍照速度的不再是软件而是硬件。以Nexus 5为例,分辨率全开下Andorid L的连拍速度可达到30fps。以此为基础开发而来的突发模式,或者说抓拍模式可在最短时间内记录下想要拍摄的画面。
该功能以往需要各手机厂商自行开发,一般只用于高端机型,对众多中低端机来说无疑重大利好。
3、全手动控制
尽管借助各类App也可对各项拍摄参数的控制,但Android L才真正将手动曝光控制推向了系统级别,包括快门、感光度、对焦、测光、硬件视频防抖等多种参数都被整合到了新的API内。
二、Camera2主要接口
1、CameraManager:摄像头管理器。一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。除此之外,调用CameraManager的getCameraCharacteristics(String)方法即可获取指定摄像头的相关特性。
2、CameraCharacteristics:摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头所支持的各种特性。
3、CameraDevice:代表系统摄像头。该类的功能类似于之前的Camera类。
4、CameraCaptureSession:当程序需要预览拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。为了监听CameraCaptureSession的创建过程,以及监听CameraCaptureSession的拍照过程,Camera2 API为其提供了StateCallback,CaptureCallback等内部类。
5、CameraRequest和CameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。
三、拍照的大致步骤
1、预览控件设置监听setSurfaceTextureListener,通过onSurfaceTextureAvailable监听预览控件,然后获取相机管理者CameraManager并通过相机管理者CameraManager获取相机属性CameraCharacteristics,由相机属性设置相关参数。
2、调用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
3、如果监听的摄像头打开,则通过CameraDevice创建捕获请求并创建一个ImageReader对象,用于监听获取摄像头的图像数据,然后创建预览的 CaptureRequest.Builder来设置数据传输目标。(不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。)
4、 通过调用CameraDevice的createCaptureSession(List
5、在CameraCaptureSession. StateCallback回调中创建重复预览的请求。
6、调用capture()方法,创建拍照的 CaptureRequest.Builder,通过CameraCaptureSession的capture()方法拍照。
四、配置文件
需要说明的是
android:name="android.hardware.camera2.full"必须加上,因为官方文档解释说如果没有则Camera2暴露的特性和老相机的API大致相同。
五、贴上代码
public class MainActivity extends Activity implements TextureView.SurfaceTextureListener{
private static final String TAG="MainActivity";
private static final String MYCAMERA2="MYCAMERA2";
private TextureView mTextureView;
private Handler mHandler;
private HandlerThread mHandlerThread;
private CameraDevice mCameraDevice;
private ImageReader mImageReader;
private Size mPreviewSize;
private CaptureRequest.Builder mPreviewBuilder;
private CaptureRequest.Builder mCaptureBuilder;
private CameraCaptureSession mSession;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 无标题栏
setContentView(R.layout.activity_main);
initLooper();
mTextureView= (TextureView) findViewById(R.id.tv_previewView);
mTextureView.setSurfaceTextureListener(this);
}
//很多过程都变成了异步的了,所以这里需要一个子线程的looper
// 创建一个会话是要花费几百毫秒的耗时操作,因为它需要配置相机设备的内置管道并且还要配置存储缓冲区来给需要图片数据的目标发送图片数据。因此,开启新线程。
private void initLooper() {
mHandlerThread = new HandlerThread(MYCAMERA2);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.e(TAG, "----------onSurfaceTextureAvailable()----------");
try {
//获得CameraManager
CameraManager cameraManager= (CameraManager)getSystemService(Context.CAMERA_SERVICE);
//获得属性CameraCharacteristics:CameraDevice的属性描述类,描述相机硬件设备支持可用的和输出的参数
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics("0");
//获取摄像头支持的配置属性
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] outputSizes = map.getOutputSizes(SurfaceTexture.class);
// 获取最大的预览尺寸
mPreviewSize = getMaxSize(outputSizes);
Log.e(TAG,"mPreviewSize======"+mPreviewSize);
// 创建一个ImageReader对象,用于获取摄像头的图像数据
mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);
//打开相机
cameraManager.openCamera("0", mCameraDeviceStateCallback, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// -----------------------获取最大的预览尺寸--------------------
private Size getMaxSize(Size[] outputSizes) {
Size sizeMax = null;
if (outputSizes != null) {
sizeMax = outputSizes[0];
for (Size size : outputSizes) {
Log.e("TAG",
"------- size.getWidth()===" + size.getWidth() + ",size.getHeight()===" + size.getHeight());
if (size.getWidth() * size.getHeight() > sizeMax.getWidth() * sizeMax.getHeight()) {
sizeMax = size;
}
}
}
Log.e("TAG",
"------- sizeMax.getWidth()===" + sizeMax.getWidth() + ",sizeMax.getHeight()===" + sizeMax.getHeight());
return sizeMax;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.e(TAG,"--------onSurfaceTextureSizeChanged-------------");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.e(TAG,"--------onSurfaceTextureDestroyed-------------");
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Log.e(TAG,"--------onSurfaceTextureUpdated-------------");
}
// --------------第一次回调,打开相机后回调,可执行Camera.createCaptureSession()------------------------
private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
Log.e("TAG", "CameraDevice-----onOpened()");
try {
mCameraDevice=camera;
startPreview(mCameraDevice);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//摄像头断开连接时激发该方法
@Override
public void onDisconnected(CameraDevice camera) {
Log.e("TAG", "CameraDevice-----onDisconnected()");
camera.close();
mCameraDevice = null;
mImageReader.close();
mImageReader=null;
}
@Override
public void onError(CameraDevice camera, int error) {
Log.e("TAG", "CameraDevice-----onError():"+error);
camera.close();
mCameraDevice = null;
mImageReader.close();
mImageReader=null;
MainActivity.this.finish();
}
};
private void startPreview(CameraDevice camera) throws CameraAccessException {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
// 设置适当的大小和格式去匹配相机设备的可支持的大小和格式
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Log.e("TAG", "mPreviewSize:" + mPreviewSize.getWidth() + "*" + mPreviewSize.getHeight());
Surface surface = new Surface(texture);
try {
mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (CameraAccessException e) {
e.printStackTrace();
}
// 设置预览数据传输到TextureView或SurfaceView
mPreviewBuilder.addTarget(surface);
//设置预览数据输出到ImageReader
//mPreviewBuilder.addTarget(mImageReader.getSurface());
// 预览输出JPEG方向设置
mPreviewBuilder.set(CaptureRequest.JPEG_ORIENTATION,90);
// 创建由相机设备的输出surface组成的拍照会话
camera.createCaptureSession(Arrays.asList(surface,mImageReader.getSurface()), mSessionStateCallback, mHandler);
}
// --------------第二次回调,创建Session后回调,可执行CameraCaptureSession.setRepeatingRequest()------------------------
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
Log.e("TAG", "CameraCaptureSession-----onConfigured()");
// 开启红外灯光
/* if (!isLedOn) {
InfraredLedManager.setInfraredLedOn();
isLedOn = !isLedOn;
}
*/
try {
mSession=session;
// 通过mPreviewBuilder.set(key, value)方法,设置拍照参数
// 设置预览为黑白效果
// mPreviewBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CaptureRequest.CONTROL_EFFECT_MODE_MONO);
mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// mPreviewBuilder.set(CaptureRequest.BLACK_LEVEL_LOCK,true);
mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 开启预览
mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
Log.e("TAG", "CameraCaptureSession-----onConfigureFailed()");
mSession=session;
mSession.close();
mSession=null;
mCameraDevice.close();
mCameraDevice = null;
mImageReader.close();
mImageReader=null;
}
};
// ------------------------第三次回调,拍照后回调-------------------------
private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
// 拍照完成后回调
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
Log.e("TAG", "mSessionCaptureCallback, onCaptureCompleted");
try {
//设置自动对焦
mCaptureBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
//设置自动曝光模式
mCaptureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 重新打开预览
session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult) {
Log.e("TAG", "mSessionCaptureCallback, onCaptureProgressed");
}
public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
android.hardware.camera2.CaptureFailure failure) {
Log.e("TAG", "mSessionCaptureCallback, onCaptureFailed"+request+":"+failure);
mSession.close();
mSession=null;
mCameraDevice.close();
mCameraDevice=null;
mImageReader.close();
mImageReader=null;
}
};
// -------------------------拍摄,照相功能---------------------------------------------
public void onCapture(View view) {
try {
Log.e("TAG", "onCapture");
if (mCameraDevice==null){
Log.e("TAG", "mCameraDevice==null");
return;
}
mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
//设置拍照数据输出到ImageReader
mCaptureBuilder.addTarget(mImageReader.getSurface());
//设置自动对焦
mCaptureBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
//设置自动曝光模式
mCaptureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//停止连续取景
mSession.stopRepeating();
//设置拍照数据JPEG方向
mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, 90);
//拍照方法
mSession.capture(mCaptureBuilder.build(), mSessionCaptureCallback, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// -----------------------获得数据监听,用于保存拍照数据-------------------------
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
// 获得数据时回调
@Override
public void onImageAvailable(ImageReader reader) {
Log.e(TAG, "mImageReader-----onImageAvailable");
Log.e(TAG, "reader:" + reader.getWidth() + "*" + reader.getHeight());
//获取捕获的照片数据
Image image = reader.acquireNextImage();
Log.e(TAG, "reader.getMaxImages():" + reader.getMaxImages());
Log.e(TAG, "image:" + image.getWidth() + "*" + image.getHeight());
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
Log.e(TAG, "data.length:" + data.length);
// 存储
writeImageToDisk(data);
image.close();
}
};