Kiểm tra trạng thái chơi của AVPlayer


Câu trả lời:


57

Để nhận thông báo về việc hết mặt hàng (thông qua Apple ):

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

Và để theo dõi quá trình chơi, bạn có thể:

"theo dõi các thay đổi về vị trí của đầu phát trong đối tượng AVPlayer" bằng cách sử dụng addPeriodicTimeObserverForInterval: queue: usingBlock: hoặc addBoundaryTimeObserverForTimes: queue: usingBlock : .

Ví dụ là của 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];
}];

Bạn có thể ném cờ Boolean để kiểm tra trạng thái chơi.
moonman239

Thông báo có thể gây ra sự cố nếu bạn thay đổi vật phẩm của người chơi, chẳng hạn như với -replaceCurrentItemWithPlayerItem:và không xử lý đúng cách. Một cách đáng tin cậy hơn đối với tôi là theo dõi AVPlayertrạng thái của bằng KVO. Xem câu trả lời của tôi bên dưới để biết thêm chi tiết: stackoverflow.com/a/34321993/3160561
maxkonovalov

2
Cũng cần thông báo lỗi? AVPlayerItemFailedToPlayToEndTimeNotification
ToolmakerSteve

332

Bạn có thể biết nó đang phát bằng cách sử dụng:

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

Phần mở rộng Swift 3:

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

29
Không nhất thiết, nó không xử lý các tình huống trong đó trình phát bị dừng do lỗi trong tệp. Một điều gì đó tôi đã tìm ra một cách khó khăn ...
Dermot

7
Như Dermot đã nói, nếu bạn cố gắng chơi thứ gì đó khi ở chế độ trên máy bay, tỷ lệ AVPlayer vẫn được đặt thành 1,0, vì nó ngụ ý ý định chơi.
phi

4
Các AVPlayer có một đặc tính lỗi, chỉ cần kiểm tra rằng nó không phải là con số không cũng như kiểm tra tốc độ không phải là 0 :)
James Campbell

23
Lưu ý rằng câu trả lời đã được cập nhật để ngăn chặn trường hợp @Dermot. Vì vậy, cái này thực sự hoạt động.
jomafer

2
Sẽ không được làm việc khi chơi đoạn băng video ngược ( - [AVPlayer setRate:-1.0]), vì -1.0nhỏ hơn 0.
Julian F. Weinert

49

Trong iOS10, có một thuộc tính tích hợp sẵn cho điều này ngay bây giờ: timeControlStatus

Ví dụ: chức năng này phát hoặc tạm dừng avPlayer dựa trên trạng thái của nó và cập nhật nút phát / tạm dừng một cách thích hợp.

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

Đối với câu hỏi thứ hai của bạn, để biết avPlayer đã đến cuối hay chưa, việc đơn giản nhất bạn có thể làm là thiết lập thông báo.

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

Ví dụ: khi nó đi đến cuối, bạn có thể tua lại đến đầu video và đặt lại nút Tạm dừng để Phát.

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

Những ví dụ này hữu ích nếu bạn đang tạo các điều khiển của riêng mình, nhưng nếu bạn sử dụng AVPlayerViewController, thì các điều khiển được tích hợp sẵn.


Dễ sử dụng cho iOS 10+
technerd

2
@objc func didPlayToEnd () cho Swift 4.2
Sean Stayns vào

21

rateKHÔNG cách để kiểm tra xem một đoạn video được chơi (nó có thể bị đình trệ). Từ tài liệu của rate:

Cho biết tốc độ phát lại mong muốn; 0,0 có nghĩa là "tạm dừng", 1,0 cho biết mong muốn chơi với tỷ lệ tự nhiên của mục hiện tại.

Từ khóa "mong muốn phát" - tỷ lệ 1.0không có nghĩa là video đang phát.

Giải pháp kể từ iOS 10.0 là sử dụng AVPlayerTimeControlStatuscó thể được quan sát trên AVPlayer timeControlStatustài sản.

Giải pháp trước iOS 10.0 (9.0, 8.0, v.v.) là triển khai giải pháp của riêng bạn. Tỷ lệ 0.0có nghĩa là video bị tạm dừng. Khi rate != 0.0điều đó có nghĩa là video đang phát hoặc bị dừng.

Bạn có thể tìm ra sự khác biệt bằng cách quan sát thời gian của người chơi qua: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

Khối trả về thời gian của người chơi hiện tại CMTime, do đó, so sánh giữa lastTime(thời gian nhận được lần cuối từ khối) và currentTime(thời gian mà khối vừa báo cáo) sẽ cho biết liệu người chơi đang chơi hay bị dừng. Ví dụ, nếu lastTime == currentTimerate != 0.0, thì trình phát đã bị dừng.

