AVFoundationでビデオをループする比較的簡単な方法はありますか?
私はAVPlayerとAVPlayerLayerを次のように作成しました:
avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];
avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];
そして、私は私のビデオを以下で再生します:
[avPlayer play];
ビデオは正常に再生されますが、最後で停止します。 MPMoviePlayerControllerを使用すると、repeatMode
プロパティを適切な値に設定するだけで済みます。 AVPlayerには同様のプロパティはないようです。また、映画がいつ終了したかを教えてくれるコールバックがないので、最初にシークしてもう一度再生できます。
MPMoviePlayerControllerには重大な制限があるため、使用していません。複数のビデオストリームを一度に再生できるようにしたいです。
プレーヤーが終了すると通知を受け取ることができます。チェックAVPlayerItemDidPlayToEndTimeNotification
プレーヤーをセットアップする場合:
ObjC
avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[avPlayer currentItem]];
これにより、プレーヤーが最後に一時停止するのを防ぎます。
通知内:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}
これにより、ムービーが巻き戻されます。
プレーヤーをリリースするときは、通知の登録を解除することを忘れないでください。
スイフト
avPlayer?.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero)
}
}
Swift 4 +
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: CMTime.zero, completionHandler: nil)
}
}
IOS/tvOS 10に役立つ場合、新しいAVPlayerLooper()を使用して、ビデオのシームレスなループ(Swift)を作成できます。
player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()
これはWWDC 2016の「Advances in AVFoundation Playback」で発表されました: https://developer.Apple.com/videos/play/wwdc2016/503/
このコードを使用しても、Appleでバグレポートを提出し、次の応答が返されるまで、問題が発生しました。
オーディオ/ビデオトラックよりも長いムービー時間を持つムービーファイルが問題です。 FigPlayer_Fileは、オーディオトラックの編集が映画の長さより短いため(15.682対15.787)、ギャップレストランジションを無効にします。
ムービーファイルを修正して、ムービーの長さとトラックの長さを同じ長さにするか、AVPlayerLooperの時間範囲パラメーターを使用できます(時間範囲を0からオーディオトラックの時間に設定)
Premiereは、ビデオとは少し異なる長さのオーディオトラックを持つファイルをエクスポートしていたことがわかりました。私の場合、オーディオを完全に削除しても問題ありませんでした。
In Swift:
プレーヤーの終了時に通知を受け取ることができます... AVPlayerItemDidPlayToEndTimeNotificationを確認してください
プレーヤーをセットアップするとき:
avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "playerItemDidReachEnd:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: avPlayer.currentItem)
これにより、プレーヤーが最後に一時停止するのを防ぎます。
通知内:
func playerItemDidReachEnd(notification: NSNotification) {
if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
playerItem.seekToTime(kCMTimeZero)
}
}
Swift
NotificationCenter.default.addObserver(self,
selector: #selector(PlaylistViewController.playerItemDidReachEnd),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
これにより、ムービーが巻き戻されます。
プレーヤーをリリースするときは、通知の登録を忘れないでください。
一時停止の問題を防ぐために私がやったことは次のとおりです。
スイフト:
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: nil,
queue: nil) { [weak self] note in
self?.avPlayer.seek(to: kCMTimeZero)
self?.avPlayer.play()
}
目的C:
__weak typeof(self) weakSelf = self; // prevent memory cycle
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[weakSelf.avPlayer seekToTime:kCMTimeZero];
[weakSelf.avPlayer play];
}];
注:avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone
は必要ないので使用しませんでした。
AVQueuePlayerを使用して、動画をシームレスにループすることをお勧めします。通知オブザーバーを追加する
AVPlayerItemDidPlayToEndTimeNotification
そのセレクターで、ビデオをループします
AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
ビデオが巻き戻されるときのギャップを避けるために、コンポジションで同じアセットの複数のコピーを使用することは私にとってはうまくいきました。ここで見つけました: www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (リンクは現在無効です)。
AVURLAsset *tAsset = [AVURLAsset assetWithURL:tURL];
CMTimeRange tEditRange = CMTimeRangeMake(CMTimeMake(0, 1), CMTimeMake(tAsset.duration.value, tAsset.duration.timescale));
AVMutableComposition *tComposition = [[[AVMutableComposition alloc] init] autorelease];
for (int i = 0; i < 100; i++) { // Insert some copies.
[tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil];
}
AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition];
AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem];
Swift 3および4の場合
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
self.avPlayer?.seek(to: kCMTimeZero)
self.avPlayer?.play()
}
これはしゃっくりの問題なしで私のために働いた、ポイントは一時停止中 seekToTimeメソッドを呼び出す前のプレーヤー:
init AVPlayer
let url = NSBundle.mainBundle().URLForResource("loop", withExtension: "mp4")
let playerItem = AVPlayerItem(URL: url!)
self.backgroundPlayer = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: self.backgroundPlayer)
playerLayer.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
self.layer.addSublayer(playerLayer)
self.backgroundPlayer!.actionAtItemEnd = .None
self.backgroundPlayer!.play()
通知の登録
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
videoLoop関数
func videoLoop() {
self.backgroundPlayer?.pause()
self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
self.backgroundPlayer?.play()
}
objective-c wth AVQueuePlayerでの私のソリューション-AVPlayerItemを複製し、最初の要素の再生が終了したらすぐに別のコピーを追加する必要があるようです。 「Kind of」は理にかなっていて、何の問題もなく動作します
NSURL *videoLoopUrl;
// as [[NSBundle mainBundle] URLForResource:@"assets/yourVideo" withExtension:@"mp4"]];
AVQueuePlayer *_loopVideoPlayer;
+(void) nextVideoInstance:(NSNotification*)notif
{
AVPlayerItem *currItem = [AVPlayerItem playerItemWithURL: videoLoopUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(nextVideoInstance:)
name:AVPlayerItemDidPlayToEndTimeNotification
object: currItem];
[_loopVideoPlayer insertItem:currItem afterItem:nil];
[_loopVideoPlayer advanceToNextItem];
}
+(void) initVideoPlayer {
videoCopy1 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
videoCopy2 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
NSArray <AVPlayerItem *> *dummyArray = [NSArray arrayWithObjects: videoCopy1, videoCopy2, nil];
_loopVideoPlayer = [AVQueuePlayer queuePlayerWithItems: dummyArray];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(nextVideoInstance:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: videoCopy1];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(nextVideoInstance:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: videoCopy2];
}
https://Gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad
ビデオをAVPlayerにロードした後(もちろんそのAVPlayerItemを介して):
[self addDidPlayToEndTimeNotificationForPlayerItem:item];
AddDidPlayToEndTimeNotificationForPlayerItemメソッド:
- (void)addDidPlayToEndTimeNotificationForPlayerItem:(AVPlayerItem *)item
{
if (_notificationToken)
_notificationToken = nil;
/*
Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very simplistic, and not gapless, looped playback.
*/
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
_notificationToken = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:item queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Simple item playback rewind.
[[_player currentItem] seekToTime:kCMTimeZero];
}];
}
ViewWillDisappearメソッドで:
if (_notificationToken) {
[[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
_notificationToken = nil;
}
実装ファイル内のView Controllerのインターフェイス宣言で:
id _notificationToken;
試してみる前にこれを確認する必要がありますか?このサンプルアプリをダウンロードして実行します。
このまさにコードを使用する私のアプリでは、ビデオの終わりから始まりまでの間に一時停止はありません。実際、ビデオによっては、タイムコードの表示を保存して、ビデオが最初に戻ったことを伝える方法がありません。
私がやったことは、以下の私のコードのように、ループ再生にすることです:
[player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0)
queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
if (current >= total) {
[[self.player currentItem] seekToTime:kCMTimeZero];
[self.player play];
}
}];
以下はSwiftのWKWebViewで機能しています。4.1 WKwebviewConfigurationのWKWebViewの主要部分
wkwebView.navigationDelegate = self
wkwebView.allowsBackForwardNavigationGestures = true
self.wkwebView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
wkwebView = WKWebView(frame: wkwebView.frame, configuration: config)
self.view.addSubview(wkwebView)
self.wkwebView.load(NSURLRequest(url: URL(string: self.getUrl())!) as URLRequest)
Swift 4.2 Xcode 10.1で.
はい、AVQueuePlayer()
、Key-Value Observation(KVO)テクニックおよびトークンを使用して、AVKit
/AVFoundation
でビデオをループする比較的簡単な方法があります。それ。
これは、CPUの負荷を最小限に抑えながら、多くのH.264/HEVCビデオで確実に機能します。
コードは次のとおりです。
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
private let player = AVQueuePlayer()
let clips = ["01", "02", "03", "04", "05", "06", "07"]
private var token: NSKeyValueObservation?
var avPlayerView = AVPlayerViewController()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.addAllVideosToPlayer()
present(avPlayerView, animated: true, completion: { self.player.play() })
}
func addAllVideosToPlayer() {
avPlayerView.player = player
for clip in clips {
let urlPath = Bundle.main.path(forResource: clip, ofType: "m4v")!
let url = URL(fileURLWithPath: urlPath)
let playerItem = AVPlayerItem(url: url)
player.insert(playerItem, after: player.items().last)
token = player.observe(\.currentItem) { [weak self] player, _ in
if self!.player.items().count == 1 { self?.addAllVideosToPlayer() }
}
}
}
}
aVPlayerItemDidPlayToEndTimeNotificationオブザーバーを追加し、セレクターの最初からビデオを再生できます。以下のようなコード
//add observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification
object:_aniPlayer.currentItem];
-(void)playbackFinished:(NSNotification *)notification{
[_aniPlayer seekToTime:CMTimeMake(0, 1)];//replay from start
[_aniPlayer play];
}