32

Google has released the new CameraX library as part of Jetpack. It looks great for taking pictures, but my use case also require making video's. I tried googling for that, but couldn't find anything.

So, is it possible to record videos with the CameraX Jetpack library?

Peter Fortuin
  • 5,041
  • 8
  • 41
  • 69

6 Answers6

26

Yes, we can record video using CameraX. I have tried to implement myself with help of Github demo for CameraX. Please refer below code may it helps you.

Config for Video in CameraX:

val videoCaptureConfig = VideoCaptureConfig.Builder().apply {
    setLensFacing(lensFacing)
    setTargetAspectRatio(screenAspectRatio)
    setTargetRotation(viewFinder.display.rotation)

}.build()

videoCapture = VideoCapture(videoCaptureConfig)

CameraX.bindToLifecycle(this, preview, imageCapture, videoCapture)

To Start video recording:

videoCapture?.startRecording(videoFile, object : VideoCapture.OnVideoSavedListener {
        override fun onVideoSaved(file: File?) {
            Log.i(javaClass.simpleName, "Video File : $file")
        }

        override fun onError(useCaseError: VideoCapture.UseCaseError?, message: String?, cause: Throwable?) {
            Log.i(javaClass.simpleName, "Video Error: $message")
        }

    })

To Stop video recording:

videoCapture?.stopRecording()

Same above I have mentioned in Github issue comment: https://github.com/android/camera/issues/2#issuecomment-490773932

Notes: There may be different in code to implementation of video recording using CameraX. Because this above code was developed by me without any other reference rather than Github Demo.

Please check important comment of Oscar Wahltinez on this answer as of 14 May 2019

Community
  • 1
  • 1
Patel Pinkal
  • 8,984
  • 4
  • 28
  • 50
  • 15
    Please note that video recording use case is currently marked as hidden in the API and is in a very preliminary state and subject to change. Refer to [the javadocs](https://developer.android.com/reference/androidx/camera/core/package-summary.html) for the publicly available classes. – Oscar Wahltinez May 14 '19 at 00:11
  • 5
    Do not forget to add `RECORD_AUDIO` permission – Sergey Stasishin Jun 09 '19 at 17:18
  • Hmmm... it seems you cannot use that from Java, or maybe is the version. VideoCapture class is marked as @RestrictTo(Scope.LIBRARY_GROUP), which prevents its instantiation. – Fran Marzoa Sep 27 '19 at 16:07
  • Please share the XML as well. It's difficult to understand what is the component on which video preview is being shown – rohegde7 Jul 29 '20 at 21:31
  • @rohegde7 You can check the official CameraX sample with entire demo with XML. – Patel Pinkal Jul 30 '20 at 07:06
9

This is my solution

//Versions in Gradle
def camerax_version = "1.0.0-beta06"
def camera_extensions = "1.0.0-alpha13"

private lateinit var videoCapture: VideoCapture
private lateinit var viewFinder: PreviewView
private lateinit var outputDirectory: File
private var lensFacing: Int = CameraSelector.LENS_FACING_FRONT


private val executor = Executors.newSingleThreadExecutor()
private var isRecording = false
private var camera: Camera? = null
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>


//onCreate
viewFinder = preview_video_view
    runWithPermissions(*permissions) {
        startCamera(view.context)
        initClicks()
    }

 @SuppressLint("RestrictedApi", "UnsafeExperimentalUsageError")
private fun startCamera(context: Context) {
    outputDirectory = getOutputDirectory(context)
    cameraProviderFuture = ProcessCameraProvider.getInstance(context)

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

    // Create a configuration object for the video use case
    val videoCaptureConfig = VideoCaptureConfig.Builder().apply {
        setTargetRotation(viewFinder.display.rotation)
        setCameraSelector(cameraSelector)
    }


    //CameraX.initialize(context, this.cameraXConfig) 

    videoCapture = VideoCapture(videoCaptureConfig.useCaseConfig)

    val preview: Preview = Preview.Builder().apply {
        setTargetAspectRatio(AspectRatio.RATIO_16_9)
        setTargetRotation(viewFinder.display.rotation)
    }.build()
    preview.setSurfaceProvider(viewFinder.createSurfaceProvider())

    cameraProviderFuture.addListener(Runnable {
        val cameraProvider = cameraProviderFuture.get()
        camera = cameraProvider.bindToLifecycle(
            viewLifecycleOwner,
            cameraSelector,
            preview,
            videoCapture
        )
        
    }, ContextCompat.getMainExecutor(context))
}

@SuppressLint("RestrictedApi")
private fun startRecording() {
    val file = createFile(
        outputDirectory,
        FILENAME,
        VIDEO_EXTENSION
    )

    videoCapture.startRecording(
        file,
        executor,
        object : VideoCapture.OnVideoSavedCallback {
            override fun onVideoSaved(file: File) {
                Handler(Looper.getMainLooper()).post {
                    showMessage(file.name + " is saved")
                }
            }

            override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
                Handler(Looper.getMainLooper()).post {
                    showMessage(videoCaptureError.toString() + " " + message)
                }
            }
        }
    )
}

