私はAVQueuePlayer
のサブクラスを使用しており、ストリーミングURLで新しいAVPlayerItem
を追加すると、アプリが約1〜2秒フリーズします。フリーズとは、UIへのタッチに反応しないことを意味します。また、すでに曲を再生していて、別の曲をキューに追加した場合、AVQueuePlayer
は最初の曲のストリーミング中に自動的に曲のプリロードを開始します。これにより、最初の曲を追加するときと同様に、UIへのタッチにアプリが2秒間応答しなくなりますが、曲はまだ再生されています。つまり、これはAVQueuePlayer
がメインスレッドで何らかの動作を行って、明らかに「フリーズ」を引き起こしていることを意味します。
使ってます insertItem:afterItem:
を追加してAVPlayerItem
を追加します。私はテストして、これが遅延を引き起こしている方法であることを確認しました。キューに追加したときにAVPlayerItem
によってアクティブ化されたときにAVQueuePlayer
が実行する可能性があります。
Dropbox API v1ベータ版を使用して、このメソッド呼び出しを使用してストリーミングURLを取得していることを指摘する必要があります。
[[self restClient] loadStreamableURLForFile:metadata.path];
次に、ストリームURLを受け取ったら、次のようにAVQueuePlayer
に送信します。
[self.player insertItem:[AVPlayerItem playerItemWithURL:url] afterItem:nil];
だから私の質問は:これをどうやって避けるのですか? AVPlayer
の助けを借りずに、自分でオーディオストリームのプリロードを行う必要がありますか?もしそうなら、どうすればいいですか?
ありがとう。
playerItemWithURL
は同期しないので使用しないでください。 URLを含む応答を受け取ったら、次のことを試してください。
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = @[@"playable"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
[self.player insertItem:[AVPlayerItem playerItemWithAsset:asset] afterItem:nil];
}];
バンプ、これは非常に評価された質問であり、オンラインでの同様の質問は回答が古くなっているか、あまりよくありません。 AVKit
とAVFoundation
を使用すると、全体のアイデアは非常に簡単です。つまり、サードパーティのライブラリに依存する必要がなくなります。唯一の問題は、いじくり回して部品を組み合わせるということです。
AVFoundation
のPlayer()
を使用したurl
による初期化は、スレッドセーフではないようです。つまり、スレッドセーフではありません。つまり、バックグラウンドスレッドでどのように初期化しても、プレーヤーの属性がメインキューに読み込まれ、特にUITableView
sとUICollectionViews
でUIがフリーズします。この問題を解決するにはApple AVAsset
が提供されます。これは、URLを受け取り、トラック、再生、再生時間などのメディア属性の読み込みを支援します。この読み込みプロセスはキャンセル可能であること(タスクを終了することが他のDispatchキューのバックグラウンドスレッドとは異なります)。これは、テーブルビューで高速にスクロールするときに、バックグラウンドでゾンビスレッドが残っていることを心配する必要がないことを意味します。またはコレクションビュー。最終的には、未使用のオブジェクトがたくさんあるメモリに蓄積されます。この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
でスムーズにスクロールするために、アプリを調整するために多少の調整が必要になる場合があります。それが機能するためには、AVPlayerItem
プロパティにある程度のKVOを実装する必要がある場合もあります。ここのSOに、AVPlayerItem
KVOについて説明するたくさんの投稿があります。詳細。
ビデオをループするには、上記と同じ方法を使用し、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
}
})
[〜#〜] edit [〜#〜]: documentation に従って、AVPlayerLooper
には動画をループできるようにするために完全に読み込まれるアセットのduration
プロパティ。また、timeRange: timeRange
は、無限ループが必要な場合、AVPlayerLooper
初期化の開始時間範囲と終了時間範囲を使用して、オプションです。この回答を投稿してから、AVPlayerLooper
は特にAVAsset
がURLから動画をストリーミングする必要がある場合に、動画のループで約70〜80%だけ正確であることにも気付きました。この問題を解決するために、ビデオをループするためのまったく異なる(まだ単純な)アプローチがあります。
//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()
}
}
})
Swiftに対するGigisommoの回答には、コメントからのフィードバックが含まれています。
let asset = AVAsset(url: url)
let keys: [String] = ["playable"]
asset.loadValuesAsynchronously(forKeys: keys) {
DispatchQueue.main.async {
let item = AVPlayerItem(asset: asset)
self.playerCtrl.player = AVPlayer(playerItem: item)
}
}