Есть ли относительно простой способ зациклить видео в 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]; }
это перемотает фильм.
Не забудьте отменить регистрацию уведомления при освобождении плеера.
Swift
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) } }
источник
avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
это, это не остановится, поэтому не нужно настраивать его для воспроизведения снова[player play];
после перемотки позвонить .[[NSNotificationCenter defaultCenter] removeObserver:self];
например, когдаself
слушаете уведомления.Если это поможет, в iOS / tvOS 10 есть новый AVPlayerLooper (), который вы можете использовать для создания бесшовного цикла видео (Swift):
Это было представлено на WWDC 2016 в разделе «Достижения в области воспроизведения AVFoundation»: https://developer.apple.com/videos/play/wwdc2016/503/
Даже при использовании этого кода у меня возникала икота, пока я не отправил отчет об ошибке в Apple и не получил такой ответ:
Оказалось, что Premiere экспортировал файлы со звуковой дорожкой, длина которой немного отличалась от длины видео. В моем случае можно было полностью удалить звук, и это устранило проблему.
источник
В 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) } }
Swift3
NotificationCenter.default.addObserver(self, selector: #selector(PlaylistViewController.playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avPlayer?.currentItem)
это перемотает фильм.
Не забудьте отменить регистрацию уведомления при отпускании плеера.
источник
Вот что я в итоге сделал, чтобы предотвратить проблему паузы и икоты:
Swift:
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
как он не нужен.источник
[weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play];
строками - когда я закомментировал эти строки, утечки памяти больше нет. Я проанализировал это в инструментах.Я рекомендую использовать 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];
источник
это сработало для меня без проблем с икотой , дело в том, чтобы приостановить проигрыватель перед вызовом метода seekToTime:
инициализировать 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() }
источник
Для Swift 3 и 4
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in self.avPlayer?.seek(to: kCMTimeZero) self.avPlayer?.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
источник
Swift 5:
Я внес некоторые небольшие изменения из предыдущих ответов, например, добавил playerItem в очередь, прежде чем добавлять его в playerLayer.
let playerItem = AVPlayerItem(url: url) let player = AVQueuePlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: player) playerLooper = AVPlayerLooper(player: player, templateItem: playerItem) playerLayer.frame = cell.eventImage.bounds playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill // Add the playerLayer to a UIView.layer player.play()
И сделайте playerLooper свойством вашего UIViewController, иначе видео может воспроизводиться только один раз.
источник
После загрузки видео в 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;
Хотите убедиться в этом, прежде чем пытаться? Загрузите и запустите этот образец приложения:
https://developer.apple.com/library/prerelease/ios/samplecode/AVBasicVideoOutput/Listings/AVBasicVideoOutput_APLViewController_m.html#//apple_ref/doc/uid/DTS40013109-AVBasicVideoOutput_ElementControlView
В моем приложении, которое использует именно этот код, между концом видео и началом нет никакой паузы. Фактически, в зависимости от видео, я не могу сказать, что видео снова в начале, сохраните отображение временного кода.
источник
вы можете добавить наблюдателя 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]; }
источник
Следующее работает для меня в WKWebView в быстром 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]; } }];
источник
Swift 4.2 в Xcode 10.1.
Да , есть относительно простой способ зацикливания
AVKit
/AVFoundation
использования видео,AVQueuePlayer()
метод наблюдения за ключом (KVO) и токен для него.Это определенно работает для группы видео 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() } } } } }
источник
SWIFT 5:
private var player: AVPlayer? override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(restartVideo), name: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem) } @objc func restartVideo() { player?.pause() player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in 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.источник