'CameraX Video capture not recording on physical device

Helllo. I have a problem regarding CameraX VideoCapture use case. I'm using the following version of the library:

implementation "androidx.camera:camera-video:1.1.0-alpha11"

And I'm using the use case as follow:

  • MediaModule.kt (Hilt)

      @Module
      @InstallIn(SingletonComponent::class)
      class VideoCaptureModule {
      @Provides
      fun provideMediaExecutor(): ExecutorService = Executors.newSingleThreadExecutor()
    
      @Provides
      fun provideCameraProviderFuture(
          @ApplicationContext ctx: Context
      ): ListenableFuture<ProcessCameraProvider> = ProcessCameraProvider.getInstance(ctx)
    
      @Provides
      fun provideCameraPreview(): Preview = Preview.Builder().build()
    
      @Provides
      fun provideCameraQualitySelector(): QualitySelector = QualitySelector
          .firstTry(QualitySelector.QUALITY_UHD)
          .thenTry(QualitySelector.QUALITY_FHD)
          .thenTry(QualitySelector.QUALITY_HD)
          .finallyTry(QualitySelector.QUALITY_SD, QualitySelector.FALLBACK_STRATEGY_LOWER)
    
      @Provides
      fun provideCameraRecorder(
          cameraExecutor: ExecutorService,
          qualitySelector: QualitySelector
      ): Recorder = Recorder.Builder()
          .setExecutor(cameraExecutor)
          .setQualitySelector(qualitySelector)
          .build()
    
      @Provides
      fun provideVideoCapture(recorder: Recorder): VideoCapture<Recorder> =
          VideoCapture.withOutput(recorder)
      }
    
  • Record ViewModel (AndroidViewModel)

      @HiltViewModel
      class RecordVideoViewModel @Inject constructor(
          private val preview: Preview,
          private val app: Application,
          private val cameraExecutor: ExecutorService,
          private val videoCapture: VideoCapture<Recorder>,
          private val cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
      ) : AndroidViewModel(app) {
    
          private var camera: Camera? = null
          private var activeRecording: ActiveRecording? = null
          private var cameraProvider: ProcessCameraProvider? = null
    
          private val _videoRecordingStatus = MutableLiveData<VideoRecordingStatus>()
          val videoRecordingStatus = _videoRecordingStatus as LiveData<VideoRecordingStatus>
    
          fun startCamera(
              waitForReload: Boolean,
              lifecycleOwner: LifecycleOwner,
              cameraSelector: CameraSelector,
              surfaceProvider: Preview.SurfaceProvider
          ) {
              cameraProviderFuture.addListener({
                  cameraProvider = cameraProviderFuture.get()
                  preview.setSurfaceProvider(surfaceProvider)
    
                  try {
                      viewModelScope.launch {
                          cameraProvider?.unbindAll()
                          if (waitForReload) delay(CAMERA_WAITING_TIME)
                          camera = cameraProvider
                              ?.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)
                          _torchAvailable.value = camera?.cameraInfo?.hasFlashUnit() == true
                      }
                  } catch (t: Throwable) {
                      FirebaseCrashlytics.getInstance().log(t.localizedMessage.orEmpty())
                  }
              }, ContextCompat.getMainExecutor(app))
          }
    
          fun stopCamera() {
              cameraProvider?.unbindAll()
              cameraProvider = null
              camera = null
              torchEnabled = false
    
              try {
                  activeRecording?.stop()
              } catch (t: IllegalStateException) {
                  // Ignore, active recording already stopped.
              }
              activeRecording = null
          }    
    
          fun startRecording() {
              val name = "${app.getString(R.string.app_name)}-${Date().time}"
              val contentValues = ContentValues().apply {
                  put(MediaStore.Video.Media.DISPLAY_NAME, name)
              }
              val mediaStoreOutput = MediaStoreOutputOptions.Builder(
                  app.contentResolver,
                  MediaStore.Video.Media.EXTERNAL_CONTENT_URI
              ).setContentValues(contentValues).build()
    
              activeRecording = videoCapture.output.prepareRecording(app, mediaStoreOutput)
                  .withAudioEnabled()
                  .withEventListener(cameraExecutor, { event ->
                      when (event) {
                          is VideoRecordEvent.Start ->
                              _videoRecordingStatus.postValue(VideoRecordingStatus.Recording)
                          is VideoRecordEvent.Finalize -> _videoRecordingStatus.postValue(
                              event.cause?.run(VideoRecordingStatus::Failed)
                                  ?: VideoRecordingStatus.Succeeded(event.outputResults.outputUri.toString())
                          )
                      }
                  }).start()
          }
    
          fun stopRecording() {
              activeRecording?.stop()
          }
    
          override fun onCleared() {
              super.onCleared()
              stopCamera()
              cameraExecutor.shutdown()
          }
    
          sealed class VideoRecordingStatus {
              object Recording : VideoRecordingStatus()
              data class Failed(val t: Throwable?) : VideoRecordingStatus()
              data class Succeeded(val uri: String) : VideoRecordingStatus()
          }
      }
    

