APP使用相机CameraX

Posted xhBruce

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APP使用相机CameraX相关的知识,希望对你有一定的参考价值。

APP使用相机

Android 开发者>文档>指南 - 选择相机库


CameraX 示例应用

SampleDescription
CameraXBasicDemonstrates how to use CameraX APIs.
CameraXAdvancedDemonstrates how to use CameraX APIs with TFLite and others
CameraXVideoDemonstrates how to use CameraX VideoCapture API.
Camera2BasicDemonstrates capturing JPEG, RAW and DEPTH images, e.g. unprocessed pixel data directly from the camera sensor.
Camera2SlowMotionDemonstrates capturing high-speed video in a constrained camera capture session.
Camera2VideoDemonstrates recording video using the Camera2 API and MediaRecorder.
Camera2ExtensionsDemonstrates Camera2 extension live preview and still capture.
HdrViewfinderDemonstrates use of RenderScript to display a live HDR feed from camera frames using Camera2 API.

0. CameraX 概览 | android Jetpack 的一部分

1. CameraX获取Context.CAMERA_SERVICE服务

CameraXBasicsetUpCamera()方法第一次调用 ProcessCameraProvider.getInstance() 期间,初始化 CameraX 会cameraFactory.getAvailableCameraIds()枚举和查询设备上可用摄像头的特性。


其中cameraFactory在com/android/example/cameraxbasic/MainApplication.kt中调用CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())初始化对象Camera2CameraFactory、CameraXConfig

1.1 获取Context.CAMERA_SERVICE服务

下面获取Context.CAMERA_SERVICE服务,实际获取 SystemServiceRegistry.java中注册的CameraManager.java。通过CameraManagerGlobal连接CameraService

androidx/camera/camera2/internal/Camera2CameraFactory.java
androidx/camera/camera2/internal/compat/CameraManagerCompat.java
androidx/camera/camera2/internal/compat/CameraManagerCompatApi29Impl.java
androidx/camera/camera2/internal/compat/CameraManagerCompatApi28Impl.java
androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java

frameworks/base/core/java/android/app/SystemServiceRegistry.java

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public ICameraService getCameraService() 
    synchronized(mLock) 
        connectCameraServiceLocked();
        if (mCameraService == null && !sCameraServiceDisabled) 
            Log.e(TAG, "Camera service is unavailable");
        
        return mCameraService;
    


private void connectCameraServiceLocked() 
    // Only reconnect if necessary
    if (mCameraService != null || sCameraServiceDisabled) return;

    Log.i(TAG, "Connecting to camera service");

    IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
    if (cameraServiceBinder == null) 
        // Camera service is now down, leave mCameraService as null
        return;
    
    try 
        cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
     catch (RemoteException e) 
        // Camera service is now down, leave mCameraService as null
        return;
    

    ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);

    try 
        CameraMetadataNative.setupGlobalVendorTagDescriptor();
     catch (ServiceSpecificException e) 
        handleRecoverableSetupErrors(e);
    

    try 
        CameraStatus[] cameraStatuses = cameraService.addListener(this);
        for (CameraStatus c : cameraStatuses) 
            onStatusChangedLocked(c.status, c.cameraId);

            if (c.unavailablePhysicalCameras != null) 
                for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) 
                    onPhysicalCameraStatusChangedLocked(
                            ICameraServiceListener.STATUS_NOT_PRESENT,
                            c.cameraId, unavailPhysicalCamera);
                
            
        
        mCameraService = cameraService;
     catch(ServiceSpecificException e) 
        // Unexpected failure
        throw new IllegalStateException("Failed to register a camera service listener", e);
     catch (RemoteException e) 
        // Camera service is now down, leave mCameraService as null
    

    try 
        ConcurrentCameraIdCombination[] cameraIdCombinations =
                cameraService.getConcurrentCameraIds();
        for (ConcurrentCameraIdCombination comb : cameraIdCombinations) 
            mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
        
     catch (ServiceSpecificException e) 
        // Unexpected failure
        throw new IllegalStateException("Failed to get concurrent camera id combinations",
                e);
     catch (RemoteException e) 
        // Camera service died in all probability
    

1.2 枚举和查询设备上可用摄像头

CameraX 初始化时 cameraFactory.getAvailableCameraIds()枚举和查询设备上可用摄像头的特性。最终调用CameraSelectionOptimizer.getSelectedAvailableCameraIds(this, availableCamerasSelector)通过cameraFactory.getCameraManager().getCameraIdList()获取。


FWK层中CameraManager.java中CameraManagerGlobal的getCameraIdList():connectCameraServiceLocked()连接CameraService服务CAMERA_SERVICE_BINDER_NAME = "media.camera"



摄像头限制器 如果传递给 CameraXConfig.Builder.setAvailableCamerasLimiter() 的 CameraSelector 过滤掉了某个摄像头,则 CameraX 在运行时会假定该摄像头不存在。CameraSelector.DEFAULT_BACK_CAMERA 后置摄像头LENS_FACING_BACK = 1CameraSelector.DEFAULT_FRONT_CAMERA 前置摄像头LENS_FACING_FRONT = 0

