如何让 CameraDevice 在 camera2 中工作?

Posted

技术标签:

【中文标题】如何让 CameraDevice 在 camera2 中工作?【英文标题】:How do I get a CameraDevice working in camera2? 【发布时间】:2017-02-28 09:46:09 【问题描述】:

我一直在关注 camera2 文档,尝试使用 ImageReader 实现非预览相机,但是当我到达 cameraDevice.createCaptureSession(outputs, mccsStateCallback, cameraHandler); 时,cameraDevice 对象为空,尽管我确信它应该是在 CameraCaptureSession.StateCallback 事件监听器中分配(也没有被触发,因为 CameraCaptureSession 本身为空)。要么我完全错了,要么我错过了一些大事。这是我的代码:

private CameraDevice cameraDevice;
private String cameraId;
private Handler cameraHandler = new Handler();
private CameraCharacteristics cameraCharacteristics;
private ImageReader jpgReader;
Bitmap bitmap;
private Handler imgHandler = new Handler();
private CameraCaptureSession mSession;
private CameraManager cameraManager;

private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() 
    @Override
    public void onOpened(@NonNull CameraDevice camera) 
        cameraDevice = camera;
    

    @Override
    public void onDisconnected(@NonNull CameraDevice camera) 
        cameraDevice.close();
    

    @Override
    public void onError(@NonNull CameraDevice camera, int error) 
        cameraDevice.close();
        cameraDevice = null;
    
;

OnImageAvailableListener imageAvailableListener = new OnImageAvailableListener() 
    @Override
    public void onImageAvailable(ImageReader reader) 
        Image image = reader.acquireLatestImage();
        Image.Plane[] planes = image.getPlanes();
        Buffer buffer = planes[0].getBuffer().rewind();
        bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
        bitmap.copyPixelsFromBuffer(buffer);
        mFaceOverlayView.setBitmap(bitmap);

    
;

private CameraCaptureSession.StateCallback mccsStateCallback = new CameraCaptureSession.StateCallback() 
    @Override
    public void onConfigured(@NonNull CameraCaptureSession session) 
        try 
            if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(getBaseContext(), android.Manifest.permission.CAMERA)) 
                mSession = session;
                CaptureRequest.Builder request = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                request.addTarget(jpgReader.getSurface());
                mSession.setRepeatingRequest(request.build(), new CameraCaptureSession.CaptureCallback() 
                    @Override
                    public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) 
                        super.onCaptureCompleted(session, request, result);
                    
                , null);
                cameraManager.openCamera(cameraId, mStateCallback, cameraHandler);
            
         catch (Exception ex) 
            ex.printStackTrace();
        
    

    @Override
    public void onConfigureFailed(CameraCaptureSession session) 

    
;

private void initialiseCamera() 
    cameraManager = (CameraManager) FilesPlayer.this.getSystemService(Context.CAMERA_SERVICE);
    try 
        cameraId = getFrontFacingCameraId(cameraManager);
        if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)) 

            cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);

            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            android.util.Size[] jpegSizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);

            Size biggestSize = new Size(0, 0);
            for (Size size : jpegSizes) 
                if (size.getHeight() >= biggestSize.getHeight() && size.getWidth() >= biggestSize.getWidth()) 
                    biggestSize = size;
                
            
            jpgReader = ImageReader.newInstance(biggestSize.getWidth(), biggestSize.getHeight(), ImageFormat.JPEG, 1);
            jpgReader.setOnImageAvailableListener(imageAvailableListener, imgHandler);
            List<Surface> outputs = Arrays.asList(jpgReader.getSurface());
            cameraDevice.createCaptureSession(outputs, mccsStateCallback, cameraHandler);

        
        else
            Log.d("NOOO","NOOOO");
        

     catch (Exception ex) 
        ex.printStackTrace();
    


private String getFrontFacingCameraId(CameraManager cameraManager) 
    try 
        for (String id : cameraManager.getCameraIdList()) 
            CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
            Integer cameraOrientation = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
            if (cameraOrientation != null && cameraOrientation == CameraMetadata.LENS_FACING_FRONT) 
                return id;
            
        

     catch (CameraAccessException ex) 
        ex.printStackTrace();
    
    return null;

编辑:我设法通过在initialiseCamera() 中的cameraDevice.createCaptureSession(outputs, mccsStateCallback, cameraHandler); 之前放置cameraManager.openCamera(cameraId,mStateCallback,cameraHandler); 来使CameraDevice 自行分配。 现在我遇到了一个问题,即相机输出没有被定向到 ImageReader 的表面,并且我在控制台中收到错误消息 E/Surface: getSlotFromBufferLocked: unknown buffer: 0xa1ae7000

【问题讨论】:

在onImageAvailable方法中处理完图像对象后,需要调用image.close();释放它。另外我不确定是否可以仅使用bitmap.copyPixelsFromBuffer(buffer); 从 JPEG 创建位图,因为 JPEG 是压缩格式。 【参考方案1】:

这是一个简短的说明。

请务必在清单中包含权限。还要确保用户可以通过 ActivityCompact 和 ContextCompact 向应用授予权限。在 Android 6.0 及更高版本上,权限也由用户在运行时授予。请参阅文档here。

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) 

    // Permission is not granted
    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) 
        // Show an explanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.
     else 
        // No explanation needed; request the permission
        ActivityCompat.requestPermissions(thisActivity,
                new String[]Manifest.permission.READ_CONTACTS,
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    
 else 
    // Permission has already been granted

【讨论】:

以上是关于如何让 CameraDevice 在 camera2 中工作?的主要内容,如果未能解决你的问题,请参考以下文章

调用需要 API 级别 21(当前最低为 17):android.hardware.camera2.CameraDevice.StateCallback

当应用程序处于 onStop 状态时尝试录制视频时,CameraDevice.StateCallback 获取 onError(值 3)

Android Camera(二)

Android Camera 打开预览流程分析-- Camera 连接到CameraService 过程分析

Android Camera 打开预览流程分析-- Camera 连接到CameraService 过程分析

Android Camera 打开预览流程分析-- Camera 连接到CameraService 过程分析