Everything works fine in an emulator. I can record and save videos no problem, but, when I run the app in a real device (Samsung Galaxy S20 Ultra 5G) running Android 11, I get the following crash:

2021-12-15 11:56:00.271 3758-3789/? E/DatabaseUtils: Writing exception to parcel
    java.lang.IllegalArgumentException: MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*
        at com.android.providers.media.MediaProvider.ensureFileColumns(MediaProvider.java:3301)
        at com.android.providers.media.MediaProvider.ensureUniqueFileColumns(MediaProvider.java:3072)
        at com.android.providers.media.MediaProvider.insertFile(MediaProvider.java:3826)
        at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:4386)
        at com.android.providers.media.MediaProvider.insert(MediaProvider.java:4110)
        at android.content.ContentProvider$Transport.insert(ContentProvider.java:336)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:167)
        at android.os.Binder.execTransactInternal(Binder.java:1190)
        at android.os.Binder.execTransact(Binder.java:1159)
2021-12-15 11:56:00.275 19974-4965/? E/SequentialExecutor: Exception while executing runnable androidx.camera.core.impl.utils.executor.SequentialExecutor$1@9d3f8d3
    java.lang.IllegalArgumentException: MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
        at android.content.ContentProviderProxy.insert(ContentProviderNative.java:549)
        at android.content.ContentResolver.insert(ContentResolver.java:2159)
        at android.content.ContentResolver.insert(ContentResolver.java:2121)
        at androidx.camera.video.Recorder$RecordingRecord.lambda$initializeRecording$1(Recorder.java:2247)
        at androidx.camera.video.Recorder$RecordingRecord$$ExternalSyntheticLambda1.get(Unknown Source:4)
        at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2394)
        at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
        at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.328 19974-4965/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-1
    Process: com.pt.wshhp, PID: 19974
    java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
        at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
        at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
        at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.395 19974-5309/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-2
    Process: com.pt.wshhp, PID: 19974
    java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
        at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
        at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
        at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
2021-12-15 11:56:00.465 19974-5313/? E/AndroidRuntime: FATAL EXCEPTION: pool-40-thread-3
    Process: com.pt.wshhp, PID: 19974
    java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=Worldstar-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.pt.wshhp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}
        at androidx.camera.video.Recorder$RecordingRecord.performOneTimeMediaMuxerCreation(Recorder.java:2391)
        at androidx.camera.video.Recorder.setupAndStartMediaMuxer(Recorder.java:1298)
        at androidx.camera.video.Recorder$2.onEncodedData(Recorder.java:1436)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback.lambda$sendEncodedData$3(EncoderImpl.java:937)
        at androidx.camera.video.internal.encoder.EncoderImpl$MediaCodecCallback$$ExternalSyntheticLambda6.run(Unknown Source:4)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$1.run(SequentialExecutor.java:111)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:231)
        at androidx.camera.core.impl.utils.executor.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:173)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

