Surface Android fMP4 writer failures

This commit is contained in:
2026-05-18 16:59:17 -07:00
parent 8a6537c914
commit a1f23e0183
4 changed files with 38 additions and 9 deletions

View File

@@ -110,6 +110,8 @@ class UnknownCaptureError(wasImageCaptured: Boolean) :
CameraError("capture", "unknown", "An unknown error occurred while trying to capture an Image! Was Image captured: $wasImageCaptured")
class RecorderError(name: String, extra: Int) :
CameraError("capture", "recorder-error", "An error occured while recording a video! $name $extra")
class FragmentedRecorderError(message: String, cause: Throwable? = null) :
CameraError("capture", "fragmented-recorder-error", message, cause)
class NoRecordingInProgressError :
CameraError("capture", "no-recording-in-progress", "There was no active video recording in progress! Did you call stopRecording() twice?")
class InsufficientStorageError : CameraError("capture", "insufficient-storage", "There is not enough storage space available.")

View File

@@ -20,7 +20,8 @@ import java.io.File
*/
class FragmentedRecordingManager(
private val encoder: MediaCodec,
private val muxer: HlsMuxer
private val muxer: HlsMuxer,
private val onError: (CameraError) -> Unit
) : MediaCodec.Callback(), ChunkedRecorderInterface {
companion object {
@@ -36,6 +37,7 @@ class FragmentedRecordingManager(
bitRate: Int,
options: RecordVideoOptions,
outputDirectory: File,
onError: (CameraError) -> Unit,
segmentDurationSeconds: Int = DEFAULT_SEGMENT_DURATION_SECONDS
): FragmentedRecordingManager {
val mimeType = options.videoCodec.toMimeType()
@@ -81,11 +83,12 @@ class FragmentedRecordingManager(
)
muxer.setSegmentDuration(segmentDurationSeconds * 1_000_000L)
return FragmentedRecordingManager(codec, muxer)
return FragmentedRecordingManager(codec, muxer, onError)
}
}
private var recording = false
private var failed = false
private var muxerStarted = false
private var trackIndex = -1
@@ -100,6 +103,17 @@ class FragmentedRecordingManager(
recording = true
}
private fun failRecording(message: String, cause: Throwable) {
if (failed) {
return
}
failed = true
recording = false
Log.e(TAG, message, cause)
onError(FragmentedRecorderError(message, cause))
}
override fun finish() {
synchronized(this) {
recording = false
@@ -138,7 +152,10 @@ class FragmentedRecordingManager(
val buffer = encoder.getOutputBuffer(index)
if (buffer == null) {
Log.e(TAG, "getOutputBuffer returned null")
failRecording(
"Failed to write fragmented MP4 sample: output buffer was null",
RuntimeException("getOutputBuffer returned null")
)
encoder.releaseOutputBuffer(index, false)
return
}
@@ -146,7 +163,7 @@ class FragmentedRecordingManager(
try {
muxer.writeSampleData(trackIndex, buffer, bufferInfo)
} catch (e: Exception) {
Log.e(TAG, "Error writing sample", e)
failRecording("Failed to write fragmented MP4 sample", e)
}
encoder.releaseOutputBuffer(index, false)
@@ -154,16 +171,22 @@ class FragmentedRecordingManager(
}
override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
Log.e(TAG, "Codec error: ${e.message}")
synchronized(this) {
failRecording("Fragmented MP4 encoder error", e)
}
}
override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
synchronized(this) {
Log.i(TAG, "Output format changed: $format")
trackIndex = muxer.addTrack(format)
muxer.start()
muxerStarted = true
try {
trackIndex = muxer.addTrack(format)
muxer.start()
muxerStarted = true
} catch (e: Exception) {
failRecording("Failed to start fragmented MP4 muxer", e)
}
}
}
}

View File

@@ -273,7 +273,10 @@ class HlsMuxer(
// Write init segment
val initBytes = buildInitSegment(format)
val initFile = File(outputDirectory, "init.mp4")
FileOutputStream(initFile).use { it.write(initBytes) }
FileOutputStream(initFile).use { output ->
output.write(initBytes)
output.fd.sync()
}
// Log frame rate metadata for debugging
val defaultSampleDuration = timescale / fps

View File

@@ -72,6 +72,7 @@ class RecordingSession(
bitRate,
options,
outputPath,
onError,
SEGMENT_DURATION_SECONDS
)
} else {