使用Avplayer在后台播放视频

9
在我的iPhone应用程序中,我希望在应用进入后台模式时继续播放视频。 我正在使用AVPlayer,但无法找到在后台播放视频的方法。如果有人能帮助我解决这个问题,我将非常感激。谢谢。
6个回答

19

惊讶地说,这是可以实现的,我刚刚做到了。

这种方法支持所有可能性:

  • 用户锁定屏幕;
  • 按下主页按钮;
  • 切换到其他应用程序。

只要你有一个运行AVPlayer的实例,iOS就会防止设备自动锁定。

首先,您需要从Info.plist文件配置应用程序以支持音频后台,在UIBackgroundModes数组中添加audio元素。

然后将以下方法放入AppDelegate.m中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions:

这些方法

[[AVAudioSession sharedInstance] setDelegate: self];    
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

并且 #import <AVFoundation/AVFoundation.h>

然后在控制AVPlayer的视图控制器中

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [mPlayer pause];    
    [super viewWillDisappear:animated];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

然后回复

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    switch (event.subtype) {
        case UIEventSubtypeRemoteControlTogglePlayPause:
            if([mPlayer rate] == 0){
                [mPlayer play];
            } else {
                [mPlayer pause];
            }
            break;
        case UIEventSubtypeRemoteControlPlay:
            [mPlayer play];
            break;
        case UIEventSubtypeRemoteControlPause:
            [mPlayer pause];
            break;
        default:
            break;
    }
}

如果用户按下主页按钮(此时视频会淡出暂停),需要另一个技巧来恢复视频的播放。

当您控制视频的播放(我有play:和pause:方法)时,请设置

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];

以及对应的被调用方法,将启动一个计时器并恢复播放。

- (void)applicationDidEnterBackground:(NSNotification *)notification
{
    [mPlayer performSelector:@selector(play) withObject:nil afterDelay:0.01];
}

好的,谢谢。让我检查一下,如果可以的话,我会告诉你的。 - S S
实际上,YouTube 应用可以在后台模式下播放视频(通过 AirPlay)。这太棒了。 - chings228
谢谢你的代码!我已经在我的项目中设置好了,它可以使用Home按钮和切换到另一个应用程序,并且可以正常工作,但是锁屏会停止视频。然而,我发现当我注释掉AVPlayerLayer部分时,它可以在锁屏时播放。显然,我需要PlayerLayer才能真正看到视频。有没有办法让AVPlayerLayer跟随AVPlayer的流程,不会在锁定手机时停止播放? - Nerrolken
1
@AlexanderWinn 这有点晚了,但 AVPlayerLayer 不应该随波逐流哈哈。看看苹果文档:https://developer.apple.com/library/ios/qa/qa1668/_index.html 具体来说,“如果 AVPlayer 的当前项目正在设备的显示器上显示视频,则在将应用程序发送到后台时自动暂停 AVPlayer 的播放。有两种方法可以防止此暂停:[...]” - user2734823
这非常有帮助!请注意,在iOS 8中,您将不需要设置委托,因为它已被弃用。 - Anconia
似乎在iOS 11上不再起作用了。有人有什么想法吗? - gutenbergn

1

在您的 applicationDidEnterBackground 中尝试此代码:

UIApplication *app = [UIApplication sharedApplication]; 
bgTask = 0;

backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(backgroundTask) userInfo:nil repeats:YES];

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ 
[app endBackgroundTask:bgTask];

我在堆栈某处找到了它 对我有用
此外,请查看本教程,其中包括后台模式,包括背景音频.. http://www.raywenderlich.com/29948/backgrounding-for-ios

0
在viewDidLoad中将AVAudioSession类别设置为.playback模式。
let audioSession = AVAudioSession.sharedInstance()
do {
     try audioSession.setCategory(.playback, mode: .moviePlayback, options: [])
} catch {
     print("Failed to set audio session category.")
}

添加 NotificationObservers
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)

