关于 AVPlayer 播放黑屏问题的解决

原因

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

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

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上,遮挡黑屏

附上截屏方法:

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)
      }
    }
  }
}

你可能感兴趣的:(关于 AVPlayer 播放黑屏问题的解决)