SwiftUI VideoPlayer加载视频过慢及控制台报错的解决咨询
问题
我正在开发一款视频处理应用,使用SwiftUI的VideoPlayer组件及PHPickerViewController的封装类,将视频从相机胶卷复制至临时目录后,加载一段1分52秒的视频耗时长达10秒。相关代码如下:
import SwiftUI import PhotosUI import AVKit struct ContentView: View { @State private var isShowingPicker = false @State private var videoURL: URL? var body: some View { VStack { Button("Select Video") { isShowingPicker = true } if let videoURL = videoURL { VideoPlayer(player: AVPlayer(url: videoURL)) .frame(height: 300) } } .sheet(isPresented: $isShowingPicker) { PHPickerViewControllerWrapper(videoURL: $videoURL) } } } struct PHPickerViewControllerWrapper: UIViewControllerRepresentable { @Binding var videoURL: URL? func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration() configuration.filter = .videos configuration.selectionLimit = 1 let picker = PHPickerViewController(configuration: configuration) picker.delegate = context.coordinator return picker } func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: PHPickerViewControllerDelegate { let parent: PHPickerViewControllerWrapper init(_ parent: PHPickerViewControllerWrapper) { self.parent = parent } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { guard let result = results.first else { return } result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { (url, error) in guard let url = url else {return} let fileName = "\(Int(Date().timeIntervalSince1970)).\(url.pathExtension)" let newUrl = URL(fileURLWithPath: NSTemporaryDirectory() + fileName) try? FileManager.default.linkItem(at: url, to: newUrl) DispatchQueue.main.async { self.parent.videoURL = newUrl picker.dismiss(animated: true) } } } } }
运行时控制台出现以下报错信息:
2023-05-24 08:01:06.964329-0300 videoLoader[2846:964095] [api] -[CIImage initWithCVPixelBuffer:options:] failed because the buffer is nil. 2023-05-24 08:01:07.041494-0300 videoLoader[2846:964651] [TextIdentificationService] Text LID dominant failure: lidInconclusive 2023-05-24 08:01:07.041839-0300 videoLoader[2846:964651] [VisualTranslationService] Visual isTranslatable: NO; not offering translation: lidInconclusive 2023-05-24 08:01:07.080261-0300 videoLoader[2846:964095] [api] -[CIImage initWithCVPixelBuffer:options:] failed because the buffer is nil. 2023-05-24 08:01:08.008267-0300 videoLoader[2846:964651] Metal API Validation Enabled
请问如何优化视频加载性能?如何消除这些控制台报错信息?
优化视频加载性能
1. 移除不必要的文件链接操作
loadFileRepresentation返回的URL本身就是沙盒内的临时文件,无需额外创建链接。删除linkItem相关代码,直接使用原URL,节省文件操作耗时:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { guard let result = results.first else { return } result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { (url, error) in guard let url = url else {return} DispatchQueue.main.async { self.parent.videoURL = url picker.dismiss(animated: true) } } }
2. 异步预加载视频资源
避免主线程阻塞,后台加载视频元数据后再初始化播放器:
// 在ContentView中添加 @State private var player: AVPlayer? .onChange(of: videoURL) { newURL in guard let newURL = newURL else { player = nil return } Task { let asset = AVAsset(url: newURL) await asset.loadValuesAsynchronously(forKeys: ["playable"]) if asset.statusOfValue(forKey: "playable", error: nil) == .loaded { await MainActor.run { player = AVPlayer(playerItem: AVPlayerItem(asset: asset)) } } } } // 修改视频显示部分 if let player = player { VideoPlayer(player: player) .frame(height: 300) }
3. 调整播放器缓冲策略
通过AVPlayerItem设置缓冲时长,平衡加载速度与内存占用:
let playerItem = AVPlayerItem(asset: asset) playerItem.preferredForwardBufferDuration = 5.0 // 提前缓冲5秒内容 player = AVPlayer(playerItem: playerItem)
消除控制台报错信息
1. 解决CIImage初始化失败报错
手动生成视频缩略图,避免系统自动尝试时出错:
// 新增缩略图生成方法 func generateThumbnail(for url: URL) async -> UIImage? { let asset = AVAsset(url: url) let generator = AVAssetImageGenerator(asset: asset) generator.appliesPreferredTrackTransform = true do { let cgImage = try generator.copyCGImage(at: .zero, actualTime: nil) return UIImage(cgImage: cgImage) } catch { return nil } } // 在加载视频时调用 Task { let _ = await generateThumbnail(for: newURL) }
2. 禁用文本识别相关服务
修改PHPickerConfiguration,关闭自动文本识别功能:
var configuration = PHPickerConfiguration() configuration.filter = .videos configuration.selectionLimit = 1 configuration.preferredAssetRepresentationMode = .current configuration.showsAssetBadges = false
3. 关闭Metal API Validation提示
在Xcode的Scheme设置中,进入Run -> Arguments -> Environment Variables,移除METAL_API_VALIDATION_ENABLED或设置为NO,发布版本会自动关闭该验证。
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

