Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way of starting and stopping camera using CameraX?

I've started exploring CameraX library along with sample app and I've noticed some inconsistency in managing lifecycle.

In this thread I will talk only about preview use case as it is mostly related to lifecycle.

In the sample app, in CameraFragment, use cases are bound to CameraX in onViewCreated and unbound in onDestroyView. First question is do we have to unbind use cases if we're passing LifecycleOwner to bind method? Can we just bind them in onCreate and leave lifecycle management to CameraX?

I've also tried to follow getting started tutorial, where SurfaceTexture of the TextureView is just replaced. In the sample app, TextureView is first removed from parent, then added and then SurfaceTexture is replaced. Do we have to do that? What is the reason?

Another thing is, in the sample app, use cases are bound from view.post { } method. I've encountered many issues with this approach, because after fragment is put on the backstack, replaced with another fragment and than than recreated, CameraX logged many messages:

E/CamX: [ERROR][STATS_AEC] aec_led_calibration.cpp:560: aec_led_cal_apply_calibration Invalid pointer 0x7921174000 0x0
E/CamX: [ERROR][STATS_AEC] aec_set.cpp:1346: aec_set_fps_range Aec_Error invalid input 414  E/CamX: [ERROR][STATS_AEC] camxcaecstatsprocessor.cpp:1671 SetAlgoBayerHistValue() Unsupported bayer hist channel! 
E/CamX: [ERROR][STATS  ] camxcaecstatsprocessor.cpp:3194 ProcessRequestFastAE() [FastAE] Failed to apply gain to the stats! E/CamX: [ERROR][STATS_AEC] aec_process.cpp:1229: aec_process_stats_parsing aec is null or invalid 
E/CamX: [ERROR][STATS_AEC] aec_process.cpp:7983: aec_process_preview_and_video Error: invalid stats

Is it ok to just set OnPreviewOutputUpdateListener instead of binding all use cases?

Edit

To show the exact problem, I've created simple project Camera Playground.

Here is CameraFragment with whole logic.

class CameraFragment : Fragment() {

    private val preview by lazy {
        val configuration = PreviewConfig.Builder().build()
        Preview(configuration)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        CameraX.bindToLifecycle(this, preview)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_camera, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        button_gallery.setOnClickListener {
            requireActivity().supportFragmentManager
                .beginTransaction()
                .replace(R.id.container, GalleryFragment())
                .addToBackStack("GalleryFragment")
                .commit()
        }

        preview.setOnPreviewOutputUpdateListener { texture_view.surfaceTexture = it.surfaceTexture }
    }
}

Now after clicking gallery button, CameraFragment is replaced with GalleryFragment. After pressing back button and returning to CameraFragment, CameraX logs such messages:

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][STATS  ] gcamfastaeutil.cpp:1170 SetTuningData() [FastAE] ERROR! Failed to get the tuning data

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][HAL    ] camxmetadatapool.cpp:1447 GetMetadataByTag() Invalid Slot to get a metadata from

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][HAL    ] camxmetadatapool.cpp:1447 GetMetadataByTag() Invalid Slot to get a metadata from

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][STATS_AEC] aec_led_calibration.cpp:560: aec_led_cal_apply_calibration Invalid pointer 0x7920f1d000 0x0

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][STATS_AEC] aec_set.cpp:1346: aec_set_fps_range Aec_Error invalid input 0 

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][STATS  ] camxae.cpp:2203 AECSetSensorInfo() Wrong initial sequence from HAL!

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][STATS_AEC] aec_get.cpp:777: aec_get_param GET_EXP_PARAMS ERROR, Uninitialized exposure settings requested

2019-05-09 14:12:20.969 778-1363/? E/CamX: [ERROR][HAL    ] camxmetadatapool.cpp:1447 GetMetadataByTag() Invalid Slot to get a metadata from
like image 229
Nominalista Avatar asked May 09 '19 11:05

Nominalista


2 Answers

CameraX Library, Camera Start & Stop with JAVA

To open camera, we do not have specific code. Only you can use bindToLifecycle(). Also, do NOT forget, implements LifecycleObserver. This is shot code example to start camera in CameraX.

private void mStartCamera(){
    Camera camera = mCameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
}

To close camera, you should work on cameraProvider. You do NOT need to work with camera object. Short code example.

private void mStopCamera(){
    mCameraProvider.unbindAll();
}
  • BONUS

If you want to ask "How can I define cameraProvider?" You need to add listener on cameraProviderFuture. Shor code example.

private void mDefineCameraProvider() {
    final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

    cameraProviderFuture.addListener(() -> {
        try {
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
            mCameraProvider = cameraProvider;
        } 
        catch (ExecutionException | InterruptedException e) {
            // No errors need to be handled for this Future.
            // This should never be reached.
            Log.e("CANER", "Camera provider error: " +e);
        }
    }, ContextCompat.getMainExecutor(this));
}
like image 195
DevPolarBear Avatar answered Oct 13 '22 12:10

DevPolarBear


First question is do we have to unbind use cases if we're passing LifecycleOwner to bind method? Can we just bind them in onCreate and leave lifecycle management to CameraX?

You are correct. I think that the sample app can safely remove the CameraX.unbindAll() call.

TextureView is first removed from parent, then added and then SurfaceTexture is replaced. Do we have to do that? What is the reason?

The TextureView needs to be removed and re-added from the parent view for the SurfaceTexture to be attached. This is because TextureView internally creates its own SurfaceTexture once it is attached to the view hierarchy, and that internal SurfaceTexture only gets correctly detached once the parent TextureView is removed from the view hierarchy. The getting started codelab has been updated to include the re-attachment as well.

Another thing is, in the sample app, use cases are bound from view.post { } method. I've encountered many issues with this approach, because after fragment is put on the backstack, replaced with another fragment and than than recreated, CameraX logged many messages

Binding use cases inside of viewFinder.post { ... } is there to ensure that use cases are bound after the TextureView has been laid out properly. The errors that you are seeing starting with E/CamX are actually unrelated to the CameraX library and appear to be coming from your device's native camera stack (i.e. the camera drivers). If you are not seeing any issues in the app itself, the error messages can probably be ignored.

Is it ok to just set OnPreviewOutputUpdateListener instead of binding all use cases?

I'm not sure I understand this. You have to bind all use cases for CameraX to actually start them as part of the camera session. Take a look at the documentation for more details.

like image 24
Oscar Wahltinez Avatar answered Oct 13 '22 13:10

Oscar Wahltinez