//App enter in forground.
@objc func applicationWillEnterForeground(_ notification: Notification) {
    // Reconnect the AVPlayer to the presentation when returning to the foreground

    // If presenting video with AVPlayerViewController
    playerViewController.player = player

    // If presenting video with AVPlayerLayer
    playerLayer.player = player
}

//App enter in foreground.
@objc func applicationDidEnterBackground(_ notification: Notification) {
    // Disconnect the AVPlayer from the presentation when entering background

    // If presenting video with AVPlayerViewController
    playerViewController.player = nil

    // If presenting video with AVPlayerLayer
    playerLayer.player = nil
}

参考资料

在后台播放视频资源中的音频,

AVAudioSession


0

当应用程序在后台运行时,您可以执行此后台任务:

1> 音频
2> 位置
3> VoIP
4> 新闻亭内容
5> 外部配件
6> 蓝牙中心
7> 蓝牙外设
请参阅此链接,

http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
http://www.rideitdown.com/2012/10/how-to-listen-youtube-videos-in.html

这也许可以帮到你: AVPlayer 在后台播放视频


mits:谢谢回复。 我查看了你给出的链接http://stackoverflow.com/questions/10478622/avplayer-play-video-in-background,但在阅读评论后,似乎视频无法在后台工作。 - S S

0

以上的答案在应用程序进入后台时会暂停视频一秒钟,但是通过使用下面提到的方法,可以使视频在应用程序进入后台时继续播放而没有任何故障。

如果AVPlayer的当前项目正在设备的显示器上显示视频,则当应用程序被发送到后台时,AVPlayer的播放将自动暂停。有两种方法可以防止此暂停:

  1. 禁用播放器项中的视频轨道(仅适用于基于文件的内容)。
  2. 从其关联的AVPlayer中删除AVPlayerLayer(将AVPlayerLayer player属性设置为nil)。

参考资料

iOS上使用AV Foundation在后台播放媒体


0

您可以通过使用下面代码集来实现您的要求。

Swift: 4.2

只需创建UIViewController的子类即可。

import UIKit
import AVFoundation

class VideoPlayer:UIViewController{
    var avPlayer: AVPlayer!
    var avPlayerLayer: AVPlayerLayer!
    var paused: Bool = false

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        view.backgroundColor = .clear
        avPlayer?.play()
        paused = false
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let theURL = Bundle.main.url(forResource:"VIDEO_NAME", withExtension: "VIDEO_TYPE") //VIDEO_TYPE -> MP4

        avPlayer = AVPlayer(url: theURL!)
        avPlayerLayer = AVPlayerLayer(player: avPlayer)
        avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        avPlayer.volume = 0
        avPlayer.actionAtItemEnd = .none

        avPlayerLayer.frame = view.layer.bounds
        view.backgroundColor = .clear
        view.layer.insertSublayer(avPlayerLayer, at: 0)

        addPlayerNotifications()
    }

    deinit {
        removePlayerNotifations()
    }

    func addPlayerNotifications() {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(playerItemDidReachEnd(notification:)),
                                               name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                               object: avPlayer.currentItem)
        NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
    }

    func removePlayerNotifations() {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
        NotificationCenter.default.removeObserver(self, name:UIApplication.willEnterForegroundNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name:UIApplication.didEnterBackgroundNotification, object: nil)
    }

    @objc func playerItemDidReachEnd(notification: Notification) {
        let p: AVPlayerItem = notification.object as! AVPlayerItem
        p.seek(to: CMTime.zero)
    }


    //App enter in forground.
    @objc func applicationWillEnterForeground(_ notification: Notification) {
        paused = false
        avPlayer?.play()
    }

    //App enter in forground.
    @objc func applicationDidEnterBackground(_ notification: Notification) {
        paused = true
        avPlayer?.pause()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        avPlayer.pause()
        paused = true
    }
}

现在,几乎完成了。只需使用如下子类创建UIViewcontroller类即可。

class Classname: VideoPlayer{
   override func viewDidLoad() {
        super.viewDidLoad()
    }
}

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