Skip to content

Commit

Permalink
fix: Use CamcorderProfile to find recommended video bit-rate
Browse files Browse the repository at this point in the history
  • Loading branch information
mrousavy committed Oct 30, 2024
1 parent 3be6492 commit f8517f0
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.mrousavy.camera.core.extensions.*
import com.mrousavy.camera.core.types.CameraDeviceFormat
import com.mrousavy.camera.core.types.Torch
import com.mrousavy.camera.core.types.VideoStabilizationMode
import com.mrousavy.camera.core.utils.CamcorderProfileUtils
import kotlin.math.roundToInt

private fun assertFormatRequirement(
Expand All @@ -44,7 +45,8 @@ private fun assertFormatRequirement(
@SuppressLint("RestrictedApi")
@Suppress("LiftReturnOrAssignment")
internal fun CameraSession.configureOutputs(configuration: CameraConfiguration) {
Log.i(CameraSession.TAG, "Creating new Outputs for Camera #${configuration.cameraId}...")
val cameraId = configuration.cameraId!!
Log.i(CameraSession.TAG, "Creating new Outputs for Camera #$cameraId...")
val fpsRange = configuration.targetFpsRange
val format = configuration.format

Expand Down Expand Up @@ -120,18 +122,24 @@ internal fun CameraSession.configureOutputs(configuration: CameraConfiguration)
// We are currently not recording, so we can re-create a recorder instance if needed.
Log.i(CameraSession.TAG, "Creating new Recorder...")
Recorder.Builder().also { recorder ->
configuration.format?.let { format ->
format?.let { format ->
recorder.setQualitySelector(format.videoQualitySelector)
}
videoConfig.config.bitRateOverride?.let { bitRateOverride ->
val bps = bitRateOverride * 1_000_000
recorder.setTargetVideoEncodingBitRate(bps.toInt())
}
videoConfig.config.bitRateMultiplier?.let { bitRateMultiplier ->
val currentBitRate = recorder.build().targetVideoEncodingBitRate
val targetBitRate = currentBitRate.toDouble() * bitRateMultiplier
val bps = targetBitRate * 1_000_000
recorder.setTargetVideoEncodingBitRate(bps.toInt())
if (format == null) {
// We need to get the videoSize to estimate the bitRate modifier
throw PropRequiresFormatToBeNonNullError("videoBitRate")
}
val recommendedBitRate = CamcorderProfileUtils.getRecommendedBitRate(cameraId, format.videoSize)
if (recommendedBitRate != null) {
val targetBitRate = recommendedBitRate.toDouble() * bitRateMultiplier
val bps = targetBitRate * 1_000_000
recorder.setTargetVideoEncodingBitRate(bps.toInt())
}
}
}.build()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.mrousavy.camera.core.utils

import android.annotation.SuppressLint
import android.media.CamcorderProfile
import android.os.Build
import android.util.Log
import android.util.Size
import kotlin.math.abs

class CamcorderProfileUtils {
companion object {
private const val TAG = "CamcorderProfileUtils"

private fun getResolutionForCamcorderProfileQuality(camcorderProfile: Int): Int =
when (camcorderProfile) {
CamcorderProfile.QUALITY_QCIF -> 176 * 144
Expand All @@ -29,6 +33,7 @@ class CamcorderProfileUtils {
val targetResolution = resolution.width * resolution.height
val cameraIdInt = cameraId.toIntOrNull()

@SuppressLint("InlinedApi")
var profiles = (CamcorderProfile.QUALITY_QCIF..CamcorderProfile.QUALITY_8KUHD).filter { profile ->
if (cameraIdInt != null) {
return@filter CamcorderProfile.hasProfile(cameraIdInt, profile)
Expand Down Expand Up @@ -70,6 +75,7 @@ class CamcorderProfileUtils {
return null
} catch (e: Throwable) {
// some Samsung phones just crash when trying to get the CamcorderProfile. Only god knows why.
Log.e(TAG, "Failed to get maximum video size for Camera ID $cameraId! ${e.message}", e)
return null
}
}
Expand All @@ -94,6 +100,32 @@ class CamcorderProfileUtils {
return null
} catch (e: Throwable) {
// some Samsung phones just crash when trying to get the CamcorderProfile. Only god knows why.
Log.e(TAG, "Failed to get maximum FPS for Camera ID $cameraId! ${e.message}", e)
return null
}
}

fun getRecommendedBitRate(cameraId: String, videoSize: Size): Int? {
try {
val quality = findClosestCamcorderProfileQuality(cameraId, videoSize, true)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val profiles = CamcorderProfile.getAll(cameraId, quality)
if (profiles != null) {
return profiles.videoProfiles.maxOf { profile -> profile.bitrate }
}
}

val cameraIdInt = cameraId.toIntOrNull()
if (cameraIdInt != null) {
val profile = CamcorderProfile.get(cameraIdInt, quality)
return profile.videoBitRate
}

return null
} catch (e: Throwable) {
// some Samsung phones just crash when trying to get the CamcorderProfile. Only god knows why.
Log.e(TAG, "Failed to get recommended video bit-rate for Camera ID $cameraId! ${e.message}", e)
return null
}
}
Expand Down

0 comments on commit f8517f0

Please sign in to comment.