Periksa status putar AVPlayer

Jawaban:

57

Untuk mendapatkan pemberitahuan untuk mencapai akhir item (melalui Apple ):

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

Dan untuk melacak permainan Anda dapat:

"track perubahan di posisi playhead dalam suatu objek AVPlayer" dengan menggunakan addPeriodicTimeObserverForInterval: antrian: usingBlock: atau addBoundaryTimeObserverForTimes: antrian: usingBlock: .

Contohnya dari 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];
}];
Todd Hopkinson
sumber
Anda bisa melempar bendera Boolean untuk memeriksa status putar.
moonman239
Pemberitahuan dapat menyebabkan masalah jika Anda mengganti item pemain, misalnya, dengan -replaceCurrentItemWithPlayerItem:, dan tidak menanganinya dengan benar. Cara yang lebih dapat diandalkan bagi saya adalah melacak AVPlayerstatus menggunakan KVO. Lihat jawaban saya di bawah untuk detail lebih lanjut: stackoverflow.com/a/34321993/3160561
maxkonovalov
2
Juga butuh notifikasi error? AVPlayerItemFailedToPlayToEndTimeNotification
ToolmakerSteve
332

Anda dapat membedakannya dengan menggunakan:

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

Ekstensi Swift 3:

extension AVPlayer {
    var isPlaying: Bool {
        return rate != 0 && error == nil
    }
}
maz
sumber
29
Belum tentu, itu tidak menangani situasi di mana pemain terhenti karena kesalahan dalam file. Sesuatu yang saya temukan dengan cara yang sulit ...
Dermot
7
Seperti yang dikatakan Dermot, jika Anda mencoba memainkan sesuatu saat dalam mode pesawat, tingkat AVPlayer masih disetel ke 1.0, karena ini menyiratkan niat untuk bermain.
phi
4
AVPlayer memiliki properti kesalahan, cukup periksa bahwa itu tidak nihil serta memeriksa tarifnya bukan 0 :)
James Campbell
23
Perhatikan bahwa jawaban telah diperbarui untuk mencegah skenario @Dermot. Jadi yang ini benar-benar berfungsi.
jomafer
2
Tidak akan berfungsi saat memutar rekaman video terbalik ( - [AVPlayer setRate:-1.0]), karena -1.0kurang dari 0.
Julian F. Weinert
49

Di iOS10, ada properti bawaan untuk ini sekarang: timeControlStatus

Misalnya, fungsi ini memutar atau menjeda avPlayer berdasarkan statusnya dan memperbarui tombol putar / jeda dengan tepat.

@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)
    }
}

Adapun pertanyaan kedua Anda, untuk mengetahui apakah avPlayer mencapai akhir, hal termudah yang harus dilakukan adalah mengatur notifikasi.

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

Saat video mencapai akhir, misalnya, Anda dapat memundurkannya ke awal video dan mengatur ulang tombol Jeda ke Putar.

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

Contoh ini berguna jika Anda membuat kontrol Anda sendiri, tetapi jika Anda menggunakan AVPlayerViewController, kontrol tersebut sudah ada di dalamnya.

Travis M.
sumber
Mudah digunakan untuk iOS 10+
technerd
2
@objc func didPlayToEnd () untuk Swift 4.2
Sean Stayns
21

rateadalah TIDAK cara untuk memeriksa apakah video yang diputar (bisa terhenti). Dari dokumentasi rate:

Menunjukkan kecepatan pemutaran yang diinginkan; 0,0 berarti "dijeda", 1,0 menunjukkan keinginan untuk bermain dengan kecepatan alami dari item saat ini.

Kata kunci "keinginan untuk bermain" - tingkat 1.0tidak berarti video sedang diputar.

Solusi sejak iOS 10.0 adalah menggunakan AVPlayerTimeControlStatusyang dapat diamati di AVPlayer timeControlStatusproperti.

Solusi sebelum iOS 10.0 (9.0, 8.0 dll.) Adalah menggulung solusi Anda sendiri. Nilai rata 0.0-rata video dijeda. Jika rate != 0.0itu berarti video sedang diputar atau terhenti.

Anda dapat mengetahui perbedaannya dengan mengamati waktu pemain melalui: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

Blok mengembalikan waktu pemain saat ini masuk CMTime, jadi perbandingan lastTime(waktu yang terakhir diterima dari blok) dan currentTime(waktu blok baru saja dilaporkan) akan memberi tahu apakah pemain itu bermain atau terhenti. Misalnya, jika lastTime == currentTimedan rate != 0.0, maka pemain terhenti.

Sebagaimana dicatat oleh orang lain, mencari tahu apakah pemutaran telah selesai ditunjukkan dengan AVPlayerItemDidPlayToEndTimeNotification.

kgaidis
sumber
18

