Удар, так как это вопрос с высоким рейтингом, и похожие вопросы онлайн либо устарели, либо не очень хороши.Вся идея довольно проста с AVKit
и AVFoundation
, что означает, что больше не нужно полагаться на сторонние библиотеки.Единственная проблема заключается в том, что потребовалось немного повозиться и собрать все вместе.
AVFoundation
Player()
инициализация с url
, по-видимому, не является поточно-ориентированной, или, скорее, она не предназначена для этого.Это означает, что независимо от того, как вы инициализируете его в фоновом потоке, атрибуты проигрывателя будут загружаться в основную очередь, вызывая зависания в пользовательском интерфейсе, особенно в UITableView
s и UICollectionViews
.Для решения этой проблемы Apple предоставила AVAsset
, который берет URL-адрес и помогает в загрузке атрибутов мультимедиа, таких как дорожка, воспроизведение, длительность и т. Д., И может делать это асинхронно, при этом лучше всего отменять этот процесс загрузки (в отличие от других очередей отправки).фоновые потоки, где завершение задачи может быть не таким простым).Это означает, что вам не нужно беспокоиться о затяжных потоках зомби в фоновом режиме, поскольку вы быстро прокручиваете таблицу или коллекцию, в конечном итоге накапливая в памяти целый набор неиспользуемых объектов.Эта функция cancellable
великолепна и позволяет нам отменить любую длительную асинхронную загрузку AVAsset
, если она выполняется, но только во время отключения ячейки.Процесс асинхронной загрузки может быть вызван методом loadValuesAsynchronously
и может быть отменен (по желанию) в любое более позднее время (если все еще выполняется).
Не забудьте правильно обработать исключение, используя результатыloadValuesAsynchronously
.В Swift (3/4) , вот как вы могли бы загружать видео асинхронно и обрабатывать ситуации, если асинхронный процесс завершается неудачно (из-за медленных сетей и т. Д.) -
TL; DR
ДЛЯ ВОСПРОИЗВЕДЕНИЯ ВИДЕО
let asset = AVAsset(url: URL(string: self.YOUR_URL_STRING))
let keys: [String] = ["playable"]
var player: AVPlayer!
asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
var error: NSError? = nil
let status = asset.statusOfValue(forKey: "playable", error: &error)
switch status {
case .loaded:
DispatchQueue.main.async {
let item = AVPlayerItem(asset: asset)
self.player = AVPlayer(playerItem: item)
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.frame = self.YOUR_VIDEOS_UIVIEW.bounds
self.YOUR_VIDEOS_UIVIEW.layer.addSublayer(playerLayer)
self.player.isMuted = true
self.player.play()
}
break
case .failed:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
case .cancelled:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
default:
break
}
})
ПРИМЕЧАНИЕ :
В зависимости от того, чего ваше приложение хочет достичь, вы все еще можетечтобы настроить плавность прокрутки в UITableView
или UICollectionView
, нужно немного поработать.Вам также может понадобиться ввести некоторое количество KVO в свойствах AVPlayerItem
, чтобы оно работало, и здесь, в SO, есть множество постов, в которых подробно обсуждается AVPlayerItem
KVO.
ДЛЯ ПЕРЕХОДА ЧЕРЕЗ АКТИВЫ(петли видео / GIF-файлы)
Чтобы зациклить видео, вы можете использовать тот же метод, что и выше, и ввести AVPlayerLooper
.Вот пример кода для зацикливания видео (или, возможно, короткого видео в стиле GIF). Примечание использование ключа duration
, которое требуется для нашего видео цикла.
let asset = AVAsset(url: URL(string: self.YOUR_URL_STRING))
let keys: [String] = ["playable","duration"]
var player: AVPlayer!
var playerLooper: AVPlayerLooper!
asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
var error: NSError? = nil
let status = asset.statusOfValue(forKey: "duration", error: &error)
switch status {
case .loaded:
DispatchQueue.main.async {
let playerItem = AVPlayerItem(asset: asset)
self.player = AVQueuePlayer()
let playerLayer = AVPlayerLayer(player: self.player)
//define Timerange for the loop using asset.duration
let duration = playerItem.asset.duration
let start = CMTime(seconds: duration.seconds * 0, preferredTimescale: duration.timescale)
let end = CMTime(seconds: duration.seconds * 1, preferredTimescale: duration.timescale)
let timeRange = CMTimeRange(start: start, end: end)
self.playerLooper = AVPlayerLooper(player: self.player as! AVQueuePlayer, templateItem: playerItem, timeRange: timeRange)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.frame = self.YOUR_VIDEOS_UIVIEW.bounds
self.YOUR_VIDEOS_UIVIEW.layer.addSublayer(playerLayer)
self.player.isMuted = true
self.player.play()
}
break
case .failed:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
case .cancelled:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
default:
break
}
})
РЕДАКТИРОВАНИЕ : согласно документации ,AVPlayerLooper
требует, чтобы свойство duration
ресурса было полностью загружено, чтобы иметь возможность циклически просматривать видео.Кроме того, timeRange: timeRange
с начальным и конечным временным диапазоном в инициализации AVPlayerLooper
действительно необязателен, если вы хотите бесконечный цикл.С того момента, как я опубликовал этот ответ, я также понял, что * * * * * * * * * * * * является точной только на 70-80% в цикле видео, особенно если вашему AVAsset
нужно транслировать видео с URL.Чтобы решить эту проблему, существует совершенно другой (но простой) подход к циклу видео-
//this will loop the video since this is a Gif
let interval = CMTime(value: 1, timescale: 2)
self.timeObserverToken = self.player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { (progressTime) in
if let totalDuration = self.player?.currentItem?.duration{
if progressTime == totalDuration{
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
}
})