原因
1 | If the AVPlayer's current item is displaying video on the device's display, playback of the AVPlayer is automatically paused when the app is sent to the background. |
参考:Playing media while in the background using AV Foundation on iOS
一般情况下不会有有黑屏,但是因为这个原因导致可能会发生。我们的App里有openGL的使用,导致性能消耗过大,当退回到后台后系统会释放AVPlayer的资源,所以再次返回的时候会出现短暂的黑屏。
我们的App有一个更加严重的问题,当一个视频播放完之后会停留在当前页面,退回后台再返回会明显黑屏。(如果是视频播放中的话系统会重新加载资源,黑屏时间是短暂发生,但是视频播放完会长期黑屏)
一般解决方式
目前暂时没有好的办法,苹果有两个推荐方案,但是不太试用于我们项目。如下:
- Disable the video tracks in the player item (file-based content only).
- Remove the
AVPlayerLayer from its associated AVPlayer (set the AVPlayerLayer player property to nil).
Disabling the video tracks in the player item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import AVFoundation let playerItem = <#Your player item#> let tracks = playerItem.tracks for playerItemTrack in tracks { // Find the video tracks. if playerItemTrack.assetTrack.hasMediaCharacteristic(AVMediaCharacteristicVisual) { // Disable the track. playerItemTrack.isEnabled = false } } |
Remove/Restore the AVPlayerLayer and its associated AVPlayer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import UIKit import AVFoundation ... // A `UIView` subclass that is backed by an `AVPlayerLayer` layer. class PlayerView: UIView { var player: AVPlayer? { get { return playerLayer.player } set { playerLayer.player = newValue } } var playerLayer: AVPlayerLayer { return layer as! AVPlayerLayer } override class var layerClass: AnyClass { return AVPlayerLayer.self } } ... @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? @IBOutlet weak var playerView: PlayerView! /* Remove the AVPlayerLayer from its associated AVPlayer once the app is in the background. */ func applicationDidEnterBackground(_ application: UIApplication) { // Remove the player. playerView.player = nil } // Restore the AVPlayer when the app is active again. func applicationDidBecomeActive(_ application: UIApplication) { // Restore the player. playerView.player = <#Your player#> } } |
方法一比较通用。方法二只能作用于本地视频。
最终解决方式
我们采用了截屏的方式,截取最后一帧放到PlayerView上,遮挡黑屏
附上截屏方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | extension AVPlayer { func getPlayerScreenShot(screenSize: CGSize, callback: @escaping (UIImage?) ->Void) { //1. 获取当前时间 let currentTime = self.currentTime().value / 1000 if currentTime < 1 { callback(nil) return } guard let urlAsset = currentItem?.asset else { callback(nil) return } //2. 截图配置 let generator = AVAssetImageGenerator(asset: urlAsset) generator.appliesPreferredTrackTransform = true generator.maximumSize = screenSize generator.requestedTimeToleranceBefore = .zero generator.requestedTimeToleranceAfter = .zero //3. 分段截图时间数组 var times: [NSValue] = [] let timeM = CMTimeMake(value: currentTime, timescale: self.currentTime().timescale) let timeV = NSValue(time: timeM) times.append(timeV) //4. 开始截图 generator.generateCGImagesAsynchronously(forTimes: times) { (requestedTime, cgimage, actualTime, result, error) in switch result { case .cancelled, .failed: callback(nil) case .succeeded: guard let image = cgimage else { callback(nil) return } let displayImage = UIImage(cgImage: image) callback(displayImage) } } } } |