在应用程序重新激活后恢复AVPlayer视频播放

16

我为视频播放编写了AVPlayer的自定义播放器。根据苹果文档,设置视频图层:

    self.player = [IPLPlayer new];
    self.player.playerLayer = (AVPlayerLayer *)self.playerView.layer;

假设 self.playerView 是文档中的一个通常类:

    @implementation PlayerView

+ (Class) layerClass {
    return [AVPlayerLayer class];
}

- (AVPlayer *)player {
    return [(AVPlayerLayer *)[self layer] player];
}

- (void)setPlayer:(AVPlayer *) player {
    [(AVPlayerLayer *) [self layer] setPlayer:player];
}
问题是: 当关闭应用程序(Home按钮)或锁定屏幕时,视频播放会停止,仅音频播放恢复,屏幕上的图像仍然是锁定屏幕之前的 - 它完全静态且不更改帧。
如何在屏幕锁定后恢复视频播放?
似乎我必须注册通知,在应用程序变为活动状态后恢复视频层:
    -(void)registerNotification
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(willEnterBackground)
                                                 name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(didEnterForeground)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];
}

-(void)unregisterNotification
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


-(void)willEnterBackground
{
    NSLog(@"willEnterBackground");
    [self.playerView willEnterBackground];
}

-(void)didEnterForeground
{
    NSLog(@"didEnterForeground");
    [self.playerView didEnterForeground];
}
5个回答

19

同时,还有一个解决方案将所有这些信息绑定在一起。

也许应该以不同方式处理播放器状态,但我喜欢递归的方式。

注意:如果您不需要精确的查找时间,可以使用 [_player seekToTime:<#(CMTime)#> completionHandler:<#^(BOOL finished)completionHandler#>],它更快,但会查找最接近的关键帧。

- (void)viewDidLoad
{
    [super viewDidLoad];

....

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}

-(void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}

....

-(void) appEnteredForeground {
    AVPlayerLayer *player = (AVPlayerLayer *)[playerView layer];
    [player setPlayer:NULL];
    [player setPlayer:_player];
    [self playAt:currentTime];
}

-(void) appEnteredBackground {
    [_player pause];
    currentTime = [_player currentTime];
}

-(void)playAt: (CMTime)time {
    if(_player.status == AVPlayerStatusReadyToPlay && _player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        [_player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
            [_player play];
        }];
    } else {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self playAt:time];
        });
    }
}

8

这对我在 Swift 3 上起作用。

在设置视图时添加以下内容:

NotificationCenter.default.addObserver(self, 
  selector: #selector(appWillEnterForegroundNotification),
  name: .UIApplicationWillEnterForeground, object: nil)

抓住你的播放器并强制播放:
func appWillEnterForegroundNotification() {
  myPlayer.play()
}

不要忘记在不需要观察者时将它们移除:

override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
  NotificationCenter.default.removeObserver(self)
}

1
嗯,我非常确定这会导致包含它的类保留+1。因此,'deinit'将永远不会被调用。您需要从其他地方删除观察者,以便对象得到释放:] - Andres Canella
1
是的,viewDidDisappear 是移除观察者的好地方。 - Harish
我已经更新了答案。谢谢 @AndresCanella 和 Harish <3 - budiDino
@AndresCanella,您能否详细说明为什么这会创建额外的retain?NSNotificationCenter不会保留观察者。 - iur
@iur 在发布此帖子时,在实践中,它是保留的。似乎在某个时候它被强引用了。在dealloc中使用removeObserver导致包含对象永远不会释放,因为dealloc从未被调用。将其移动到viewWillDisappear中解决了这个问题。我自那以后没有测试过它。 - Andres Canella

3

同时使用UIApplicationWillEnterForegroundNotification。这样,您就知道您的应用程序将是活动的,并且对用户可见:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(appEnteredForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];

2
抱歉,我的问题不是关于这个的。 - HotJard

3

技巧在于当应用程序进入后台时,将所有视频层从其播放器中分离出来,并在应用程序再次变为活动状态时重新连接它们。

因此,在您的 -applicationDidEnterBackground: 中,您需要触发一个机制,以实现此目的。

avPlayerLayer.player = nil;

在你的-applicationDidBecomeActive:方法中,你需要重新连接播放器,例如:
avPlayerLayer.player = self.avPlayer;

此外,您还可以参考苹果公司的这篇技术说明(QA1668)。

不需要这样做,也许已经修复了? - thibaut noah

1

经过一些研究,我发现在iOS播放器(WebBrowser,MPMoviePlayerController)中有相同的包。可能是因为内容的分发类型是渐进式下载。

所以解决方案是:

  • 使用下面的通知并保存当前时间。
  • 应用程序恢复后,重新创建AVPlayer并从保存的时间开始播放。

在所有其他情况下,图像都会被阻止,或视图变黑。


这应该是被接受的答案。 此外,如果您在通知上实现行为,请注意您不能将player对象传递给UIApplication通知,如果您的通知从未被调用,则是罪魁祸首。 - thibaut noah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接