@SuppressLint("RestrictedApi")
private fun stopRecording() {
    videoCapture.stopRecording()
}

 override fun getCameraXConfig(): CameraXConfig {
    return Camera2Config.defaultConfig()
}

companion object {
    private const val FILENAME = "yyyy_MM_dd_HH_mm_ss"
    private const val VIDEO_EXTENSION = ".mp4"

    private val permissions = arrayOf(
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA,
        Manifest.permission.RECORD_AUDIO
    )

    fun getOutputDirectory(context: Context): File {
        val appContext = context.applicationContext
        val mediaDir = appContext.externalMediaDirs.firstOrNull()?.let {
            File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() }
        }
        return if (mediaDir != null && mediaDir.exists()) mediaDir else appContext.filesDir
    }

    fun createFile(baseFolder: File, format: String, extension: String) =
        File(baseFolder, SimpleDateFormat(format, Locale.US)
            .format(System.currentTimeMillis()) + extension)
}
Sergei Maleev
  • 307
  • 3
  • 4
9

Updating on Patel Pinkal's answer. Having beta released, we can't use VideoCaptureConfig.Builder() anymore, instead you go with something like this:

 videoCapture = VideoCapture.Builder().apply {
     // init config here
 }.build()
6

As of April 2021

val videoCapture = VideoCapture.Builder().build()
val outputDirectory = getOutputDirectory()


fun getOutputDirectory(): File {
        val mediaDir = externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else filesDir
}


@SuppressLint("RestrictedApi")
    private fun startRecording() {
        val videoFile = File(
            outputDirectory,
            SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US
            ).format(System.currentTimeMillis()) + ".mp4")
        val outputOptions = VideoCapture.OutputFileOptions.Builder(videoFile).build()

        videoCapture?.startRecording(outputOptions, ContextCompat.getMainExecutor(this), object: VideoCapture.OnVideoSavedCallback {
            override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
                Log.e(TAG, "Video capture failed: $message")
            }

            override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
                val savedUri = Uri.fromFile(videoFile)
                val msg = "Video capture succeeded: $savedUri"
                Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                Log.d(TAG, msg)
            }
        })
    }

@SuppressLint("RestrictedApi")
    private fun stopRecording() {
        videoCapture?.stopRecording()
    }

Please note that this API is still restricted and is still subject to change.

elbert rivas
  • 1,464
  • 1
  • 17
  • 15
1

CameraX VideoCapture use case is implemented in camera-video, released since 1.1.0-alpha30. Refer to the following:

for details.

Gerry
  • 1,223
  • 9
  • 17
0

Regarding Sergei's answer, videoCapture.startRecording() recieves VideoCapture.OutputFileOptions instead of file for camerax_version = '1.0.0-rc01', so it should be used as:

videoCapture.startRecording(
        VideoCapture.OutputFileOptions.Builder(file).build(),
        executor,
        object : VideoCapture.OnVideoSavedCallback {
            override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
                TODO("Not yet implemented")
            }

            override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
                TODO("Not yet implemented")
            }
        }
jefimijana
  • 19
  • 3