AVPlayer播放本地视频黑屏问题排查:代码报错咨询
问题分析与解决方案
先看你本地视频播放的代码,有两个关键问题直接导致了黑屏和警告:
1. 文件存在判断逻辑完全写反了
你这段代码里的判断:
if (MediaUtils.fileExists(filePath: filepath)) { Logging.logError("Playing video failed, file not found: \(filepath)") return false }
意思是当文件存在的时候,反而输出“文件未找到”的错误日志并返回false,直接跳过了后续的播放逻辑!这就导致文件明明存在,但代码根本没执行播放的部分,自然会黑屏。正确的逻辑应该是判断文件不存在的时候才报错返回。
2. AVPlayerViewController的错误展示方式
你同时做了两件事:把playerViewController.view添加到传入的view作为子视图,又调用vc.present(playerViewController, animated: true)。这两种方式都是用来展示AVPlayerViewController的,同时使用会导致视图层级混乱,触发你看到的Unbalanced calls to begin/end appearance transitions警告,而且展示逻辑冲突。
你应该二选一:要么present模态展示,要么把playerViewController作为子控制器添加到当前VC(而不是只加view)。
修正后的本地视频播放代码
这里给出修正后的代码,同时解决上述两个问题:
static func playVideo(from filename: String, vc: UIViewController, view: UIView) -> Bool { let filepath = videosDirectoryPath + filename guard MediaUtils.fileExists(filePath: filepath) else { Logging.logError("Playing video failed, file not found: \(filepath)") return false } let player = AVPlayer(url: URL(fileURLWithPath: filepath)) let playerViewController = AVPlayerViewController() playerViewController.player = player // 选择一种展示方式:要么模态present(和你网络播放逻辑一致),要么作为子控制器嵌入 // 方式1:模态present(推荐) vc.present(playerViewController, animated: true) { player.play() } // 方式2:如果要嵌入到指定view中,需要添加子控制器(不要只加view) // vc.addChild(playerViewController) // playerViewController.view.frame = view.bounds // view.addSubview(playerViewController.view) // playerViewController.didMove(toParent: vc) // player.play() return true }
如何获取player.play()的错误信息
要捕获AVPlayer的播放错误,可以通过两种方式实现:
方法1:监听AVPlayerItem的status状态
let playerItem = AVPlayerItem(url: URL(fileURLWithPath: filepath)) let player = AVPlayer(playerItem: playerItem) // 添加状态监听 playerItem.addObserver(self, forKeyPath: "status", options: [.new, .initial], context: nil) // 在observeValue方法中处理状态变化 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "status", let playerItem = object as? AVPlayerItem { switch playerItem.status { case .failed: if let error = playerItem.error { Logging.logError("Playback failed with error: \(error.localizedDescription)") } case .readyToPlay: Logging.logDebug("Player is ready to play") case .unknown: Logging.logDebug("Player status unknown") @unknown default: break } } } // 记得在合适时机移除监听,比如VC销毁时 // playerItem.removeObserver(self, forKeyPath: "status")
方法2:监听播放失败通知
NotificationCenter.default.addObserver(self, selector: #selector(handlePlaybackFailure(_:)), name: .AVPlayerItemFailedToPlayToEndTime, object: player.currentItem) @objc func handlePlaybackFailure(_ notification: Notification) { if let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error { Logging.logError("Playback failed: \(error.localizedDescription)") } } // 记得在合适时机移除通知监听 // NotificationCenter.default.removeObserver(self, name: .AVPlayerItemFailedToPlayToEndTime, object: nil)
这样就能准确获取到播放失败的具体错误信息了。
内容的提问来源于stack exchange,提问作者rysv
相关产品推荐
相关产品推荐