Như những người khác đã lưu ý, việc tìm hiểu xem quá trình phát lại đã kết thúc hay chưa được chỉ ra bởi AVPlayerItemDidPlayToEndTimeNotification.


18

Một giải pháp thay thế đáng tin cậy hơn NSNotificationlà thêm bạn làm người quan sát ratetài sản của người chơi .

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

Sau đó, kiểm tra xem giá trị mới cho tỷ lệ quan sát có bằng 0 hay không, có nghĩa là quá trình phát lại đã dừng vì một lý do nào đó, như kết thúc hoặc bị đình trệ do bộ đệm trống.

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

Đối với rate == 0.0trường hợp, để biết chính xác nguyên nhân nào khiến quá trình phát lại bị dừng, bạn có thể thực hiện các bước kiểm tra sau:

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
}

Và đừng quên yêu cầu trình phát của bạn dừng lại khi đến cuối:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;


Tôi nghĩ rằng cũng cần thông báo về việc không đạt được kết thúc - AVPlayerItemFailedToPlayToEndTimeNotification. Nếu lỗi này xảy ra, sẽ không bao giờ đến thời điểm kết thúc. Hoặc đọc câu trả lời của maz, thêm vào mã của bạn một kiểm tra player.error != nil, trong trường hợp phát lại đã "kết thúc" do lỗi.
ToolmakerSteve

BTW, điều gì bạn thấy "không đáng tin cậy" khi sử dụng NSNotification của AVPlayerItemDidPlayToEndTimeNotification?
ToolmakerSteve

ToolmakerSteve, cảm ơn vì ghi chú của bạn, đã thêm kiểm tra lỗi vào câu trả lời của tôi. Đối với các thông báo không đáng tin cậy - tôi đã tự mình gặp vấn đề khi triển khai các chế độ xem người chơi lặp lại và có thể sử dụng lại trong chế độ xem bộ sưu tập và KVO đã làm cho mọi thứ rõ ràng hơn, vì nó cho phép giữ toàn bộ thông báo cục bộ hơn mà không có thông báo của những người chơi khác nhau can thiệp vào nhau.
maxkonovalov

17

Đối với Swift :

AVPlayer :

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

Cập nhật :
player.rate > 0điều kiện được thay đổi thành player.rate != 0vì nếu video đang phát ngược lại, nó có thể là tiêu cực, cảm ơn Julian đã chỉ ra.
Lưu ý : Câu trả lời này có thể giống như câu trả lời ở trên (của Maz) nhưng trong Swift '! Player.error' đã cho tôi lỗi trình biên dịch, vì vậy bạn phải kiểm tra lỗi khi sử dụng 'player.error == nil' trong Swift. (Vì thuộc tính lỗi không thuộc loại '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
xếp hàng

2
Lưu ý: tất cả được đề cập ở trên trong câu trả lời hiển thị hai năm trước câu trả lời này.
Dan Rosenstark

1
@Yar Tôi cũng biết câu trả lời ở trên đã ủng hộ nhưng điều này là dành cho nhanh chóng và trong Swift '! Player.error' không hoạt động với tôi, nó chỉ được phép đối với các loại bool vì vậy tôi đã thêm câu trả lời rất tiếc đã bỏ phiếu bình luận của bạn do nhầm lẫn để trả lời bằng cách sử dụng ứng dụng tràn ngăn xếp này :)
Aks

Mối quan tâm của tôi là tôi không biết trình phát player.error not nil thực sự hoạt động như thế nào.
Dan Rosenstark

1
Sẽ không được làm việc khi chơi đoạn băng video ngược ( player.rate = -1.0), vì -1.0 nhỏ hơn 0.
Julian F. Weinert

9

Tiện ích mở rộng Swift dựa trên câu trả lời của maz

extension AVPlayer {

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

6

Câu trả lời của maxkonovalov trong phiên bản Swift là:

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

Cảm ơn bạn maxkonovalov!


Xin chào ông Stanev, cảm ơn vì câu trả lời của ông. Điều này không làm việc cho tôi (hay còn gọi là không in bất cứ điều gì trong giao diện điều khiển?)
Cesare

Đừng bận tâm, nó hoạt động - máy nghe nhạc của tôi là nil! Nhưng tôi cần biết khi nào chế độ xem bắt đầu (không kết thúc). Bất kỳ cách nào để làm điều đó?
Cesare

3

Hiện tại với swift 5, cách dễ nhất để kiểm tra xem trình phát đang chơi hay bị tạm dừng là kiểm tra biến .timeControlStatus .

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

1

Câu trả lời là Mục tiêu 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
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.