文档章节

android camera2 API流程分析

天王盖地虎626
 天王盖地虎626
发布于 05/21 22:52
字数 1920
阅读 22
收藏 0

Android camera2 API流程分析
Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原来的camera的类并没有深入分析。在做项目的时候,由于需要涉及到这方面的知识,自己学了一下。由于本人英文也不是很优秀,看着看着还要看前人的总结。这个是在半年前就简单总结了一下,现在po上来。如有错误,敬请指教!

一、总体分析


Camera2流程示意图:


CameraManager:管理所有的摄像头(CameraDevice)设备的管理者,打开摄像头等功能。

 

CameraDevice:一个手机设备一般有两个摄像头(CameraDevice),前置和后置。该类通过CameraCharacteristics对象提供摄像头的硬件信息,配置信息和输出参数等。

 

CameraCaptureSession:通过CameraDevice 中createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)创建一个CaptureSession会话,所有的CaptureRequest和返回的data都在这个会话中进行。其中,该类中的capture (CaptureRequest request, CameraCaptureSession.CaptureCallback listener, Handler handler)的功能是捕获一次(one-shot),一般用于照相setRepeatingRequest (CaptureRequest request, CameraCaptureSession.CaptureCallbacklistener, Handler handler)是不停的发出capture的请求,也就是一直在捕获画面,一般用于捕获画面输出至预览界面或者录制视频。Capture()比setRepeatingRequest ()优先级高,当在setRepeatingRequest 时进行Capture,会先处理Capture,然后继续setRepeatingRequest 。(PS:可以根据平时使用相机时,首先我们看到的预览界面是setRepeatingRequest 显示出来的,当点击拍照时执行Capture,然后又出现预览界面继续实行setRepeatingRequest )。

 

CameraRequest:request中定义了照相效果的一些参数,并且必须使用addTarget()函数为这个request添加一个target surface,在最后CameraDevice返回的数据送到这个target surface中。在android camera2的API文档中,这个target surface可以是Surface View,Surface Texture,将返回的数据传递到预览界面中;还可以是MediaRecorder或mageReader,将返回的数据传给这两个类,进行进一步处理,形成视频文件或者图片。

 

TotalCaptureResult:继承CaptureResult类,CaptureResult继承CameraMetadata类。包含camera device的状态信息。


二、照相流程分析(参考Camera2Basic)


            Camera2Basic在显示预览界面和拍照时创建了一个session,两个request,mPreviewRequest和captureBuilder.build()分别将数据返回给预览界面和Image。


显示preview的代码:


private void openCamera(int width, int height) {
        setUpCameraOutputs(width, height);
        configureTransform(width, height);
        Activity activity = getActivity();
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);//根据mCameraId打开前置或者后置摄像头
                                                                            //mBackgroundHandler是处理打开摄像头的线程
                                                                            //mStateCallback打开摄像头后,进入这个回调函数
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
        }
}
 
 
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
        @Override
        public void onOpened(CameraDevice cameraDevice) {//若成功打开,进入这个函数
            // This method is called when the camera is opened.  We start camera preview here.
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();//创建显示预览界面的函数
        }
            //………
}
 
 
private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
 
            // We configure the size of default buffer to be the size of camera preview we want.
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
 
            // This is the output Surface we need to start preview.
            Surface surface = new Surface(texture);
 
            // We set up a CaptureRequest.Builder with the output Surface.
            mPreviewRequestBuilder
                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(surface);//target为surface,就是手机的界面
 
            // Here, we create a CameraCaptureSession for camera preview.
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),//拍照的session,注意这里有两个surface
                                                                                                //一个是手机的界面,一个是图片。
                                                                                                //也就是说,这个session形成的数据流,
                                                                                                //可以一个传向手机界面,一个形成图片
                                                                                                //具体看Caputre()或者SetRepeatingRequest函数里面的
                                                                                                //参数request的addtarget()里面的值
                                                                                                
                    new CameraCaptureSession.StateCallback() {
 
                        @Override
                        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                            // The camera is already closed
                            if (null == mCameraDevice) {
                                return;
                            }
 
                            // When the session is ready, we start displaying the preview.
                            mCaptureSession = cameraCaptureSession;
                            try {
                                // Auto focus should be continuous for camera preview.
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                // Flash is automatically enabled when necessary.
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
 
                                // Finally, we start displaying the camera preview.
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,//mPreviewRequest的target是手机界面的surface,就是形成预览
                                                                                    //因此需要setRepeatingRequest,持续捕获帧形成视频
                                        mCaptureCallback, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }
 
                      //……….
    }