These are the crash highlights:

  • MIME type application/octet-stream cannot be inserted into content://media/external/video/media; expected MIME type under video/*

  • java.lang.AssertionError: One-time media muxer creation has already occurred for recording RecordingRecord{getOutputOptions=MediaStoreOutputOptions{contentResolver=android.app.ContextImpl$ApplicationContentResolver@e6e1f6b, collectionUri=content://media/external/video/media, contentValues=_display_name=VideoRecording-1639590959896, fileSizeLimit=0}, getCallbackExecutor=java.util.concurrent.Executors$FinalizableDelegatedExecutorService@850110f, getEventListener=com.videoapp.viewmodels.RecordVideoViewModel$$ExternalSyntheticLambda0@fe4059c, hasAudioEnabled=true, getRecordingId=1}

This is what I tried so far (without success):

  • Create the objects from MediaModule.kt within the ViewModel instead of injecting them
  • Use an Activity Context instead of the Application object
  • Stop the camera and re-starting it again when this crash occurs (using try-catch)

Seems like is trying to record twice to the video file source. Has anyone faced this before? In the emulator is recording just fine but in the real device is crashing.



Solution 1:[1]

Hey I don't figure out really ur problem But I will send you My CameraX works so great in My Samsung A10s . I'm using fragment not activity but it's the same u only change context.

import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.*
import androidx.camera.video.VideoCapture
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.fragment.app.Fragment
import com.example.splashscreenkotlin.R
import com.example.splashscreenkotlin.databinding.FragmentCameraBinding
import java.lang.Exception
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

typealias LumaListener = (luma: Double) -> Unit

class CameraFragment : Fragment() {

    private var binding: FragmentCameraBinding? = null
    private val _binding get() = binding!!

    private var imageCapture: ImageCapture? = null
    private var videoCapture: VideoCapture<Recorder>? = null
    private var recording: Recording? = null

    private lateinit var cameraExecutorService: ExecutorService

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentCameraBinding.inflate(layoutInflater,container,false)
        return _binding.root
    }

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

        // Request camera permissions
        if (allPermissionGranted()){
            startCamera()
        }else {
            ActivityCompat.requestPermissions(requireActivity(), REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }

        // Set up the listeners for take photo and video capture buttons
        _binding.imageCaptureButton.setOnClickListener { takePhoto() }
        _binding.videoCaptureButton.setOnClickListener { captureVideo() }

        cameraExecutorService = Executors.newSingleThreadExecutor()
    }


    private fun takePhoto(){
        //First, get a reference to the ImageCapture use case.
    // If the use case is null, exit out of the function.
    // This will be null If we tap the photo button before image capture is set up.
    // Without the return statement, the app would crash if it was null.
        val imageCapture = imageCapture ?: return

        //create a MediaStore content value to hold the image.
    // Use a timestamp so the display name in MediaStore will be unique.
        val name = SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(System.currentTimeMillis())
        //This class is used to store a set of values that the ContentResolver can process.
        //ContentResolver class provides applications access to the content model.
        val contentValues = ContentValues().apply {
            //MediaStore is contract between the media provider and applications.
            // Contains definitions for the supported URIs and columns.
            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
                put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/SplashApp-Images")
            }
        }

        //Create an OutputFileOptions object.
    // This object is where we can specify things about how we want our output to be.
    // We want the output saved in the MediaStore so other apps could display it,
    // so add our MediaStore entry.
        val outputOptions = ImageCapture.OutputFileOptions
            .Builder(requireActivity().contentResolver,MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues)
            .build()

        //Call takePicture() on the imageCapture object.
    // Pass in outputOptions, the executor, and a callback for when the image is saved
        imageCapture.takePicture(outputOptions,ContextCompat.getMainExecutor(requireContext()),
        object : ImageCapture.OnImageSavedCallback{

            override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                val msg = "Photo capture succeeded: ${outputFileResults.savedUri}"
                Toast.makeText(requireContext(),msg,Toast.LENGTH_SHORT).show()
                Log.d(TAG,msg)
            }

            //In the case that the image capture fails or saving the image capture fails,
            // add in an error case to log that it failed.
            override fun onError(exception: ImageCaptureException) {
                Log.e(TAG,"Photo Captured failed: ${exception.message}",exception)
            }
        })
    }

    private fun captureVideo(){
        //Check if the VideoCapture use case has been created: if not, do nothing.7
        val videoCapture = videoCapture ?: return

        //Disable the UI until the request action is completed by CameraX
        _binding.videoCaptureButton.isEnabled = false

        //If there is an active recording in progress, stop it and release the current recording.
    // We will be notified when the captured video file is ready to be used by our application.
        val currentRecording = recording
        if (currentRecording != null){
            currentRecording.stop()
            recording = null
            return
        }

        // we create our intended MediaStore video content object,
    // with system timestamp as the display name(so we could capture multiple videos).
        val name = SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(System.currentTimeMillis())
        //ContentValue class is used to store a set of values that the ContentResolver can process.
        // ContentResolver class provides applications access to the content model.
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME,name)
            put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P ) {
                put(MediaStore.Video.Media.RELATIVE_PATH,"Movies/SplashApp-Videos")
            }
        }

        //Create a MediaStoreOutputOptions.Builder with the external content option.
        //MediaStoreOutputOptions class providing options for storing output to MediaStore.
        val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(
            requireActivity().contentResolver,MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        )//Set the created video contentValues to the MediaStoreOutputOptions.Builder, and build our MediaStoreOutputOptions instance.
            .setContentValues(contentValues).build()

        //Configure the output option to the Recorder of VideoCapture<Recorder> and enable audio recording
        recording = videoCapture.output
            .prepareRecording(requireContext(),mediaStoreOutputOptions).apply {
                if (PermissionChecker.checkSelfPermission(requireContext(),Manifest.permission.RECORD_AUDIO)
                    == PermissionChecker.PERMISSION_GRANTED) {
                    withAudioEnabled()
                }
            }//Start this new recording, and register a lambda VideoRecordEvent listener.
            .start(ContextCompat.getMainExecutor(requireContext())){ recordEvent ->

                when(recordEvent) {
                    //When the request recording is started by the camera device,
                        // toggle the "Start Capture" button text to say "Stop Capture".
                    is VideoRecordEvent.Start -> {
                        _binding.videoCaptureButton.apply {
                            text = getString(R.string.stop_capture)
                            isEnabled = true
                        }
                    }
                    //When the active recording is complete, notify the user with a toast,
                // and toggle the "Stop Capture" button back to "Start Capture", and re-enable it
                    is VideoRecordEvent.Finalize -> {
                        _binding.videoCaptureButton.apply {
                            text = getString(R.string.start_capture)
                            isEnabled = true
                        }
                        if(!recordEvent.hasError()){
                            val msg = "Video Capture Succeed: " + "${recordEvent.outputResults.outputUri}"
                            Toast.makeText(requireContext(),msg,Toast.LENGTH_LONG).show()
                            Log.d(TAG,msg)
                        }else {
                            recording?.close()
                            recording = null
                            Log.e(TAG,"Video Capture Ends with an Error: " + "${recordEvent.error}")
                        }
                    }
                }
            }
    }

    private fun startCamera(){
        // User ProcessCameraProvider Bcs A singleton which can be used to bind the lifecycle of cameras
        // to any LifecycleOwner within an application's process.
        // Retrieve it with getInstance
        // This eliminates the task of opening and closing the camera since CameraX is lifecycle-aware.
        val cameraFutureProvider = ProcessCameraProvider.getInstance(requireContext())
        //Registers a listener to be run  on the given executor.
        // The listener will run when the Future's computation is complete  or,
        // if the computation is already complete, immediately.
        cameraFutureProvider.addListener({
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraFutureProvider.get()

            // Preview use case that provides a camera preview stream for displaying on-screen.
            val preview = Preview.Builder().build().also {
                // Decide how the surface we using
                // get a surface provider from viewfinder, and then set it on the preview.
                it.setSurfaceProvider(_binding.viewFinder.surfaceProvider)
            }

            val recorder = Recorder.Builder()
                .setQualitySelector(QualitySelector.from(Quality.HIGHEST,
                    //Add a FallbackStrategy to the existing QualitySelector creation.
                    // This allows CameraX to pick up a supported resolution
                    // if the required Quality.HIGHEST is not supportable with the imageCapture use case.
                FallbackStrategy.higherQualityOrLowerThan(Quality.SD)))
                .build()
            videoCapture = VideoCapture.withOutput(recorder)

            imageCapture = ImageCapture.Builder().build()

            /*
            //ImageAnalysis use case providing CPU accessible images for an app to perform image analysis on.
            //ImageAnalysis acquires images from the camera via an ImageReader.
            // Each image is provided to an ImageAnalysis.Analyzer function which can be implemented by application code
            //where it can access image data for application analysis via an ImageProxy.
            val imageAnalyzer = ImageAnalysis.Builder().build().also {
                it.setAnalyzer(cameraExecutorService,LuminosityAnalyzer { luma ->  
                    Log.d(TAG,"The Average luminosity: $luma")
                })
            }
            */


            // Select back camera as a default
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
            try {
                // Unbind use case before rebinding
                    // This will initiate a close of every currently open camera.
                cameraProvider.unbindAll()

                // Bind use cases to camera
                cameraProvider.bindToLifecycle(
                    requireActivity(),cameraSelector,preview,imageCapture,videoCapture
                )
            }catch (e:Exception){
                Log.e(TAG, "Use case binding failed",e)
            }
        }// This returns an Executor that runs on the main thread.
            ,ContextCompat.getMainExecutor(requireContext()))
    }

    //The analyzer logs the average luminosity of the image.
    // To create an analyzer, we override the analyze function in a class that implements the ImageAnalysis.Analyzer interface.
    private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer{

        private fun ByteBuffer.toByteArray(): ByteArray{
            rewind() //Rewind the Buffer to zero
            val data = ByteArray(remaining())
            get(data) //Copy the buffer into a byte array
            return data //Return byte array
        }

        override fun analyze(image: ImageProxy) {
            val buffer = image.planes[0].buffer
            val data = buffer.toByteArray()
            val pixels = data.map { it.toInt() and 0xFF }
            val luma = pixels.average()

            listener(luma)
            //The application is responsible for calling ImageProxy.close() to close the image.
            // Failing to close the image will cause future images to be stalled or dropped depending on the backpressure strategy.
            image.close()
        }
    }

    // Send Permission To access to camera
    private fun allPermissionGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(requireContext(),it) ==
                PackageManager.PERMISSION_GRANTED
    }

    //Callback for the result from requesting permissions.
    // This method is invoked for every call on requestPermissions(String[], int).
    @Deprecated("Deprecated in Java")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_CODE_PERMISSIONS){
            if (allPermissionGranted()){
                startCamera()
            }else {
                Toast.makeText(requireContext(),"Permission not granted by the user.",Toast.LENGTH_SHORT).show()
                activity?.finish()
            }
        }
    }


    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
        cameraExecutorService.shutdown()
    }



    companion object {
        private const val TAG = "CAMERAX"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS =
            mutableListOf(
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO
            ).apply {
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P){
                    add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                }
            }.toTypedArray()
    }
}

For My FragmentLayout :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.CameraFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:id="@+id/camera_container"
        android:background="@android:color/black"
        android:layout_height="match_parent">

        <androidx.camera.view.PreviewView
            android:id="@+id/viewFinder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <Button
            android:id="@+id/image_capture_button"
            android:layout_width="110dp"
            android:layout_height="110dp"
            android:layout_marginBottom="50dp"
            android:layout_marginEnd="50dp"
            android:elevation="2dp"
            android:text="@string/take_photo"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintEnd_toStartOf="@id/vertical_centerline" />

        <Button
            android:id="@+id/video_capture_button"
            android:layout_width="110dp"
            android:layout_height="110dp"
            android:layout_marginBottom="50dp"
            android:layout_marginStart="50dp"
            android:elevation="2dp"
            android:text="@string/start_capture"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/vertical_centerline" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/vertical_centerline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent=".50" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

Work awesome with bottomNavigation by the way :) Even if I change fragment my camera fragment don't crash

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Marouan Akechtah