From 1ac1b045fec24d50eb44f01c2e576d554c712254 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 15 May 2024 15:50:01 -0600 Subject: [PATCH] Tweak things to make segments a bit more regular --- .../mrousavy/camera/core/ChunkedRecorder.kt | 37 +++++++++++-------- .../mrousavy/camera/core/RecordingSession.kt | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/package/android/src/main/java/com/mrousavy/camera/core/ChunkedRecorder.kt b/package/android/src/main/java/com/mrousavy/camera/core/ChunkedRecorder.kt index ea1439b..1ec8611 100644 --- a/package/android/src/main/java/com/mrousavy/camera/core/ChunkedRecorder.kt +++ b/package/android/src/main/java/com/mrousavy/camera/core/ChunkedRecorder.kt @@ -12,26 +12,31 @@ import com.mrousavy.camera.types.Orientation import com.mrousavy.camera.types.RecordVideoOptions import java.io.File import java.nio.ByteBuffer +import kotlin.math.ceil -class ChunkedRecordingManager(private val encoder: MediaCodec, private val outputDirectory: File, private val orientationHint: Int, private val iFrameInterval: Int, private val callbacks: CameraSession.Callback) : +class ChunkedRecordingManager(private val encoder: MediaCodec, private val outputDirectory: File, private val orientationHint: Int, private val iFrameInterval: Float, private val callbacks: CameraSession.Callback) : MediaCodec.Callback() { companion object { private const val TAG = "ChunkedRecorder" + private fun roundIntervalLengthUp(fps: Float, interval: Float): Float { + return ceil(fps * interval) / fps + } + fun fromParams( callbacks: CameraSession.Callback, size: Size, enableAudio: Boolean, - fps: Int? = null, + fps: Float = 30.0f, cameraOrientation: Orientation, bitRate: Int, options: RecordVideoOptions, outputDirectory: File, - iFrameInterval: Int = 5 + iFrameInterval: Float = 5.0f ): ChunkedRecordingManager { val mimeType = options.videoCodec.toMimeType() val cameraOrientationDegrees = cameraOrientation.toDegrees() - val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).toDegrees(); + val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).toDegrees() val (width, height) = if (cameraOrientation.isLandscape()) { size.height to size.width } else { @@ -42,25 +47,25 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu val codec = MediaCodec.createEncoderByType(mimeType) + val roundedInterval = roundIntervalLengthUp(fps, iFrameInterval) + // Set some properties. Failing to specify some of these can cause the MediaCodec // configure() call to throw an unhelpful exception. format.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface ) - fps?.apply { - format.setInteger(MediaFormat.KEY_FRAME_RATE, this) - } + format.setFloat(MediaFormat.KEY_FRAME_RATE, fps) // TODO: Pull this out into configuration - format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval) + format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, roundedInterval) format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate) - Log.d(TAG, "Video Format: $format, camera orientation $cameraOrientationDegrees, recordingOrientation: $recordingOrientationDegrees") + Log.d(TAG, "Video Format: $format, camera orientation $cameraOrientationDegrees, recordingOrientation: $recordingOrientationDegrees, Set fps: $fps") // Create a MediaCodec encoder, and configure it with our format. Get a Surface // we can use for input and wrap it with a class that handles the EGL work. codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) return ChunkedRecordingManager( - codec, outputDirectory, recordingOrientationDegrees, iFrameInterval, callbacks + codec, outputDirectory, recordingOrientationDegrees, roundedInterval, callbacks ) } } @@ -69,7 +74,7 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu private var currentFrameNumber: Int = 0 private var chunkIndex = -1 private var encodedFormat: MediaFormat? = null - private var recording = false; + private var recording = false private val targetDurationUs = iFrameInterval * 1000000 @@ -83,15 +88,15 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu } // Muxer specific - private class MuxerContext(val muxer: MediaMuxer, val filepath: File, val chunkIndex: Int, startTimeUs: Long, encodedFormat: MediaFormat) { + private class MuxerContext( + val muxer: MediaMuxer, val filepath: File, val chunkIndex: Int, + val startTimeUs: Long, encodedFormat: MediaFormat + ) { val videoTrack: Int = muxer.addTrack(encodedFormat) - val startTimeUs: Long = startTimeUs init { muxer.start() } - - fun finish() { muxer.stop() muxer.release() @@ -146,7 +151,7 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu override fun onInputBufferAvailable(codec: MediaCodec, index: Int) { } - override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, bufferInfo: MediaCodec.BufferInfo) { + override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, bufferInfo: BufferInfo) { synchronized(this) { if (!recording) { return diff --git a/package/android/src/main/java/com/mrousavy/camera/core/RecordingSession.kt b/package/android/src/main/java/com/mrousavy/camera/core/RecordingSession.kt index 1d61188..b56ab9d 100644 --- a/package/android/src/main/java/com/mrousavy/camera/core/RecordingSession.kt +++ b/package/android/src/main/java/com/mrousavy/camera/core/RecordingSession.kt @@ -45,7 +45,7 @@ class RecordingSession( allCallbacks, size, enableAudio, - fps, + (fps ?: 30).toFloat(), cameraOrientation, bitRate, options,