fix #5 camera video preview with settling and diagnostics
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

- Wait until the picker movie's file size stops changing before
  staging it, so a movie whose final bytes are still being flushed is
  never copied truncated (which produced drafts without a thumbnail
  and unplayable previews).
- Log the whole camera video path (pickup size, staging result,
  analyze outcome, thumbnail decode) under the video-preview os_log
  category for fast on-device diagnosis.
- Cover the video analysis pipeline with a simulator test that
  generates an HEVC QuickTime movie and asserts duration plus JPEG
  thumbnail, and guard the diagnostics in Scripts/verify.sh.
This commit is contained in:
wt committed 2026-08-29 04:30:40 +07:00
1 parent 75fbcc5346
commit 496ee6f0f2
7 files changed
+137 -5

No files matched your search

+35 -2
View File
@@ -1,6 +1,7 @@
import CoreTransferable
import Foundation
import ImageIO
import os
import PhotosUI
import SwiftData
import SwiftUI
@@ -1263,12 +1264,44 @@ struct ChatView: View {
photoCameraIsPreparingResult = false
releaseArchiveSyncAfterCapture()
}
let logger = Logger(subsystem: "Luma", category: "video-preview")
await waitForCameraFileSettling(at: media.url, logger: logger)
let staged = await stageImportedFiles([media.url], preferredKind: media.kind)
if !staged, model.errorMessage == nil {
model.errorMessage = "Камера вернула файл, который не удалось подготовить к отправке."
if staged {
if let draft = attachmentDrafts.last {
logger.info(
"camera draft staged: kind=\(draft.kind.rawValue) mime=\(draft.mimeType) size=\(draft.byteCount) duration=\(String(describing: draft.duration)) thumbnailBytes=\(draft.thumbnailData?.count ?? 0)"
)
}
} else {
logger.error("camera staging produced no drafts")
if model.errorMessage == nil {
model.errorMessage = "Камера вернула файл, который не удалось подготовить к отправке."
}
}
}
/// The picker can hand over a movie whose final bytes are still being
/// flushed. Wait until the file size stops changing so staging never
/// copies a truncated file.
private func waitForCameraFileSettling(at url: URL, logger: Logger) async {
guard let initial = try? url.resourceValues(forKeys: [.fileSizeKey]).fileSize else {
return
}
var previous = initial
for _ in 0..<10 {
try? await Task.sleep(nanoseconds: 250_000_000)
guard let current = try? url.resourceValues(forKeys: [.fileSizeKey]).fileSize else {
return
}
if current == previous {
return
}
previous = current
}
logger.warning("camera file size kept changing; staging anyway")
}
private func presentCamera() {
#if os(iOS)
guard SystemPhotoCameraView.isAvailable else {