CameraDevice.StateCallback:

当CameraDevice状态改变(打开或关闭)后调用该函数,一般在该函数执行如下功能:

创建CameraDevice.TEMPLATE_PREVIEW类型的previewRequest,设置addTarget()为preview的surface。

创建session,这个session有两个request,因此要把request的target surface都放到List中,createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)中的List为preview和ImageReader的surface。CameraCaptureSession.StateCallback里面进行setRepeatingCapture(),将捕获的画面显示在preview上。mCaptureCallback说是捕获完成后的回调函数,暂不分析。

拍照(捕获静态图像)的代码:

private void captureStillPicture() {
        try {
            final Activity activity = getActivity();
            if (null == activity || null == mCameraDevice) {
                return;
            }
            // This is the CaptureRequest.Builder that we use to take a picture.
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mImageReader.getSurface()); //target是image
 
            // Use the same AE and AF modes as the preview.
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
 
            // Orientation
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
 
            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {
 
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                               TotalCaptureResult result) {
                    showToast("Saved: " + mFile);
                    unlockFocus();
                }
            };
 
            mCaptureSession.stopRepeating();//停掉之前的setrepeating的持续不断的捕获
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);//capture的时候,数据流形成image
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

类似上述分析,不过此时request的类型cameraDevice.TEMPLATE_STILL_CAPTURE并且addTarget()的对象ImageReader,另外setRepeatingCapture换成了capture。即捕获一个frame,返回至ImageReader中形成图片。


三、录像流程分析(参考Camera2Video)
private void openCamera(int width, int height) {
        final Activity activity = getActivity();
        if (null == activity || activity.isFinishing()) {
            return;
        }
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            String cameraId = manager.getCameraIdList()[0];//后置摄像头的id
 
            // Choose the sizes for camera preview and video recording
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap map = characteristics
                    .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
            mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                    width, height, mVideoSize);
 
            int orientation = getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            } else {
                mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
            }
            configureTransform(width, height);
            mMediaRecorder = new MediaRecorder();
            manager.openCamera(cameraId, mStateCallback, null);//打开相机,进入回调函数
           //……
}
 
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
        @Override
        public void onOpened(CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            startPreview(); //进入这个函数
            mCameraOpenCloseLock.release();
            if (null != mTextureView) {
                configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
            }
        }
        //……
}
 
    private void startPreview() {
        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
            return;
        }
        try {
            setUpMediaRecorder();//设置mediarecoder的参数,具体的介绍看android developer文档
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);//此时request的参数是record
            List<Surface> surfaces = new ArrayList<Surface>();
 
            Surface previewSurface = new Surface(texture);
            surfaces.add(previewSurface);
            mPreviewBuilder.addTarget(previewSurface);//target是预览界面
 
            Surface recorderSurface = mMediaRecorder.getSurface();
            surfaces.add(recorderSurface);
            mPreviewBuilder.addTarget(recorderSurface);//target是mediarecorder
 
            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
 
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    mPreviewSession = cameraCaptureSession;
                    updatePreview();//进入这个函数
                }
 
//……….
            }, mBackgroundHandler);}
            
 
//此为mediaRecoder的设置,具体见MediaRecorder
    private void setUpMediaRecorder() throws IOException {
        final Activity activity = getActivity();
        if (null == activity) {
            return;
        }
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setOutputFile(getVideoFile(activity).getAbsolutePath());
        mMediaRecorder.setVideoEncodingBitRate(10000000);
        mMediaRecorder.setVideoFrameRate(30);
        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int orientation = ORIENTATIONS.get(rotation);
        mMediaRecorder.setOrientationHint(orientation);
        mMediaRecorder.prepare();
    }
 
 
 
    private void updatePreview() {
        if (null == mCameraDevice) {
            return;
        }
        try {
            setUpCaptureRequestBuilder(mPreviewBuilder);
            mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);//session发送请求,持续捕获帧
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
}
 
