深入理解相机体系 三 相机应用层 APP

122 阅读6分钟
  • 其次是实例化了一个CameraDeviceImpl对象,并将来自App的CameraDevice.StateCallback接口存入该对象中,再将CameraDeviceImpl中的内部类CameraDeviceCallback作为参数通过ICameraService的connectDevice方法传入Camera Service去打开并获取一个ICameraDeviceUser代理,并将该代理存入CameraDeviceImpl中进行管理。

  • 最后通过App传入的回调将CameraDeviceImpl返回给App使用,至此整个流程便完成了。

b) createCaptureSession

在打开相机设备之后便需要去创建一个相机会话,用于传输图像请求,其最终实现是调用该方法来进行实现的,而该方法会去调用到Camera Framework中的createCaptureSessionInternal方法,该方法主要做了两件事:

  • 首先调用configureStreamsChecked方法来配置数据流。

  • 其次实例化了一个CameraCaptureImpl对象,并通过传入CameraCaptureSession.StateCallback回调类将该对象发送至至App中。

而在configureStreamsChecked方法中会去调用ICameraDeviceUser代理的一系列方法进行数据流配置,其中调用cancelRequest方法停掉当前的的预览流程,调用deleteStream方法删除之前的数据流,调用createStream创建新的数据流,最后调用endConfigure来进行数据流的配置工作,针对性的配置便在最后这个endConfigure方法中。

c) createCaptureRequest

在创建并获取相机会话之后,便可以开始下发图像请求了,而在此之前,需要通过该方法来创建一个CaptureRequest,一旦调用该方法,最终会调用到Camera Service中ICameraDeviceUser的createDefaultRequest方法来创建一个默认配置的CameraMetadataNative,其次实例化一个CaptureRequest.Builder对象,并将刚才获取的CameraMetadataNative传入其中,之后返回该CaptureRequest.Builder对象,在App中,直接通过调用该Buidler对象的build方法,获取一个CaptureRequest对象。

CaptureRequest对象也创建成功了,接下来需要下发图像请求了,一般常用请求分为两种,一个是预览一个是拍照。

d) setRepeatingRequest

App调用该方法开始预览流程,通过层层调用最终会调用到Framework中的submitCaptureRequest方法,该方法主要做了两件事:

  • 首先调用CameraService层CameraDeviceUser的submitRequestList方法,将此次Request下发到CameraService中。

  • 其次将App通过参数传入的CameraCaptureSession.CaptureCallback对象存到CameraDeviceImpI对象中。

接下来看下拍照请求的处理流程:

e) capture

该方法最终也会调用到Framework中的submitCaptureRequest方法,接下来边和预览流程大致相同,会去调用Camera Service 中的ICameraDeviceUser的submitRequestList方法传入请求,之后将App实现的回调对象存入CameraDeviceImpl对象中。

f) onCaptureProgressed

一旦Request下发到Camera Service之后,当底层生成了Partial Meta Data数据,Camera Service会调用通过调用在打开相机设备时传入的ICameraDeviceCallback代理,通过其onResultReceived方法将数据传回Framework,之后调用App传入的CameraCaptureSession.CaptureCallback中的onCaputreProgressed方法将结果回传至App进行解析以及后处理。

g) onCaptureCompleted

一旦Request下发到Camera Service之后,当底层生成了Meta data数据,Camera Service会调用通过调用在打开相机设备时传入的ICameraDeviceCallback代理,通过其onResultReceived方法将数据传回Framework,之后调用App传入的CameraCaptureSession.CaptureCallback中的onCaputreCompleted方法将结果回传至App进行解析以及后处理。

h) onImageAvailable

之前已经通过两个回调接口onCaptureProgressed以及onCaptureCompleted方法将meta data上传到了App,一般情况下,图像数据会在他们之后上传,而且这个上传过程并不经过Camera Framework,而是通过BufferQueue来进行的,当Camera Service接收到底层传来的图像数据,便会立即调用processCaptureResult_3_4方法,该方法中会去调用BufferQueue中生产者角色的Surface的queueBuffer方法,将数据入队并通知消费者去消费,而此时的消费者正是App端的ImageReader,并经过一层层回调,最终会通过调用ImageReader的onImageAvailable方法,通知ImageReader去将数据取出,并做后期操作。

从上面的梳理不难发现,整个Camera Framework除了是对Camera Api v2的实现外,还承担着与Camera Service跨进程通信的任务,充当了一个位于App与Service之间的中转站的角色。

四、Camera App Demo

经过上面的梳理总结,我们已经对整个Camera Api v2接口以及实现都有了一个较为深入的认识,但是认识暂时仅仅停留在代码层面,为了更好理解其功能,接下来我们以一个简单的相机应用入手来加深下对接口的使用流程的理解:

该相机Demo比较简单,界面有两个元素,一个是用于预览显示的TextureView,以及一个用于拍照的按钮,整个代码就采用了一个MainActiviy,相机操作就在该类中进行,其主要代码如下:


package com.bruce.camerademo1;



import androidx.annotation.NonNull;

import androidx.appcompat.app.AppCompatActivity;

import androidx.core.app.ActivityCompat;



import android.Manifest;

import android.content.Context;

import android.content.pm.PackageManager;

import android.graphics.ImageFormat;

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.hardware.camera2.TotalCaptureResult;

import android.media.Image;

import android.media.ImageReader;

import android.os.Bundle;

import android.os.Environment;

import android.util.Log;

import android.util.Size;

import android.view.Surface;

import android.view.TextureView;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;



import java.io.File;

import java.io.FileOutputStream;

import java.nio.ByteBuffer;

import java.util.Arrays;



