Перевірте стан відтворення AVPlayer


Відповіді:


57

Щоб отримати повідомлення про досягнення кінця товару (через Apple ):

[[NSNotificationCenter defaultCenter] 
      addObserver:<self>
      selector:@selector(<#The selector name#>)
      name:AVPlayerItemDidPlayToEndTimeNotification 
      object:<#A player item#>];

А відстежувати гру ви можете:

"відстежувати зміни в позиції відтворення в об'єкті AVPlayer", використовуючи addPeriodicTimeObserverForInterval: черга: usingBlock: або addBoundaryTimeObserverForTimes: черга: usingBlock : .

Приклад з Apple:

// Assume a property: @property (retain) id playerObserver;

Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 1);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = [NSArray arrayWithObjects:[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird], nil];

self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
    // Passing NULL for the queue specifies the main queue.

    NSString *timeDescription = (NSString *)CMTimeCopyDescription(NULL, [self.player currentTime]);
    NSLog(@"Passed a boundary at %@", timeDescription);
    [timeDescription release];
}];

Ви можете кинути булевий прапор, щоб перевірити стан відтворення.
— moonman239

Повідомлення можуть спричинити проблеми, якщо ви зміните елемент гравця, наприклад, на -replaceCurrentItemWithPlayerItem:, і не обробляєте його належним чином. Більш надійним способом для мене є відстеження AVPlayerстатусу за допомогою KVO. Дивіться мою відповідь нижче, щоб отримати докладнішу інформацію: stackoverflow.com/a/34321993/3160561
— maxkonovalov

2
Також потрібне сповіщення про помилку? AVPlayerItemFailedToPlayToEndTimeNotification
— ToolmakerSteve

332

Ви можете сказати, що це гра, використовуючи:

AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
    // player is playing
}

Розширення Swift 3:

extension AVPlayer {
    var isPlaying: Bool {
        return rate != 0 && error == nil
    }
}

29
Не обов'язково, він не обробляє ситуації, коли плеєр зупиняється через помилку у файлі. Щось я дізнався важким шляхом ...
— Дермот

7
Як сказав Дермот, якщо ви намагаєтесь грати щось у режимі літака, швидкість AVPlayer все ще встановлена ​​на 1,0, оскільки це передбачає намір грати.
— phi

4
У AVPlayer є властивість помилок, просто перевірте, що вона не нульова, а також перевірка ставки не 0 :)
— Джеймс Кемпбелл

23
Зауважте, що відповідь оновлено, щоб запобігти сценарію @Dermot. Отож, це насправді працює.
— Джомафер

2
Не буде працювати під час відтворення відеороликів, реверсованих ( - [AVPlayer setRate:-1.0]), тому що -1.0менше 0.
— Джуліан Ф. Вайнерт

49

В iOS10 зараз існує вбудована властивість: timeControlStatus

Наприклад, ця функція відтворює або призупиняє avPlayer залежно від її стану та належним чином оновлює кнопку відтворення / паузи.

@IBAction func btnPlayPauseTap(_ sender: Any) {
    if aPlayer.timeControlStatus == .playing {
        aPlayer.pause()
        btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
    } else if aPlayer.timeControlStatus == .paused {
        aPlayer.play()
        btnPlay.setImage(UIImage(named: "control-pause"), for: .normal)
    }
}

Що стосується вашого другого питання, щоб знати, чи досяг avVlayer до кінця, найпростіше зробити налаштування сповіщення.

NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)

Наприклад, коли він закінчується, ви можете перемотати його на початок відео та скинути кнопку "Пауза" для відтворення.

@objc func didPlayToEnd() {
    aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
    btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}

Ці приклади корисні, якщо ви створюєте власні елементи керування, але якщо ви використовуєте AVPlayerViewController, то елементи керування вбудовані.


Простий у використанні для iOS 10+
— technerd

2
@objc func didPlayToEnd () для Swift 4.2
— Sean Stayns

21

rateце НЕ спосіб , щоб перевірити , чи є відео відтворення (він може припинятися). З документації rate:

Позначає бажану швидкість відтворення; 0,0 означає "призупинено", 1,0 вказує на бажання грати з натуральною швидкістю поточного елемента.

Ключові слова "бажання грати" - швидкість 1.0не означає, що відео відтворюється.

Рішенням з iOS 10.0 є використання, AVPlayerTimeControlStatusяке можна спостерігати у AVPlayer timeControlStatusвласності.

Рішення до iOS 10.0 (9.0, 8.0 і т.д.) полягає в тому, щоб запустити власне рішення. Коефіцієнт 0.0означає, що відео призупинено. Коли rate != 0.0це означає, що відео або відтворюється, або затримується.

Дізнатися різницю можна, спостерігаючи за часом гравця за допомогою: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

Блок повертає поточний час програвача у CMTime, тому порівняння lastTime(час, який останній був отриманий від блоку), і currentTime(час, про який щойно повідомлявся блок) покаже, грає гравець або затримується. Наприклад, якщо lastTime == currentTimeі rate != 0.0, то гравець застопорився.

Як зазначають інші, з'ясування того, чи завершено відтворення, позначається символом AVPlayerItemDidPlayToEndTimeNotification.


18

Більш надійною альтернативою NSNotificationє додавання себе в якості спостерігача до rateвласності гравця .

[self.player addObserver:self
              forKeyPath:@"rate"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];

