Зацикливание видео с AVFoundation "плеере АВ"?
есть ли относительно простой способ зацикливания видео в 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, потому что у него есть некоторые серьезные ограничения. Я хочу иметь возможность воспроизводить сразу несколько видеопотоков.
15 ответов
вы можете получить уведомление, когда игрок заканчивается. Проверка 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];
}
это будет перемотать фильм.
Не забудьте отменить регистрацию уведомления при освобождении игрока.
Свифт
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: Notification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero, 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 в "достижения в воспроизведении AVFoundation": https://developer.apple.com/videos/play/wwdc2016/503/
даже используя этот код, у меня была икота, пока я не подал отчет об ошибке с Apple и не получил этот ответ:
файл фильма, имеющий продолжительность фильма больше, чем аудио / видео треки проблема. FigPlayer_File отключает беззазорный переход, потому что редактирование звуковой дорожки короче, чем продолжительность фильма (15.682 vs 15.787).
вам нужно либо исправить файлы фильмов, чтобы иметь продолжительность фильма и длительность трека должна быть одинаковой или вы можете использовать диапазон времени параметр AVPlayerLooper (установить диапазон времени от 0 до продолжительности аудио трек)
оказывается, что Premiere экспортировал файлы с помощью звуковая дорожка немного отличается по длине от видео. В моем случае было нормально Полностью удалить звук, и это исправило проблему.
на Свифт:
вы можете получить уведомление, когда игрок заканчивается... проверьте 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)
}
}
Swift3
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:
-
"плеере АВ" инит
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() }
после загрузки видео в 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;
}
в объявлении интерфейса контроллера вашего вида в файле реализации:
id _notificationToken;
нужно увидеть это вверх и работает, прежде чем пытаться? Загрузите и запустите этот образец app:
в моем приложении, которое использует этот самый код, нет никакой паузы между концом видео и началом. На самом деле, в зависимости от видео, я не могу сказать, что видео снова в начале, сохраните отображение тайм-кода.
вы можете добавить 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];
}
мое решение в objective-c с AVQueuePlayer-кажется, вам нужно дублировать AVPlayerItem и по завершении воспроизведения первого элемента мгновенно добавить другую копию. "Вид" имеет смысл и работает для меня без каких-либо икоты
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
следующее работает для меня в WKWebView в swift 4.1 Основная часть WKWebView в WKwebviewConfiguration
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)
то, что я сделал, это сделать его цикл игры, как мой код ниже:
[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];
}
}];
используйте AVPlayerViewController ниже кода, он работает для меня
let type : String! = "mp4"
let targetURL : String? = NSBundle.mainBundle().pathForResource("Official Apple MacBook Air Video YouTube", ofType: "mp4")
let videoURL = NSURL(fileURLWithPath:targetURL!)
let player = AVPlayer(URL: videoURL)
let playerController = AVPlayerViewController()
playerController.player = player
self.addChildViewController(playerController)
self.playView.addSubview(playerController.view)
playerController.view.frame = playView.bounds
player.play()
все элементы управления, которые будут показаны, надеюсь, что его полезным
/* "numberOfLoops" is the number of times that the sound will return to the beginning upon reaching the end.
A value of zero means to play the sound just once.
A value of one will result in playing the sound twice, and so on..
Any negative number will loop indefinitely until stopped.
*/
@property NSInteger numberOfLoops;
Это свойство уже определено внутри AVAudioPlayer
. Надеюсь, это поможет вам.
Я использую Xcode 6.3.