com/android/example/cameraxbasic/fragments/CameraFragment.kt
androidx/camera/core/CameraX.java
androidx/camera/core/impl/CameraRepository.java

public void init(@NonNull CameraFactory cameraFactory) throws InitializationException 
    synchronized (mCamerasLock) 
        try 
            Set<String> camerasList = cameraFactory.getAvailableCameraIds();
            for (String id : camerasList) 
                Logger.d(TAG, "Added camera: " + id);
                mCameras.put(id, cameraFactory.getCamera(id));
            
         catch (CameraUnavailableException e) 
            throw new InitializationException(e);
        
    

androidx/camera/camera2/internal/Camera2CameraFactory.java

public Camera2CameraFactory(@NonNull Context context,
        @NonNull CameraThreadConfig threadConfig,
        @Nullable CameraSelector availableCamerasSelector) throws InitializationException 
    mThreadConfig = threadConfig;
    mCameraStateRegistry = new CameraStateRegistry(DEFAULT_ALLOWED_CONCURRENT_OPEN_CAMERAS);
    mCameraManager = CameraManagerCompat.from(context, mThreadConfig.getSchedulerHandler());

    List<String> optimizedCameraIds = CameraSelectionOptimizer.getSelectedAvailableCameraIds(
            this, availableCamerasSelector);
    mAvailableCameraIds = getBackwardCompatibleCameraIds(optimizedCameraIds);


public Set<String> getAvailableCameraIds() 
    // Use a LinkedHashSet to preserve order
    return new LinkedHashSet<>(mAvailableCameraIds);

androidx/camera/camera2/internal/CameraSelectionOptimizer.java

static List<String> getSelectedAvailableCameraIds(
        @NonNull Camera2CameraFactory cameraFactory,
        @Nullable CameraSelector availableCamerasSelector)
        throws InitializationException 
    try 
        List<String> availableCameraIds = new ArrayList<>();
        List<String> cameraIdList =
                Arrays.asList(cameraFactory.getCameraManager().getCameraIdList());
        if (availableCamerasSelector == null) 
            for (String id : cameraIdList) 
                availableCameraIds.add(id);
            
            return availableCameraIds;
        

        // Skip camera ID by heuristic: 0 is back lens facing, 1 is front lens facing.
        String skippedCameraId;
        try 
            Integer lensFacingInteger = availableCamerasSelector.getLensFacing();
            skippedCameraId = decideSkippedCameraIdByHeuristic(
                    cameraFactory.getCameraManager(), lensFacingInteger, cameraIdList);
         catch (IllegalStateException e) 
            // Don't skip camera if there is any conflict in camera lens facing.
            skippedCameraId = null;
        

        List<CameraInfo> cameraInfos = new ArrayList<>();

        for (String id : cameraIdList) 
            if (id.equals(skippedCameraId)) 
                continue;
            
            Camera2CameraInfoImpl cameraInfo = cameraFactory.getCameraInfo(id);
            cameraInfos.add(cameraInfo);
        

        List<CameraInfo> filteredCameraInfos =
                availableCamerasSelector.filter(cameraInfos);

        for (CameraInfo cameraInfo : filteredCameraInfos) 
            String cameraId = ((CameraInfoInternal) cameraInfo).getCameraId();
            availableCameraIds.add(cameraId);
        

        return availableCameraIds;
     catch (CameraAccessExceptionCompat e) 
        throw new InitializationException(CameraUnavailableExceptionHelper.createFrom(e));
     catch (CameraUnavailableException e) 
        throw new InitializationException(e);
    

2. 拍照按钮执行

除了setUpCamera()初始化CameraX,其中重要查看updateCameraUi()布局更新;camera_capture_button按钮cameraUiContainerBinding?.cameraCaptureButton?.setOnClickListener设置
com/android/example/cameraxbasic/fragments/CameraFragment.kt

