feat: Add customizable Video Bit Rate (videoBitRate
) (#1882)
* feat: Add `videoBitRate` option to `RecordVideoOptions` * feat: Implement `videoBitRate` on iOS * feat: Implement `videoBitRate` on Android * chore: Format * docs: Separate recording and photo docs * docs: Fix links * docs: Add docs about bitrate and quality * docs: Add blob * fix: Don't use inline style for CI * fix: Correctly log default bitRate * fix: Fix typo * fix: Calculate default bit-rate on Android depending on resolution * Update RecordingSession.kt
This commit is contained in:
@@ -33,6 +33,10 @@ suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Ca
|
||||
if (options.hasKey("fileType")) {
|
||||
fileType = VideoFileType.fromUnionValue(options.getString("fileType"))
|
||||
}
|
||||
var bitRate: Double? = null
|
||||
if (options.hasKey("videoBitRate")) {
|
||||
bitRate = options.getDouble("videoBitRate")
|
||||
}
|
||||
|
||||
val callback = { video: RecordingSession.Video ->
|
||||
val map = Arguments.createMap()
|
||||
@@ -44,7 +48,7 @@ suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Ca
|
||||
val errorMap = makeErrorMap(error.code, error.message)
|
||||
onRecordCallback(null, errorMap)
|
||||
}
|
||||
cameraSession.startRecording(audio == true, codec, fileType, callback, onError)
|
||||
cameraSession.startRecording(audio == true, codec, fileType, bitRate, callback, onError)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
|
@@ -279,6 +279,7 @@ class CameraSession(
|
||||
enableAudio: Boolean,
|
||||
codec: VideoCodec,
|
||||
fileType: VideoFileType,
|
||||
bitRate: Double?,
|
||||
callback: (video: RecordingSession.Video) -> Unit,
|
||||
onError: (error: RecorderError) -> Unit
|
||||
) {
|
||||
@@ -287,7 +288,8 @@ class CameraSession(
|
||||
val outputs = outputs ?: throw CameraNotReadyError()
|
||||
val videoOutput = outputs.videoOutput ?: throw VideoNotEnabledError()
|
||||
|
||||
val recording = RecordingSession(context, videoOutput.size, enableAudio, fps, codec, orientation, fileType, callback, onError)
|
||||
val recording =
|
||||
RecordingSession(context, videoOutput.size, enableAudio, fps, codec, orientation, fileType, bitRate, callback, onError)
|
||||
recording.start()
|
||||
this.recording = recording
|
||||
}
|
||||
|
@@ -22,14 +22,13 @@ class RecordingSession(
|
||||
private val codec: VideoCodec = VideoCodec.H264,
|
||||
private val orientation: Orientation,
|
||||
private val fileType: VideoFileType = VideoFileType.MP4,
|
||||
videoBitRate: Double? = null,
|
||||
private val callback: (video: Video) -> Unit,
|
||||
private val onError: (error: RecorderError) -> Unit
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "RecordingSession"
|
||||
|
||||
// bits per second
|
||||
private const val VIDEO_BIT_RATE = 10_000_000
|
||||
private const val AUDIO_SAMPLING_RATE = 44_100
|
||||
private const val AUDIO_BIT_RATE = 16 * AUDIO_SAMPLING_RATE
|
||||
private const val AUDIO_CHANNELS = 1
|
||||
@@ -37,6 +36,7 @@ class RecordingSession(
|
||||
|
||||
data class Video(val path: String, val durationMs: Long)
|
||||
|
||||
private val bitRate = videoBitRate ?: getDefaultBitRate()
|
||||
private val recorder: MediaRecorder
|
||||
private val outputFile: File
|
||||
private var startTime: Long? = null
|
||||
@@ -44,7 +44,6 @@ class RecordingSession(
|
||||
val surface: Surface = MediaCodec.createPersistentInputSurface()
|
||||
|
||||
init {
|
||||
|
||||
outputFile = File.createTempFile("mrousavy", fileType.toExtension(), context.cacheDir)
|
||||
|
||||
Log.i(TAG, "Creating RecordingSession for ${outputFile.absolutePath}")
|
||||
@@ -56,11 +55,11 @@ class RecordingSession(
|
||||
|
||||
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
||||
recorder.setOutputFile(outputFile.absolutePath)
|
||||
recorder.setVideoEncodingBitRate(VIDEO_BIT_RATE)
|
||||
recorder.setVideoEncodingBitRate((bitRate * 1_000_000).toInt())
|
||||
recorder.setVideoSize(size.height, size.width)
|
||||
if (fps != null) recorder.setVideoFrameRate(fps)
|
||||
|
||||
Log.i(TAG, "Using $codec Video Codec..")
|
||||
Log.i(TAG, "Using $codec Video Codec at $bitRate Mbps..")
|
||||
recorder.setVideoEncoder(codec.toVideoCodec())
|
||||
if (enableAudio) {
|
||||
Log.i(TAG, "Adding Audio Channel..")
|
||||
@@ -131,8 +130,22 @@ class RecordingSession(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultBitRate(): Double {
|
||||
var baseBitRate = when (size.width * size.height) {
|
||||
in 0..640 * 480 -> 2.0
|
||||
in 640 * 480..1280 * 720 -> 5.0
|
||||
in 1280 * 720..1920 * 1080 -> 10.0
|
||||
in 1920 * 1080..3840 * 2160 -> 30.0
|
||||
in 3840 * 2160..7680 * 4320 -> 100.0
|
||||
else -> 100.0
|
||||
}
|
||||
baseBitRate = baseBitRate / 30.0 * (fps ?: 30).toDouble()
|
||||
if (this.codec == VideoCodec.H265) baseBitRate *= 0.8
|
||||
return baseBitRate
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val audio = if (enableAudio) "with audio" else "without audio"
|
||||
return "${size.width} x ${size.height} @ $fps FPS $codec $fileType $orientation RecordingSession ($audio)"
|
||||
return "${size.width} x ${size.height} @ $fps FPS $codec $fileType $orientation $bitRate Mbps RecordingSession ($audio)"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user