Я использую AVPlayer
и AVPlayerLayer
для отображения живого контента HLS. Проблема в том, что AVPlayerLayer - , даже если он отображается на экране и правильно прикреплен как подпредставление , не отображает контент / графику, но вместо этого воспроизводит только аудио . Проблема возникает только на реальном устройстве , но на симуляторе все работает отлично.
При тестировании того же кода с некоторыми другими образцами URL ( .mp4, .m3u8), найденными в inte rnet, он отлично работает как на устройстве, так и на симуляторе.
Для случаев, когда проигрыватель не показывает никакого контента, а только аудио, я использую прямой эфир клиента HLS версии 7, 25 кадров в секунду.
У меня есть сомнения в том, что с момента запуска приложения до появления первого кадра графики (на симуляторе) возникает огромная задержка, вызывающая срыв. Я рассмотрел, добавив automaticallyWaitsToMinimizeStalling
к false
. Полный пример кода здесь:
import UIKit
import AVFoundation
class PlayerView: UIView {
private var playerItem: AVPlayerItem?
private var playerItemContext = 0
var player: AVPlayer? {
get { return playerLayer.player }
set { playerLayer.player = newValue }
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
func play(with url: URL) {
setUpAsset(with: url) { [weak self] (asset: AVAsset) in
self?.setUpPlayerItem(with: asset)
}
}
private func setUpAsset(with url: URL, completion: ((_ asset: AVAsset) -> Void)?) {
let asset = AVAsset(url: url)
asset.loadValuesAsynchronously(forKeys: ["playable"]) {
var error: NSError? = nil
let status = asset.statusOfValue(forKey: "playable", error: &error)
switch status {
case .loaded:
completion?(asset)
print("--- Loaded ---")
case .failed:
print("--- Failed ---")
case .cancelled:
print("--- Cancelled ---")
default: break
}
}
}
private func setUpPlayerItem(with asset: AVAsset) {
playerItem = AVPlayerItem(asset: asset)
playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: &playerItemContext)
DispatchQueue.main.async { [weak self] in
self?.player = AVPlayer(playerItem: self?.playerItem!)
self?.player?.automaticallyWaitsToMinimizeStalling = false
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &playerItemContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath == #keyPath(AVPlayerItem.status) {
let status: AVPlayerItem.Status
if let statusNumber = change?[.newKey] as? NSNumber {
status = AVPlayerItem.Status(rawValue: statusNumber.intValue)!
} else {
status = .unknown
}
switch status {
case .readyToPlay: player?.play()
case .failed: print(".failed")
case .unknown: print(".unknown")
@unknown default: print("@unknown default")
}
}
}
deinit {
playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))
}
}
// MARK: - View Controller -
class ViewController: UIViewController {
private var playerView: PlayerView!
@IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.setUpPlayerView()
self.playVideo()
}
private func setUpPlayerView() {
playerView = PlayerView()
playerView.backgroundColor = UIColor.black
playerView.frame = self.videoView.bounds
videoView.addSubview(playerView)
}
func playVideo() {
playerView.play(with: url)
}
}