14

我有一个 AVPlayer 类,所有设置都可以流式传输音频文件。有点长,所以我不能在这里发布全部内容。我坚持的是如何允许用户在他们听完一次后重播音频文件。当它第一次完成时,我正确地收到了通知AVPlayerItemDidPlayToEndTimeNotification。当我去重播时,我立即收到相同的通知,这阻止了我重播。

我怎样才能重置它,这样AVPlayerItem它就不会认为它已经播放了音频文件?我可以取消分配所有内容并重新设置,但我相信这会迫使用户再次下载音频文件,这是毫无意义且缓慢的。

以下是我认为相关的课程的一些部分。尝试重播文件时得到的输出如下所示。前两行正是我所期望的,但第三行是一个惊喜。

正在播放
无计时器
音频播放器已完成播放音频

- (id) initWithURL : (NSString *) urlString
{
    self = [super init];
    if (self) {
        self.isPlaying = NO;
        self.verbose = YES;

        if (self.verbose) NSLog(@"url: %@", urlString);
        NSURL *url = [NSURL URLWithString:urlString];

        self.playerItem = [AVPlayerItem playerItemWithURL:url];
        self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];

        [self determineAudioPlayTime : self.playerItem];

        self.lengthOfAudioInSeconds = @0.0f;

        [self.player addObserver:self forKeyPath:@"status" options:0 context:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
    }

    return self;
}

// this is what gets called when the user clicks the play button after they have 
// listened to the file and the AVPlayerItemDidPlayToEndTimeNotification has been received
- (void) playAgain {
    [self.playerItem seekToTime:kCMTimeZero];
    [self toggleState];
}

- (void) toggleState {
    self.isPlaying = !self.isPlaying;

    if (self.isPlaying) {
        if (self.verbose) NSLog(@"is playing");
        [self.player play];

        if (!timer) {
            NSLog(@"no timer");
            CMTime audioTimer = CMTimeMake(0, 1);
            [self.player seekToTime:audioTimer];

            timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                     target:self
                                                   selector:@selector(updateProgress)
                                                   userInfo:nil
                                                    repeats:YES];
        }

    } else {
        if (self.verbose) NSLog(@"paused");
        [self.player pause];
    }
}

-(void)itemDidFinishPlaying:(NSNotification *) notification {
    if (self.verbose) NSLog(@"audio player has finished playing audio");
    [[NSNotificationCenter defaultCenter] postNotificationName:@"audioFinished" object:self];
    [timer invalidate];
    timer = nil;
    self.totalSecondsPlayed = [NSNumber numberWithInt:0];
    self.isPlaying = NO;
}
4

2 回答 2

26

当您的播放器收到AVPlayerItemDidPlayToEndTimeNotification时,您可以调用 seekToTime 方法

func itemDidFinishPlaying() {
    self.player.seek(to: CMTime.zero)
    self.player.play()
}
于 2015-09-16T06:18:15.480 回答
5

Apple 建议AVQueueplayer使用AVPlayerLooper.

这是 Apple 的(稍作修改)示例代码:

AVQueuePlayer *queuePlayer = [[AVQueuePlayer alloc] init];    

AVAsset *asset = // AVAsset with its 'duration' property value loaded
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];

 // Create a new player looper with the queue player and template item
self.playerLooper = [AVPlayerLooper playerLooperWithPlayer:queuePlayer
                                              templateItem:playerItem];

 // Begin looping playback
[queuePlayer play];

为您完成所有事件的AVPlayerLooper监听和播放,队列播放器用于创建他们所谓的“跑步机模式”。这种模式本质上是AVAssetItem在队列播放器中链接相同的多个实例,并将每个完成的资产移回队列的开头。

这种方法的优点是它使框架能够在下一个资产(在这种情况下是相同的资产,但它的开始仍然需要预滚)到达之前进行预滚动,从而减少资产结束和循环开始之间的延迟。

这在 ~15:00 的视频中有更详细的描述:https ://developer.apple.com/videos/play/wwdc2016/503/

于 2017-05-08T14:37:40.933 回答