// Listener for button used to capture photo
cameraUiContainerBinding?.cameraCaptureButton?.setOnClickListener 

    // Get a stable reference of the modifiable image capture use case
    imageCapture?.let  imageCapture ->

        // Create output file to hold the image
        val photoFile = createFile(outputDirectory, FILENAME, PHOTO_EXTENSION)

        // Setup image capture metadata
        val metadata = Metadata().apply 

            // Mirror image when using the front camera
            isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
        

        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
                .setMetadata(metadata)
                .build()

        // Setup image capture listener which is triggered after photo has been taken
        imageCapture.takePicture(
                outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback 
            override fun onError(exc: ImageCaptureException) 
                Log.e(TAG, "Photo capture failed: $exc.message", exc)
            

            override fun onImageSaved(output: ImageCapture.OutputFileResults) 
                val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
                Log.d(TAG, "Photo capture succeeded: $savedUri")

                // We can only change the foreground Drawable using API level 23+ API
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
                    // Update the gallery thumbnail with latest picture taken
                    setGalleryThumbnail(savedUri)
                

                // Implicit broadcasts will be ignored for devices running API level >= 24
                // so if you only target API level 24+ you can remove this statement
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 
                    requireActivity().sendBroadcast(
                            Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri)
                    )
                

                // If the folder selected is an external media directory, this is
                // unnecessary but otherwise other apps will not be able to access our
                // images unless we scan them using [MediaScannerConnection]
                val mimeType = MimeTypeMap.getSingleton()
                        .getMimeTypeFromExtension(savedUri.toFile().extension)
                MediaScannerConnection.scanFile(
                        context,
                        arrayOf(savedUri.toFile().absolutePath),
                        arrayOf(mimeType)
                )  _, uri ->
                    Log.d(TAG, "Image capture scanned into media store: $uri")
                
            
        )

        // We can only change the foreground Drawable using API level 23+ API
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 

            // Display flash animation to indicate that photo was captured
            fragmentCameraBinding.root.postDelayed(
                fragmentCameraBinding.root.foreground = ColorDrawable(Color.WHITE)
                fragmentCameraBinding.root.postDelayed(
                         fragmentCameraBinding.root.foreground = null , ANIMATION_FAST_MILLIS)
            , ANIMATION_SLOW_MILLIS)
        
    

3. 前置/后置 摄像头切换

查看updateCameraUi()布局更新;camera_switch_button按钮cameraUiContainerBinding?.cameraSwitchButton?.let设置,CameraSelector.LENS_FACING_BACK、CameraSelector.LENS_FACING_FRONT 前后置属性,bindCameraUseCases()更新预览preview、捕获imageCapture和分析imageAnalyzer用例;observeCameraState(camera?.cameraInfo!!)监听Camera状态


com/android/example/cameraxbasic/fragments/CameraFragment.kt

// Setup for button used to switch cameras
cameraUiContainerBinding?.cameraSwitchButton?.let 

    // Disable the button until the camera is set up
    it.isEnabled = false

    // Listener for button used to switch cameras. Only called if the button is enabled
    it.setOnClickListener 
        lensFacing = if (CameraSelector.LENS_FACING_FRONT == lensFacing) 
            CameraSelector.LENS_FACING_BACK
         else 
            CameraSelector.LENS_FACING_FRONT
        
        // Re-bind use cases to update selected camera
        bindCameraUseCases()
    


/** Declare and bind preview, capture and analysis use cases */
private fun bindCameraUseCases() 

    // Get screen metrics used to setup camera for full screen resolution
    val metrics = windowManager.getCurrentWindowMetrics().bounds
    Log.d(TAG, "Screen metrics: $metrics.width() x $metrics.height()")

    val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())
    Log.d(TAG, "Preview aspect ratio: $screenAspectRatio")

    val rotation = fragmentCameraBinding.viewFinder.display.rotation

    // CameraProvider
    val cameraProvider = cameraProvider
            ?: throw IllegalStateException("Camera initialization failed.")

    // CameraSelector
    val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

    // Preview
    preview = Preview.Builder()
            // We request aspect ratio but no resolution
            .setTargetAspectRatio(screenAspectRatio)
            // Set initial target rotation
            .setTargetRotation(rotation)
            .build()

    // ImageCapture
    imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            // We request aspect ratio but no resolution to match preview config, but letting
            // CameraX optimize for whatever specific resolution best fits our use cases
            .setTargetAspectRatio(screenAspectRatio)
            // Set initial target rotation, we will have to call this again if rotation changes
            // during the lifecycle of this use case
            .setTargetRotation(rotation)
            .build()

    // ImageAnalysis
    imageAnalyzer = ImageAnalysis.Builder()
            // We request aspect ratio but no resolution
            .setTargetAspectRatio(screenAspectRatio)
            // Set initial target rotation, we will have to call this again if rotation changes
            // during the lifecycle of this use case
            .setTargetRotation(rotation)
            .build()
            // The analyzer can then be assigned to the instance
            .also 
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer  luma ->
                    // Values returned from our analyzer are passed to the attached listener
                    // We log image analysis results here - you should do something useful
                    // instead!
                    Log.d(TAG, "Average luminosity: $luma")
                )
            

    // Must unbind the use-cases before rebinding them
    cameraProvider.unbindAll()

    try 
        // A variable number of use-cases can be passed here -
        // camera provides access to CameraControl & CameraInfo
        camera = cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageCapture, imageAnalyzer)

        // Attach the viewfinder's surface provider to preview use case
        preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
        observeCameraState(camera?.cameraInfo!!)
     catch (exc: Exception) 
        Log.e(TAG, "Use case binding failed"

以上是关于APP使用相机CameraX的主要内容,如果未能解决你的问题,请参考以下文章

Android进阶宝典 -- CameraX与Camera2的使用比对

Android进阶宝典 -- CameraX与Camera2的使用比对

音视频开发:为什么推荐使用Jetpack CameraX?

Android开发笔记(一百八十一)使用CameraX拍照

Android开发笔记(一百八十一)使用CameraX拍照

Android开发笔记(一百八十一)使用CameraX拍照