public class MainActivity extends AppCompatActivity {



    TextureView mTextureView = null;

    CameraManager mCamManager = null;

    CameraDevice mCamDevice = null;

    CameraCaptureSession mCamSession = null;

    Button mClick = null;



    //预览数据大小 1080 × 720

    private Size mPreviewSize = new Size(1080, 720);

    //拍照数据图像大小 1080 × 720

   private Size mCaptureSize = new Size(1080, 720);



    private Surface mPreviewSurface = null;

    private CaptureRequest.Builder mCaptureRequestBuilder = null;

    private CaptureRequest mCaptureRequest = null;

    private ImageReader mImageReader = null;

    static String LOG_TAG = "Camera";



    private void log(String s) {

        if (s.isEmpty()) {

            Log.e(LOG_TAG, "s is empty");

            return;

        }

        Log.i(LOG_TAG, "" + s);

    }



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }



    @Override

    protected void onResume() {

        super.onResume();

        mTextureView = findViewById(R.id.textview);

        mTextureView.setSurfaceTextureListener(textureListener);

        mClick = findViewById(R.id.button);

        mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(), ImageFormat.JPEG, 2);

        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {

            @Override

            public void onImageAvailable(ImageReader reader) {

                Image image = reader.acquireNextImage();

                ByteBuffer buffer = image.getPlanes()[0].getBuffer();

                byte[] bytes = new byte[buffer.remaining()];

                //由缓冲区存入字节数组

                buffer.get(bytes);

                //字节数组转换为jpeg格式图片,并存储在设备中

                doByte2JpegFile(bytes);

                image.close();

            }

        }, null /*mCameraHandler*/);

        mClick.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                try {

                    //创建CaptureRequest.Builder,TEMPLATE_STILL_CAPTURE代表了此Request是用于拍照

                    CaptureRequest.Builder b = mCamDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);

                    b.addTarget(mImageReader.getSurface());

                    mCamSession.capture(b.build(), new CameraCaptureSession.CaptureCallback() {

                        @Override

                        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {

                            super.onCaptureCompleted(session, request, result);

                            //返回result --> meta data

                        }

                    }, null);

                } catch (CameraAccessException e) {

                    e.printStackTrace();

                }

            }

        });

    }



    public TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {

        @Override

        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {



            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);

            }



            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);

            }



            //获取CameraManager服务

            mCamManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);

            try {

                //打开主摄

                mCamManager.openCamera("0", mStateCallback, null);

            } catch (CameraAccessException e) {

                e.printStackTrace();

            }

        }



        //App需要实现该接口,用于接收CameraDevice实例

        public CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

            @Override

            public void onOpened(CameraDevice camera) {

                if (camera != null) {

                    log("camera is not null");

                }

                log("onOpened");

                //返回CameraDevice,将其存入mCamDevice

                mCamDevice = camera;

                startPreview();

            }



            @Override

            public void onDisconnected(CameraDevice camera) {

                camera.close();

                mCamDevice = null;

            }



            @Override

            public void onError(CameraDevice camera, int error) {

                camera.close();

                mCamDevice = null;

            }

        };



        public void startPreview() {

            SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();

            // 设置TextureView 用于显示的缓冲区大小

            mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            //创建Surface,用于显示预览数据

            mPreviewSurface = new Surface(mSurfaceTexture);

            try {

                //创建CameraCaptureSession,App需要实现CameraCaptureSession.StateCallback用于接收CameraCaptureSession实例

                mCamDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {

                    @Override

                    public void onConfigured(CameraCaptureSession session) {

                        try {

                            //创建用于预览的CaptureRequest.Builder,进而得到CaptureRequest

                            CaptureRequest.Builder b = mCamDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

                            b.addTarget(mPreviewSurface);

                            CaptureRequest r = b.build();

                            mCamSession = session;

                            //下发预览需求

                            mCamSession.setRepeatingRequest(r, mPreviewCaptureCallback, null);

                        } catch (CameraAccessException e) {

                            e.printStackTrace();

                        }

                    }

                    CameraCaptureSession.CaptureCallback mPreviewCaptureCallback = new CameraCaptureSession.CaptureCallback() {

                        @Override

                        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {

                            //返回result --> meta data

                        }

                    };



                    @Override

                    public void onReady(CameraCaptureSession session) {

                        super.onReady(session);

                    }



                    @Override

                    public void onConfigureFailed(CameraCaptureSession session) {



                    }

                }, null);

            } catch (CameraAccessException e) {

                e.printStackTrace();

            }

        }

        @Override

        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            log("onSurfaceTextureSizeChanged Enter");

        }



        @Override

        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {

            log("onSurfaceTextureDestroyed Enter");

            return false;

        }



        @Override

        public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            log("onSurfaceTextureUpdated Enter");

        }

    };



    private String doByte2JpegFile(byte[]... jpeg) {

        File photo = new File(Environment.getExternalStorageDirectory(), "photo-test.jpg");

        log("dir : " + Environment.getExternalStorageDirectory());



        if (photo.exists()) {

            photo.delete();

            log("photo exists");

        } else {

            log("photo not exists");

        }



        try {

            FileOutputStream fos = new FileOutputStream(photo.getPath());

            log("photo path : " + photo.getPath());



            fos.write(jpeg[0]);

            fos.close();

        }

        catch (java.io.IOException e) {

            Log.e(LOG_TAG, "Exception in photoCallback", e);

        }



        log("get jpeg files done");

        return(null);

    }

}

代码比较简单,其主要逻辑如下:

a) 初始化

随着应用的打开,首先在MainActivity的onResume方法中去初始化用于拍照的按钮和接收数据的ImageReader,并且设置其各自回调方法。