//进行上面设置之后,点击button,执行startRecordingVideo()函数,mMediaRecorder.start();开始录制视频
 
    private void startRecordingVideo() {
        try {
            // UI
            mButtonVideo.setText(R.string.stop);
            mIsRecordingVideo = true;
 
            // Start recording
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }
//再次点击button,停止录制。源代码会出现一些问题,应该先关闭capture之后再停止录制,具体问题在stackoverflow里面有写。
 
  private void stopRecordingVideo() {
        // UI
        mIsRecordingVideo = false;
        mButtonVideo.setText(R.string.record);
    //modifying!!!!~~~~~android官方的demo里面在停止拍摄的时候会卡,所以改了一点
    try {
        // Abort all pending captures.
        cameraCaptureSession.abortCaptures();
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }    
 
        // Stop recording
        mMediaRecorder.stop();
        mMediaRecorder.reset();
        Activity activity = getActivity();
        if (null != activity) {
            Toast.makeText(activity, "Video saved: " + getVideoFile(activity),
                    Toast.LENGTH_SHORT).show();
        }
        startPreview();//最后执行这个函数,重新开始预览,准备录制视频
}
    }


在Camera2Video中:只创建了一个session,一个request有两个target surface-MediaRecorder和Preview。SetRepeatingRequest()不停的捕获画面一方面显示在preview上,另一方面形成视频流。当MediaRecorder.start()时,开始录制,之前SetRepeatingRequest的帧(frame)抛弃掉,从start开始帧输出到指定的文件中。
 

本文转载自:https://blog.csdn.net/u011404910/article/details/50765687

天王盖地虎626

天王盖地虎626

粉丝 28
博文 487
码字总数 20672
作品 0
南京
私信 提问
android6.0源码分析之Camera API2.0简介

前面几篇主要分析的是android Camera API1.0的架构以及初始化流程,而google在android5.0(Lollipop)开始对Camera的架构进行了调整,为了适应HAL3,新添加实现了CameraDeviceClient,而Camer...

天王盖地虎626
05/21
0
0
Android Camera模块解析之视频录制

《Android Camera架构》 《Android Camera进程间通信类总结》 《Android Camera模块解析之拍照》 《Android Camera模块解析之视频录制》 《Android Camera原理之CameraDeviceCallbacks回调模...

天王盖地虎626
06/18
0
0
Android使用Camera2获取预览数据

一、Camera2简介 Camera2是Google在Android 5.0后推出的一个全新的相机API,Camera2和Camera没有继承关系,是完全重新设计的,且Camera2支持的功能也更加丰富,但是提供了更丰富的功能的同时...

丸子码农
07/09
0
0
音视频直播技术--Android视频采集(Camera2)

介绍 今天为大家介绍一下如何在 Android 上进行视频采集。在 Android 系统下有两套 API 可以进行视频采集,它们是 Camera 和 Camera2 。Camera是以前老的 API ,从 Android 5.0(21)之后就已经...

音视频直播技术专家
2017/07/24
0
0
JellyBean Camera Service 4.1.2和4.2.1两版本变化

4.2 CameraService的部分代码,和4.1的版本的区别还是比较大的,具体变化多大还没来得及分析.先从他们的代码布局变化看起吧 AndroidJellyBean4.1.2 Camera Service代码布局: frameworks/av/se...

Jerikc
2013/03/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

适合钱包应用开发的ERC20代币数据集

Erc20Tokens数据集包含超过1000种主流的以太坊ERC20代币的描述数据清单和图标,可用于钱包等区块链应用的开发,支持使用Java、Python、Php、NodeJs、C#等各种开发语言查询主流ERC20代币的相关...

汇智网教程
30分钟前
1
0
micro微服务 基础组件的组织方式

micro微服务 基础组件的组织方式 简介 micro是go语言实现的一个微服务框架,该框架自身实现了为服务常见的几大要素,网关,代理,注册中心,消息传递,也支持可插拔扩展。本本通过micro中的一...

魂祭心
56分钟前
4
0
简单的博客系统(三)使用Django的后台管理功能

Django新建项目和应用后,自带有后台管理功能,可直接使用 创建后台管理员账户 (demosite) E:\PycharmProjects\demosite>python manage.py createsuperuserUsername: adminEmail address:...

ZeroBit
今天
3
0
The /usr/local/mysql/data directory is not owned by the 'mysql' to '_mysql' user

20190720 经过前两天折腾环境,重装了 apache 和 mysql 之后,今天调试程序是突然发现,本机的 mysql 起不来了! 在启动面板上,显示有这样一行小字 (抱歉!光顾着解决问题,没有记录下来图片...

wwzzhh166
今天
4
0
centos安装增强功能出现kernel headers not found for target kernel解决办法

最近新安装一个centos虚拟机,在安装增强功能的时候出现了,kernel headers not found for target kernel的错误。特记下我的解决方案。 1.update kernel yum update kernel -y 2.Install the...

mbzhong
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部