Android Camera2 API 后台服务

最近在搞CameraAPP需要将Camera2弄成一个后台服务,发现跟预览的Activity没多大变动只是加了Service,和一些简单的修改。之前的公司也用到Camera2,发现用到的时候还是蛮多的所以记录一下,代码在文章末尾

camera2的结构如下,主要是通过相机管理器(CameraManager)获得相机设备(CameraDevice),然后再开启一个控制相机的会话,最后发送 拍照、预览、录像等请求。

Camera流程大概如下

1.获取Camera2服务管理器,遍历摄像头,打开每一个摄像头

    public void onCreate() {
        super.onCreate();
        mActivity = this;
        //获取Camera管理器
        CameraManager manager = (CameraManager) this.getSystemService("camera");
        try {
            String[] ids = manager.getCameraIdList();
            mCameraNum = ids.length ;
            mCameraIds = ids;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        mBackgroundThread = new HandlerThread[mCameraNum];
        mBackgroundHandler = new Handler[mCameraNum];
        mCameraOpenCloseLock = new Semaphore[mCameraNum];
        mPreviewBuilder = new CaptureRequest.Builder[mCameraNum];
        mPreviewSession =new CameraCaptureSession[mCameraNum];
        mCameraDevice = new CameraDevice[mCameraNum];
        mStateCallback =new StateCallback[mCameraNum];
        mVideoSize = new Size[mCameraNum];
        mPreviewSize = new Size[mCameraNum];
        mImageReader = new RefCountedAutoCloseable[mCameraNum];
        mFrameListener = new FrameListener[mCameraNum];
        for (int i = 0; i < mCameraNum; i++) {

            mCameraOpenCloseLock[i]= new Semaphore(1);
            mStateCallback[i] = new StateCallback(i);

        }
        int width =1920;
        int height = 1080;
        mOpenCameraList.clear();
       //遍历摄像头,分别打开
        for (int i = 0; i < mCameraNum; i++) {
            int CameraId = Integer.valueOf(mCameraIds[i]);
            mFrameListener[i] = new FrameListener(CameraId,this);
            if(CameraId < 100 ){
                Log.e(TAG,"只打开USB摄像头 skip:"+mCameraIds[i]);
                continue;
            }
            mOpenCameraList.add(CameraId);
            startBackgroundThread(i);
            打开摄像头
            openCamera(i,width, height);
        }
        //设置前台服务
        bindNotification("Launcher 进程");
    }

1.获取摄像头参数,设置图像回调,打开摄像头

private void openCamera(int cameraNum, int width, int height) {
    if (!hasPermissionsGranted(VIDEO_PERMISSIONS)) {
        requestVideoPermissions();
        return;
    }
    CameraManager manager = (CameraManager) this.getSystemService(this.CAMERA_SERVICE);

    try {
        if (!mCameraOpenCloseLock[cameraNum].tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw new RuntimeException("Time out waiting to lock camera opening.");
        }
        Log.e(TAG, String.valueOf(manager.getCameraIdList().length));
        String cameraId = manager.getCameraIdList()[cameraNum];
        mCameraIds[cameraNum] = cameraId;
        // Choose the sizes for camera preview and video recording
        获取摄像头的参数
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

        if (map == null) {
            throw new RuntimeException("Cannot get available preview/video sizes");
        }

        mVideoSize[cameraNum] = new Size(4096,2160);
        mPreviewSize[cameraNum] = new Size(4096,2160);
        int orientation = getResources().getConfiguration().orientation;

        configureTransform(cameraNum,width, height);

        if (mImageReader[cameraNum] == null || mImageReader[cameraNum].getAndRetain() == null) {
          //设置摄像头的图像回调
            mImageReader[cameraNum] = new RefCountedAutoCloseable<>(
                    ImageReader.newInstance(mPreviewSize[cameraNum].getWidth(),
                            mPreviewSize[cameraNum].getHeight(), ImageFormat.YUV_420_888, /*maxImages*/5));

        }
        if (mImageReader[cameraNum] !=null){
            mImageReader[cameraNum].get().setOnImageAvailableListener(mFrameListener[cameraNum]
                    , mBackgroundHandler[cameraNum]);
        }
        Log.d(TAG,"openCamera:"+cameraId);
       //打开摄像头,打开成功会调用到 mStateCallback.onOpened
        manager.openCamera(cameraId, mStateCallback[cameraNum], null);
    } catch (CameraAccessException e) {
    } catch (NullPointerException e) {
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera opening.");
    }
}
class StateCallback extends CameraDevice.StateCallback {
    int cameraNum;
    public StateCallback(int cameraNum) {
        super();
        this.cameraNum = cameraNum;
    }
    //打开成功会调用到这里
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
        mCameraDevice[cameraNum] = cameraDevice;
        startPreview(cameraNum);
        mCameraOpenCloseLock[cameraNum].release();
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        mCameraOpenCloseLock[cameraNum].release();
        cameraDevice.close();
        mCameraDevice[cameraNum] = null;
    }

    @Override
    public void onError(@NonNull CameraDevice cameraDevice, int error) {
        mCameraOpenCloseLock[cameraNum].release();
        cameraDevice.close();
        mCameraDevice[cameraNum] = null;
    }

};

