Document camera finalization ownership and isolate late SDK diagnostics
This commit is contained in:
1 parent
8dd8dc96d7
commit
1529dfd04b
2 files changed
+32
-10
No files matched your search
+21
-10
@@ -283,13 +283,18 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) -
|
||||
val ready = provider; val selected = lens
|
||||
if (ready != null && selected != null) {
|
||||
if (mode == CaptureMode.PHOTO) {
|
||||
resolutions = CameraLensManager.photoResolutions(ready, selected.cameraId)
|
||||
resolution = photoSettings.resolveResolution(resolutions)
|
||||
if (photoSettings.width > 0 && (resolution?.width != photoSettings.width || resolution?.height != photoSettings.height))
|
||||
LogManager.addLog("WARN", "Capture", "Saved photo resolution unavailable camera=${selected.cameraId} " +
|
||||
"saved=${photoSettings.width}x${photoSettings.height} selected=${resolution?.label}")
|
||||
LogManager.addLog("INFO", "Capture", "Photo settings restored camera=${selected.cameraId} " +
|
||||
"resolution=${resolution?.label} jpegQuality=$jpegQuality")
|
||||
try {
|
||||
resolutions = CameraLensManager.photoResolutions(ready, selected.cameraId)
|
||||
resolution = photoSettings.resolveResolution(resolutions)
|
||||
if (photoSettings.width > 0 && (resolution?.width != photoSettings.width || resolution?.height != photoSettings.height))
|
||||
LogManager.addLog("WARN", "Capture", "Saved photo resolution unavailable camera=${selected.cameraId} " +
|
||||
"saved=${photoSettings.width}x${photoSettings.height} selected=${resolution?.label}")
|
||||
LogManager.addLog("INFO", "Capture", "Photo settings restored camera=${selected.cameraId} " +
|
||||
"resolution=${resolution?.label} jpegQuality=$jpegQuality")
|
||||
} catch (e: Exception) {
|
||||
resolutions = emptyList(); resolution = null; error = true
|
||||
LogManager.addLog("ERROR", "Capture", "Photo capabilities failed camera=${selected.cameraId}: $e")
|
||||
}
|
||||
return@LaunchedEffect
|
||||
}
|
||||
try {
|
||||
@@ -404,7 +409,11 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) -
|
||||
val pending = videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file).build()).withAudioEnabled()
|
||||
attemptedStart = true
|
||||
recording = pending.start(main) { event ->
|
||||
if (event is VideoRecordEvent.Finalize) resources.completed(file)
|
||||
if (event is VideoRecordEvent.Finalize) {
|
||||
if (event.hasError()) LogManager.addLog(if (closed || output != file) "DEBUG" else "ERROR", "Capture",
|
||||
"Video finalize code=${event.error} discarded=${closed || output != file}: ${event.cause}")
|
||||
resources.completed(file)
|
||||
}
|
||||
if (closed || output != file) {
|
||||
if (event is VideoRecordEvent.Finalize) resources.discard(file)
|
||||
} else when (event) {
|
||||
@@ -426,7 +435,6 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) -
|
||||
recording = null
|
||||
if (event.hasError()) {
|
||||
resources.discard(file); output = null; pendingSend = false; phase = CapturePhase.IDLE; error = true
|
||||
LogManager.addLog("ERROR", "Capture", "Video finalize failed code=${event.error}: ${event.cause?.message}")
|
||||
} else {
|
||||
phase = CapturePhase.FINALIZING
|
||||
scope.launch {
|
||||
@@ -486,7 +494,10 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) -
|
||||
val request = selected?.cameraControl?.enableTorch(next)
|
||||
request?.addListener({
|
||||
try { request.get(); if (!closed && camera === selected) torch = next }
|
||||
catch (e: Exception) { error = true; LogManager.addLog("ERROR", "Capture", "Torch failed: $e") }
|
||||
catch (e: Exception) {
|
||||
if (!closed && camera === selected) error = true
|
||||
LogManager.addLog("ERROR", "Capture", "Torch failed: $e")
|
||||
}
|
||||
}, main)
|
||||
} catch (e: Exception) { error = true; LogManager.addLog("ERROR", "Capture", "Torch request failed: $e") }
|
||||
}) { Icon(if (torch) Icons.Default.FlashOn else Icons.Default.FlashOff, stringResource(R.string.camera_light)) }
|
||||
|
||||
@@ -266,6 +266,17 @@ feed/pull workers и маршрутом Android Telecom; DSP и сетевой
|
||||
Вызовы lifecycle DSP выполняются после остановки его внешних feed/pull callers.
|
||||
Контракты PCM/PTS: `src/call/call_audio.h`, `src/radio/radio_audio.h`, `jni_bridge/android_jni_bridge.h`.
|
||||
|
||||
CameraRecordScreen сохраняет CameraCaptureResources до захвата и снимает только свои
|
||||
use cases. Cache фото живёт до OnImageSaved/onError, видео — до Finalize; discard/выход
|
||||
UI сначала запрашивает закрытие записи и ждёт writer перед удалением файла.
|
||||
Ошибки Recording.close, unbind, отключения ориентации и удаления независимы и сохраняются
|
||||
в process registry с retry. Поздний callback не восстанавливает UI и не удаляет переданный sender файл.
|
||||
Pause/resume подтверждаются событиями CameraX; разрешения перечитываются при ON_RESUME.
|
||||
VideoFileTracks проверяет MIME/геометрию готового MP4; MediaExtractor остаётся до release ACK,
|
||||
ошибка чтения сохраняется при дополнительной ошибке cleanup. Контракты CameraX:
|
||||
[Recording.close](https://developer.android.com/reference/androidx/camera/video/Recording#close()),
|
||||
[PendingRecording.start](https://developer.android.com/reference/androidx/camera/video/PendingRecording#start(java.util.concurrent.Executor,androidx.core.util.Consumer)).
|
||||
|
||||
Локальный `VideoPlayerScreen` сохраняет пассивный VideoFilePlayer до start. UI отражает
|
||||
результаты MediaPlayer start/pause/seek, ошибка команды завершает playback и оставляет
|
||||
доступным закрытие экрана. Пользовательская пауза сохраняется при пересоздании Surface.
|
||||
|
||||
Reference in new issue
Block a user