Files
Luma/Tests/MediaPreviewProcessorVideoTests.swift
T
wt 496ee6f0f2
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
fix #5 camera video preview with settling and diagnostics
- 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.
2026-08-29 04:30:40 +07:00

69 lines
2.6 KiB
Swift

import AVFoundation
import XCTest
@testable import Luma
final class MediaPreviewProcessorVideoTests: XCTestCase {
/// Reproduces the camera-video analysis on the simulator: the pipeline
/// must produce a duration and a JPEG thumbnail for a freshly written
/// HEVC QuickTime movie.
func testVideoAnalysisProducesDurationAndThumbnail() async throws {
let url = try await Self.makeTestMovie()
defer { try? FileManager.default.removeItem(at: url) }
let processor = MediaPreviewProcessor()
let analysis = await processor.analyze(
url: url,
kind: .video,
mimeType: "video/quicktime"
)
XCTAssertNotNil(analysis.duration)
if let duration = analysis.duration {
XCTAssertGreaterThan(duration, 0)
}
XCTAssertNotNil(analysis.thumbnailData)
XCTAssertFalse(analysis.thumbnailData?.isEmpty ?? true)
}
private static func makeTestMovie() async throws -> URL {
let output = FileManager.default.temporaryDirectory
.appendingPathComponent("test-camera-\(UUID().uuidString).mov")
let writer = try AVAssetWriter(outputURL: output, fileType: .mov)
let input = AVAssetWriterInput(
mediaType: .video,
outputSettings: [
AVVideoCodecKey: AVVideoCodecType.hevc,
AVVideoWidthKey: 640,
AVVideoHeightKey: 480,
]
)
input.expectsMediaDataInRealTime = true
let adaptor = AVAssetWriterInputPixelBufferAdaptor(
assetWriterInput: input,
sourcePixelBufferAttributes: [
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
kCVPixelBufferWidthKey as String: 640,
kCVPixelBufferHeightKey as String: 480,
]
)
writer.add(input)
guard writer.startWriting() else {
throw writer.error ?? NSError(domain: "test", code: 1)
}
writer.startSession(atSourceTime: .zero)
for frame in 0..<60 {
while !input.isReadyForMoreMediaData {
try await Task.sleep(nanoseconds: 2_000_000)
}
var buffer: CVPixelBuffer?
CVPixelBufferCreate(
kCFAllocatorDefault, 640, 480, kCVPixelFormatType_32BGRA, nil, &buffer
)
adaptor.append(buffer!, withPresentationTime: CMTime(value: Int64(frame), timescale: 30))
}
input.markAsFinished()
await writer.finishWriting()
return output
}
}