1.打开成功开始重定向输出对象到ImageReader

    private void startPreview(final int cameraNum) {
        if (null == mCameraDevice[cameraNum]  || null == mPreviewSize[cameraNum]) {
            return;
        }
        try {
            closePreviewSession(cameraNum);
            //设置Camera为预览输出
            mPreviewBuilder[cameraNum] = mCameraDevice[cameraNum].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            List<Surface> surfaces;
              //获取mImageReader的SureFace ,就能通过ImageReader的图像回调获取数据
         mPreviewBuilder[cameraNum].addTarget(mImageReader[cameraNum].get().getSurface());
                surfaces = Arrays.asList(
                          mImageReader[cameraNum].get().getSurface()
                );

            mCameraDevice[cameraNum].createCaptureSession(surfaces,
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession session) {
                            mPreviewSession[cameraNum] = session;
                            updatePreview(cameraNum);
                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                        }
                    }, mBackgroundHandler[cameraNum]);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

图像数据回调

class FrameListener implements   ImageReader.OnImageAvailableListener{
        int cameraNum;
        Context context;
        public FrameListener(int cameraNum, Context context) {
            this.cameraNum = cameraNum;
            this.context = context;
        }
        long frameID = 0;
        @Override
        public void onImageAvailable(ImageReader reader) {

            Image image = reader.acquireNextImage();
            if (image !=null) {
                 frameID++;
                 int width = image.getWidth();//1920
                 int height = image.getHeight();//1080
                 //摄像头1920*1080 y长度2073600,uv1036800
                 //获取y数据地址
                 ByteBuffer ybuffer = image.getPlanes()[0].getBuffer();
                //u数据地址,一般uv数据都是交替存放,所以这里包含有uv的数据
                 ByteBuffer ubuffer = image.getPlanes()[1].getBuffer();
                 //ByteBuffer vbuffer = image.getPlanes()[2].getBuffer();
                 int yLen = ybuffer.remaining();
                 int uLen = ubuffer.remaining();
                 int vLen = vbuffer.remaining();
                 byte[] yBytes = new byte[yLen];
                 byte[] uBytes = new byte[uLen];
                 //byte[] vBytes = new byte[vLen];
                 byte[] yuvBytes = new byte[3110400];
                 ybuffer.get(yBytes);
                 ubuffer.get(uBytes);
                 //vbuffer.get(vBytes);


                 System.arraycopy(yBytes,0,yuvBytes,0,2073600);
                 System.arraycopy(uBytes,0,yuvBytes,2073600,1036800);
                 nativeReadImageBuf(width,height,image.getFormat(),yuvBytes, 3110400, mOpenCameraList.size(), mOpenCameraList.indexOf(cameraNum));
                image.close();
            }
        }
    }

当服务起来后会直接打开摄像头,获取回调数据

运行一段时间后服务自动停止,原因是没有和APP活动在同一个生命周期

使用

        /**
     * 设置为前台服务
     * @param title
     */
protected void bindNotification(String title){
        String CHANNEL_ONE_ID = "com.example.android.camera2videopushnew";
        String CHANNEL_ONE_NAME = "com.example.android.camera2videopushnew.name";

        Notification notification = null;

        NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
                CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_LOW);
        notificationChannel.enableLights(false);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.createNotificationChannel(notificationChannel);

        notification = new Notification.Builder(this,CHANNEL_ONE_ID)
                .setContentTitle(title)
                .setContentText(title)
                .build();

        notification.flags |= Notification.FLAG_NO_CLEAR;
        startForeground(1, notification);
    }

结束

APP代码链接:【免费】AndroidCamera2后台服务APP资源-CSDN文库

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
如果您想在您的应用程序中打开相机并捕获照片或视频,则需要使用相机API。在Android中,有两种不同的相机API可供使用:Camera APICamera2 API。 使用Camera API非常简单,但相对较老,Camera2 API则更复杂,但功能更强大。对于较新的设备,建议使用Camera2 API。 以下是使用Camera2 API后台打开相机并捕获照片的基本步骤: 1. 检查设备是否支持Camera2 API。 2. 创建一个CameraCaptureSession,用于捕获图像。 3. 创建一个CaptureRequest,指定捕获图像的参数。 4. 打开相机设备,并将相机设备与CameraCaptureSession和CaptureRequest相关联。 5. 在后台线程上进行相机预览和捕获。 您可以使用以下代码打开相机并捕获图像: ``` private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { String cameraId = manager.getCameraIdList()[0]; CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); Size[] sizes = map.getOutputSizes(SurfaceTexture.class); int width = sizes[0].getWidth(); int height = sizes[0].getHeight(); ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1); List<Surface> outputSurfaces = new ArrayList<Surface>(2); outputSurfaces.add(reader.getSurface()); outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture())); final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(reader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); int rotation = getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); reader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Image image = null; try { image = reader.acquireLatestImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.capacity()]; buffer.get(bytes); save(bytes); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (image != null) { image.close(); } } } }, mBackgroundHandler); mCameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { session.capture(captureBuilder.build(), null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } ``` 请注意,此代码是使用Camera2 API编写的,并且需要在后台线程上运行。您需要在应用程序中设置后台线程,并将其与相机相关联。 此外,您需要在AndroidManifest.xml文件中添加CAMERA权限: ``` <uses-permission android:name="android.permission.CAMERA" /> ``` 希望这可以帮助您开始在您的应用程序中打开相机并捕获图像!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hmbbPdx_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值