Alternatif yang lebih dapat diandalkan NSNotificationadalah menambahkan diri Anda sebagai pengamat ke rateproperti pemain .

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

Kemudian periksa apakah nilai baru untuk kecepatan yang diamati adalah nol, yang berarti pemutaran berhenti karena beberapa alasan, seperti mencapai akhir atau terhenti karena buffer kosong.

- (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
        }
    }
}

Sebagai rate == 0.0contoh, untuk mengetahui apa sebenarnya yang menyebabkan pemutaran berhenti, Anda dapat melakukan pemeriksaan berikut:

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
}

Dan jangan lupa untuk membuat pemain Anda berhenti saat mencapai akhir:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;

maxkonovalov.dll
sumber
Saya pikir juga perlu pemberitahuan kegagalan untuk mencapai akhir - AVPlayerItemFailedToPlayToEndTimeNotification. Jika kesalahan ini terjadi, tidak akan pernah mencapai akhir zaman. Atau membaca jawaban maz, tambahkan cek untuk kode Anda player.error != nil, dalam hal ini pemutaran telah "berakhir" karena kesalahan.
ToolmakerSteve
BTW, menurut Anda apa yang "tidak dapat diandalkan" tentang penggunaan NSNotification AVPlayerItemDidPlayToEndTimeNotification?
ToolmakerSteve
ToolmakerSteve, terima kasih atas catatan Anda, menambahkan pemeriksaan kesalahan ke jawaban saya. Untuk notifikasi yang tidak dapat diandalkan - Saya menemukan masalah itu sendiri saat mengimplementasikan tampilan pemain yang berulang dan dapat digunakan kembali dalam tampilan koleksi, dan KVO membuat segalanya lebih jelas, karena memungkinkan untuk membuatnya lebih lokal tanpa notifikasi pemain yang berbeda saling mengganggu.
maxkonovalov
17

Untuk Swift :

AVPlayer :

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

Pembaruan :
player.rate > 0kondisi berubah menjadi player.rate != 0karena jika video diputar terbalik dapat menjadi negatif berkat Julian karena telah menunjukkan.
Catatan : Ini mungkin terlihat sama seperti di atas (jawaban Maz) tetapi di Swift '! Player.error' memberi saya kesalahan kompiler sehingga Anda harus memeriksa kesalahan menggunakan 'player.error == nil' di Swift. (Karena properti kesalahan bukan tipe '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
   }
}
Aks
sumber
2
AVPlayer! =
AVAudioPlayer
2
Peringatan: semua yang disebutkan di atas dalam jawaban yang muncul dua tahun sebelum yang satu ini.
Dan Rosenstark
1
@Yar saya tahu juga upvoted jawaban di atas tetapi ini untuk cepat dan di Swift '! Player.error' tidak berfungsi untuk saya itu hanya diperbolehkan untuk jenis bool jadi saya telah menambahkan jawaban oops upvoted komentar Anda karena kesalahan untuk memberikan balasan menggunakan aplikasi stack overflow ini :)
Aks
Kekhawatiran saya adalah bahwa saya tidak tahu seberapa baik player.error tidak menjadi nol sebenarnya bekerja.
Dan Rosenstark
1
Tidak akan berfungsi saat memutar rekaman video terbalik ( player.rate = -1.0), karena -1.0 kurang dari 0.
Julian F. Weinert
9

Perpanjangan cepat berdasarkan jawaban oleh maz

extension AVPlayer {

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

Versi Swift dari jawaban maxkonovalov adalah ini:

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

dan

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")
            }
        }
    }
}

Terima kasih maxkonovalov!

Tuan Stanev
sumber
Halo Tuan Stanev, terima kasih atas jawaban Anda. Ini tidak berhasil untuk saya (alias tidak mencetak apa pun di konsol?)
Cesare
Tidak masalah, itu berhasil - pemain saya dulu nil! Tapi saya perlu tahu kapan tampilan dimulai (bukan berakhir). Ada cara untuk melakukan itu?
Cesare
3

Saat ini dengan swift 5, cara termudah untuk memeriksa apakah pemutar sedang bermain atau dijeda adalah dengan memeriksa variabel .timeControlStatus .

player.timeControlStatus == .paused
player.timeControlStatus == .playing
azemi
sumber
1

Jawabannya adalah Tujuan C

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
}
iOS Lifee
sumber
0
player.timeControlStatus == AVPlayer.TimeControlStatus.playing
Noda Hikaru
sumber
1
Jawaban yang diberikan ini mungkin benar, tetapi bisa mendapat manfaat dari penjelasannya . Jawaban kode saja tidak dianggap sebagai jawaban "baik". Berikut adalah beberapa pedoman untuk Bagaimana cara menulis jawaban yang baik? . Dari review .
MyNameIsCaleb