添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
憨厚的沙滩裤  ·  ( Day 11 ) ...·  2 年前    · 

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天, 点击查看活动详情

💡Google在Camera2例子中没有具体指明预览帧的获取,即Camera1 setPreviewCallback 类似功能,需要通过实现Camera2 api 中使用 ImageReader 类间接获取帧数据.

一、Camera2简介

在Google推出Android 5.0的时候, Android Camera API 版本升级到了API2), 之前使用的API1(android.hardware.camera)就被标为 Deprecated 了. Camera API2相较于API1有很大不同, 并且API2是为了配合HAL3进行使用的, API2有很多API1不支持的特性

1.1.基本架构

如上图所示, Camera APP 通过CameraCaptureSession发送CaptureRequest, CameraDevices收到请求后返回对应数据到对应的Surface,预览数据一般都是到TextureView, 拍照数据则在ImageReader中, 整体来说就是一个请求--响应过程, 请求完成后, 可以在回调中查询到相应的请求参数和CameraDevice当前状态, 总的来说, Camera2中预览/拍照/录像数据统一由Surface来接收, CaptureRequest代表请求控制的Camera参数, CameraMetadata(CaptureResult)则表示当前返回帧中Camera使用的参数以及当前状态.

1.2.使用流程

1.通过context.getSystemService(Context.CAMERA_SERVICE) 获取CameraManager. 2.调用CameraManager .open()方法在回调中得到CameraDevice. 3.通过CameraDevice.createCaptureSession() 在回调中获取CameraCaptureSession. 4.构建CaptureRequest, 有三种模式可选 预览/拍照/录像. 5.通过 CameraCaptureSession发送CaptureRequest, capture表示只发一次请求, setRepeatingRequest表示不断发送请求. 6.拍照数据可以在ImageReader.OnImageAvailableListener回调中获取, CaptureCallback中则可获取拍照实际的参数和Camera当前状态.

二、获取帧

2.1.新建

新建一个 ImageReader 对象,用来接收预览帧数据: private ImageReader imageReader;

2.2.初始化

初始化好预览大小,比较只有预览大小确定后,才知道得使用一块多大的内存来接收这块数据:

1.mPreviewImageReader = ImageReader.newInstance(  
2.        mPreviewSize.getWidth(), // 宽度  
3.        mPreviewSize.getHeight(), // 高度  
4.        ImageFormat.YUV_420_888, // 图像格式  
        2); // 用户能同时得到的最大图像数

图像格式可以在 ImageFormat 类中查看,值得注意的是,并不是所有的格式在 Camera2 的预览中都支持,例如 ImageFormat.NV21 便不再支持。另外就是预览不建议使用 ImageFormat.JPEG 这样的格式,因为转码会消耗大量的性能,推荐使用 ImageFormat.YUV_420_888 之类的格式。

2.3.释放

不再需要ImageReader 时,需要手动释放它,在 release camera 的时机释放:

1.if (mPreviewImageReader != null) {  
2.    mPreviewImageReader.close();  
3.    mPreviewImageReader = null;  

2.4.添加到请求

在预览的 CaptureRequest.Builder 中添加一个目标到该 ImageReader,就可以获取到相应的帧数据

1.  private void initPreviewRequest() {  
2.    try {  
3.        mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);  
4.        mPreviewRequestBuilder.addTarget(mPreviewSurface); // 设置预览输出的 Surface  
5.        mPreviewRequestBuilder.addTarget(mPreviewImageReader.getSurface()); // 设置预览回调的 Surface  
6.        mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mPreviewImageReader.getSurface()),  
7.                new CameraCaptureSession.StateCallback() {  
9.                    @Override  
10.                    public void onConfigured(CameraCaptureSession session) {  
11.                        mCaptureSession = session;  
12.                        startPreview();  
13.                    }  
15.                    @Override  
16.                    public void onConfigureFailed(CameraCaptureSession session) {  
17.                        Log.e(TAG, "ConfigureFailed. session: mCaptureSession");  
18.                    }  
19.                }, mBackgroundHandler); // handle 传入 null 表示使用当前线程的 Looper  
20.    } catch (CameraAccessException e) {  
21.        e.printStackTrace();  
22.    }  
23.}  

2.5. 添加回调监听

给这个 ImageReader 添加一个回调监听事件,就可以在回调方法中获取到相应的预览帧数据:

1.mPreviewImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {  
2.    @Override  
3.    public void onImageAvailable(ImageReader reader) {  
4.        // do something  
6.}, null);  

2.6.数据处理

上述 onImageAvailable(ImageReader reader) 回调得到并不是byte[] 数组,而是一个 ImageReader 对象,需要通过这个 ImageReader获取Image,从Image中得到YUV分量的颜色数据,再按指定格式拼起来:

1.private static byte[] getDataFromImage(Image image) {  
2.       Rect crop = image.getCropRect();  
3.       int format = image.getFormat();  
4.       int width = crop.width();  
5.       int height = crop.height();  
6.       Image.Plane[] planes = image.getPlanes();  
7.       byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];  
8.       byte[] rowData = new byte[planes[0].getRowStride()];  
9.       int channelOffset = 0;  
10.       int outputStride = 1;  
11.       for (int i = 0; i < planes.length; i++) {  
12.           switch (i) {  
13.               case 0:  
14.                   channelOffset = 0;  
15.                   outputStride = 1;  
16.                   break;  
17.               case 1:  
18.                   channelOffset = width * height + 1;  
19.                   outputStride = 2;  
20.                   break;  
21.               case 2:  
23.                   channelOffset = width * height;  
24.                   outputStride = 2;  
25.                   break;  
26.           }  
27.           ByteBuffer buffer = planes[i].getBuffer();  
28.           int rowStride = planes[i].getRowStride();  
29.           int pixelStride = planes[i].getPixelStride();  
30.           Log.v(TAG, "pixelStride " + pixelStride);  
31.           Log.v(TAG, "rowStride " + rowStride);  
32.           Log.v(TAG, "width " + width);  
33.           Log.v(TAG, "height " + height);  
34.           Log.v(TAG, "buffer size " + buffer.remaining());  
35.           int shift = (i == 0) ? 0 : 1;  
36.           int w = width >> shift;  
37.           int h = height >> shift;  
38.           buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));  
39.           for (int row = 0; row < h; row++) {  
40.               int length;  
41.               if (pixelStride == 1 && outputStride == 1) {  
42.                   length = w;  
43.                   buffer.get(data, channelOffset, length);  
44.                   channelOffset += length;  
45.               } else {  
46.                   length = (w - 1) * pixelStride + 1;  
47.                   buffer.get(rowData, 0, length);  
48.                   for (int col = 0; col < w; col++) {  
49.                       data[channelOffset] = rowData[col * pixelStride];  
50.                       channelOffset += outputStride;  
51.                   }  
52.               }  
53.               if (row < h - 1) {  
54.                   buffer.position(buffer.position() + rowStride - length);  
55.               }  
56.           }  
57.       }  
58.       return data;  
59.   }  
复制代码
  • 私信