Потім перевірте, чи нове значення спостережуваної швидкості дорівнює нулю, це означає, що відтворення зупинилося з якоїсь причини, як-от досягнення до кінця або зупинення через порожній буфер.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"rate"]) {
        float rate = [change[NSKeyValueChangeNewKey] floatValue];
        if (rate == 0.0) {
            // Playback stopped
        } else if (rate == 1.0) {
            // Normal playback
        } else if (rate == -1.0) {
            // Reverse playback
        }
    }
}

Наприклад rate == 0.0, щоб знати, що саме спричинило зупинку відтворення, можна виконати такі перевірки:

if (self.player.error != nil) {
    // Playback failed
}
if (CMTimeGetSeconds(self.player.currentTime) >=
    CMTimeGetSeconds(self.player.currentItem.duration)) {
    // Playback reached end
} else if (!self.player.currentItem.playbackLikelyToKeepUp) {
    // Not ready to play, wait until enough data is loaded
}

І не забудьте змусити плеєра зупинитися, коли він досягне кінця:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;


Думаю, також потрібне сповіщення про недосягнення кінця - AVPlayerItemFailedToPlayToEndTimeNotification. Якщо ця помилка трапиться, ніколи не досягне кінця часу. Або, читаючи відповідь maz, додайте до свого коду чек player.error != nil, і в цьому випадку відтворення закінчилося через помилку.
— ToolmakerSteve

BTW, що ви вважаєте "не надійним" щодо використання NSNotification AVPlayerItemDidPlayToEndTimeNotification?
— ToolmakerSteve

Дякую за вашу замітку ToolmakerSteve, додав перевірку на помилку до моєї відповіді. Що стосується не надійних сповіщень - я зіткнувся з проблемою сам, реалізуючи повторювані та багаторазові перегляди гравців у режимі перегляду колекцій, і KVO зробила все більш зрозумілим, оскільки дозволило зберігати його все локальніше, коли сповіщення різних гравців не заважали один одному.
— maxkonovalov

17

Для Swift :

AVPlayer :

let player = AVPlayer(URL: NSURL(string: "http://www.sample.com/movie.mov"))
if (player.rate != 0 && player.error == nil) {
   println("playing")
}

Оновлення :
player.rate > 0умова змінено на те, player.rate != 0що якщо відео відтворюється в зворотному порядку, це може бути негативним завдяки Джуліану за те, що він вказав.
Примітка . Це може виглядати так само, як вище (відповідь Маза), але у Swift "! Player.error" давав мені помилку компілятора, тому вам доведеться перевірити помилку за допомогою "player.error == nil" у Swift. (Тому що властивість помилки не належить до типу "Bool")

AVAudioPlayer:

if let theAudioPlayer =  appDelegate.audioPlayer {
   if (theAudioPlayer.playing) {
       // playing
   }
}

AVQueuePlayer:

if let theAudioQueuePlayer =  appDelegate.audioPlayerQueue {
   if (theAudioQueuePlayer.rate != 0 && theAudioQueuePlayer.error == nil) {
       // playing
   }
}

2
AVPlayer! = AVAudioPlayer
— quemeful

2
Застереження: все зазначене вище у відповіді, що з’явилася за два роки до цього.
— Дан Розенстарк

1
@Yar Я знаю, що відповів вище за відповідь, але це швидко і в Swift '! Player.error' не працює для мене, це дозволено лише для типів bool, тому я додав відповідь на жаль, схвалив ваш коментар помилково, щоб дати відповідь, використовуючи це додаток для переповнення стека :)
— Акс

Мене турбує те, що я не знаю, наскільки добре працює програвач player.error на нулі.
— Дан Розенстарк

1
Не працюватимуть під час відтворення відеозаписів у зворотному напрямку ( player.rate = -1.0), оскільки -1,0 менше 0.
— Джуліан Ф. Вайнерт

9

Швидке розширення на основі відповіді maz

extension AVPlayer {

    var isPlaying: Bool {
        return ((rate != 0) && (error == nil))
    }
}

6

Швидка версія відповіді Максновалова така:

player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.New, context: nil)

і

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if keyPath == "rate" {
        if let rate = change?[NSKeyValueChangeNewKey] as? Float {
            if rate == 0.0 {
                print("playback stopped")
            }
            if rate == 1.0 {
                print("normal playback")
            }
            if rate == -1.0 {
                print("reverse playback")
            }
        }
    }
}

Дякую, максконовалов!


Привіт, пане Станев, дякую за вашу відповідь. Це не працює для мене (він же нічого не друкує на консолі?)
— Cesare

Не забудьте, це працює - мій гравець був nil! Але мені потрібно знати, коли погляд починається (а не закінчується). Будь-який спосіб це зробити?
— Чезаре

3

Наразі за допомогою швидкого 5 найпростіший спосіб перевірити, чи грає гра чи призупинився - це перевірити змінну .timeControlStatus .

player.timeControlStatus == .paused
player.timeControlStatus == .playing

1

Відповідь - мета С

if (player.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
    //player is playing
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusPaused) {
    //player is pause
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
    //player is waiting to play
}

0
player.timeControlStatus == AVPlayer.TimeControlStatus.playing

1
Ця надана відповідь може бути правильною, але вона може отримати користь від пояснення . Відповіді лише з коду не вважаються "хорошими" відповідями. Ось декілька вказівок щодо того, як написати гарну відповідь? . З огляду .
— MyNameIsCaleb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.