I have surfed all the internet and didn't find any solution, I am using Camera2 api to record a video from my front camera, I have tested on multiple devices and its working fine, but when I tried on my Samsung Galaxy 3, after I press the record button sometimes the recording work, and sometimes the camera preview freeze, you can find below the code I have implemented
private val previewRequest: CaptureRequest? by lazy {
mCaptureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
addTarget(viewFinder.holder.surface)
}.build()
}
private val recordRequest: CaptureRequest by lazy {
mCaptureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
addTarget(viewFinder.holder.surface)
addTarget(mMediaRecorder.surface)
}.build()
}
when (cameraDirection) { // I am getting this variable to see what camera I should open
CameraDirection.BACK -> { //getCameraPosition gets the cameraId for the given //LENS_FACING direction
mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_BACK)
}
CameraDirection.FRONT -> {
mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_FRONT)
}
else -> {
mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_BACK)
}
}
characteristics = cameraManager.getCameraCharacteristics(mCameraId!!)
// Selects appropriate preview size and configures view finder
mPreviewSize = getPreviewOutputSize(
viewFinder.display, characteristics, SurfaceHolder::class.java
)
// Selects appropriate video size
mVideoSize = getPreviewOutputSize(
viewFinder.display, characteristics, MediaRecorder::class.java
)
viewFinder.setAspectRatio(mPreviewSize.width, mPreviewSize.height)
// To ensure that size is set, initialize camera in the view's thread
viewFinder.post {
initializeCamera()
}
private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {
//viewFinder is the AutoFitSurfaceView
camera = openCamera(cameraManager, mCameraId!!, cameraHandler)
setupMediaRecorder()
val targets = listOf(viewFinder.holder.surface)
camera.createCaptureSession(targets, object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
mCaptureSession = session
session.setRepeatingRequest(previewRequest!!, null, cameraHandler)
}
override fun onConfigureFailed(session: CameraCaptureSession) {
}
}, cameraHandler)
}
private suspend fun openCamera(
manager: CameraManager,
cameraId: String,
handler: Handler? = null
): CameraDevice = suspendCancellableCoroutine { cont ->
manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) = cont.resume(device)
override fun onDisconnected(device: CameraDevice) {
finish()
}
override fun onError(device: CameraDevice, error: Int) {
val msg = when (error) {
ERROR_CAMERA_DEVICE -> "Fatal (device)"
ERROR_CAMERA_DISABLED -> "Device policy"
ERROR_CAMERA_IN_USE -> "Camera in use"
ERROR_CAMERA_SERVICE -> "Fatal (service)"
ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
else -> "Unknown"
}
val exc = RuntimeException("Camera $cameraId error: ($error) $msg")
if (cont.isActive) cont.resumeWithException(exc)
}
}, handler)
}
private fun setupMediaRecorder() {
mMediaRecorder = MediaRecorder()
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mMediaRecorder.setOutputFile(outputFile.absolutePath)
Log.i("CAMERA_INFO", mCameraId!!)
val profile = CamcorderProfile.get(mCameraId!!.toInt(), CamcorderProfile.QUALITY_LOW)
Log.i("CAMERA_INFO", "Frame Rate: " + profile.videoFrameRate)
mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate)
mMediaRecorder.setVideoFrameRate(profile.videoFrameRate)
mMediaRecorder.setVideoSize(mPreviewSize.width, mPreviewSize.height)
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
when (mCameraDirection) {
CameraDirection.BACK -> {
mMediaRecorder.setOrientationHint(90)
}
CameraDirection.FRONT -> {
mMediaRecorder.setOrientationHint(270)
}
else -> {
}
}
mMediaRecorder.prepare()
}
Explanation: first initializeCamera() is called and in this function I am setting the Camera and the previewSession and preparing the MediaRecorder to start recording when the user press the record button
After the user press on record button I am doing the following:
here is the following code:
button_record_video.setOnClickListener {
mCaptureSession.close() //Closing the previewSession
try {
camera.createCaptureSession( //Creating the record session passing the viewFinder surface //and the MediaRecorder session
listOf(
viewFinder.holder.surface,
mMediaRecorder.surface
), object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
mCaptureSession = session
session.setRepeatingRequest(recordRequest, null, cameraHandler)
mMediaRecorder.start()
}
override fun onConfigureFailed(p0: CameraCaptureSession) {
}
}, cameraHandler
)
} catch (e: Exception) {
}
}
PS: This code is working when capturing from the back camera as for the front camera its working on some devices and failing OCCASIONALLY on others (device tested that this code fail OCCASIONALLY Samsung Galaxy S3).
Any more information needed, I can gladly provide Thanks in Advance
Since you are preparing the media recorder right away, you could just have one capture session, shared between preview and recording. Then just add in the recording target Surface to the capture request once you want to start recording.
That avoids the glitch from creating a new capture session, and may be more compatible with the devices you're seeing an issue on. In addition, you might want to look at persistent recording surfaces from MediaCodec, to avoid having to create a new session for the second recording (if that's something you want to support).