Перевірте стан відтворення 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.