当前位置: 首页 > news >正文

(六)深入了解AVFoundation-播放:AirPlay、画中画后台播放

(一)深入了解AVFoundation:框架概述与核心模块解析-CSDN博客

(二) 深入了解AVFoundation - 播放:AVFoundation 播放基础入门-CSDN博客

(三)深入了解AVFoundation-播放:AVPlayer 进阶 播放状态 & 进度监听全解析_监听 avplayer 播放的进度,结束,等状态变化-CSDN博客

(四)深入理解AVFoundation-播放:高度自定义视频播放器 UI-CSDN博客

(五)深入了解AVFoundation-播放:多音轨、字幕、倍速播放与横竖屏切换-CSDN博客

(六)深入了解AVFoundation-播放:AirPlay、画中画后台播放-CSDN博客

引言

在我们之前的博客中,我们已经详细探讨了 AVPlayer 的基础播放功能,包括播放、暂停、进度拖拽、播放速度调整,以及字幕和音轨切换等常见需求。这些基本功能已经能够满足大多数视频播放场景的需求,但为了进一步提升用户体验,iOS 还提供了一些更为高级的播放功能。在本篇博客中,我们将深入探讨两项 AVPlayer 的进阶功能:AirPlay 投屏播放以及画中画(PiP)模式支持。

这些功能不仅可以让用户在使用过程中获得更流畅、便捷的体验,还能极大提升应用的互动性和可用性。接下来,我们将逐一解析这些高级功能的实现及其应用场景。

这也是AVFoundation 在播放篇章的最后一篇了。

AirPlay 投屏播放

AirPlay 是 Apple 提供的无线流媒体技术,支持音视频投送。iOS 的 AVPlayer 默认是支持 AirPlay 的,我们只需要做一些简单的配置和 UI 接入即可使用。

我们可以在自己的播放器界面中添加一个小的 AirPlay 投屏按钮,用户可以通过点击它来选择目标设备。

    /// 投屏按钮
    private let routePickerView = AVRoutePickerView()
        // 投屏按钮
        routePickerView.activeTintColor = .blue
        routePickerView.tintColor = .gray
        self.addSubview(routePickerView)
        // 投屏按钮
        routePickerView.snp.makeConstraints { make in
            make.trailing.equalToSuperview().inset(16.0)
            make.centerY.equalTo(backButton)
            make.width.height.equalTo(40.0)
        }

点击它就能弹出设备选择列表,选择后就可以将播放的内容投放到 Apple TV 或其他 AirPlay 设备。

如果我们希望监听用户是否正在进行投屏,可以通过监听 AVPlayer 的 externalPlaybackActive属性。

player.addObserver(self, forKeyPath: "externalPlaybackActive", options: [.new], context: nil)

override func observeValue(forKeyPath keyPath: String?, ... ) {
    if keyPath == "externalPlaybackActive" {
        if player.isExternalPlaybackActive {
            print("🎥 正在通过 AirPlay 投屏中")
        } else {
            print("📱 返回本地播放")
        }
    }
}

画中画后台播放

对于视频或音频应用,很多时候用户并不希望在他们切换到其他应用或锁屏时,播放会被中断。启用后台播放,可以让用户在进行其他操作时,依然享受无缝的媒体体验。

为了实现后台播放,iOS 提供了 AVAudioSession 和一些设置,使得我们的应用能够在后台继续播放音频。这里的关键是配置 Audio Session 和 请求后台播放权限。

配置AVAudioSession

AVAudioSession 用于管理应用的音频行为,确保在后台时能够继续播放音频或视频。在后台播放时,我们需要设置合适的音频会话类别。该方法我们可以在AppDelegate中进行调用。

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        setupAVAudioSession()
        return true
    }
    
    /// 配置音频会话模式
    func setupAVAudioSession() {
        do {
            let session = AVAudioSession.sharedInstance()
            
            // 设置后台播放类别
            try session.setCategory(.playback, mode: .moviePlayback, options: [.duckOthers])
            
            // 激活会话
            try session.setActive(true)
            
            print("Audio session configured for background playback.")
        } catch {
            print("Error configuring audio session: \(error.localizedDescription)")
        }
    }
  1. setCategory(.playback):这个类别允许应用在后台继续播放音频。
  2. setMode(.moviePlayback):当播放视频时,推荐使用 moviePlayback 模式。
  3. duckOthers:这个选项会让其他应用的音量降低,但不会完全停止,以便于视频或音频播放不会被其他声音打断。

修改 Info.plist 请求后台播放权限

除了在代码中配置 AVAudioSession,你还需要在 Info.plist 中声明后台播放权限。

在 Info.plist 中添加:

<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
</array>

这告诉系统你需要在后台播放音频或视频,如果没有这个配置,后台播放会被禁止。

实现画中画(PiP)模式

画中画(PiP)是一种允许用户在视频播放时将视频缩小并设置在屏幕的任意位置的功能。但它的实现需要一定的条件,首先设备需要支持 PiP,iOS 版本需要是 iOS14 及以上。

配置画中画

首先使用 AVPictureInPictureController 进行画中画配置。

    /// 画中画控制器
    private var pipController: AVPictureInPictureController?


    /// 配置画中画
    private func setupPictureInPicture() {
        if #available(iOS 14.0, *) {
            if AVPictureInPictureController.isPictureInPictureSupported() {
                guard let playerLayer = playerView.layer as? AVPlayerLayer else { return }
                let pipController = AVPictureInPictureController(playerLayer: playerLayer)
                pipController?.delegate = self
                self.pipController = pipController
            }
        }
    }
  1. 将播放器的 AVPlayeLayer 传递给 AVPictureInPictureController 并设置代理。
  2. 强持有 AVPictureInPictureController ,保证 AVPictureInPictureController 的实例不会在执行完配置就被释放。

实现协议、定义方法

实现 AVPictureInPictureControllerDelegate 的协议方法,并定义开启画中画和结束画中画的两个方法。

extension PHPlayerViewController: AVPictureInPictureControllerDelegate {
    
    func startPictureInPicture() {
        guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
        pipController?.startPictureInPicture()
    }
    
    func stopPictureInPicture() {
        guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
        pipController?.stopPictureInPicture()
    }
    
    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
        print("画中画失败:\(error.localizedDescription)")
    }
    
    func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
        print("画中画开始")
    }
    
    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
        print("画中画结束")
    }
    
}

添加监听

添加应用进入后台状态和进入前台状态的监听,在进入后台状态时调用开启画中画,而进入前台状态时调用停止画中画功能。

    /// 添加通知
    private func addNotification() {
        // 监听进入后台
        NotificationCenter.default.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        // 监听进入前台
        NotificationCenter.default.addObserver(self, selector: #selector(enterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }


    /// 进入后台
    @objc private func enterBackground() {
        print("进入后台")
        // 开始画中画
        startPictureInPicture()
    }
    
    /// 进入前台
    @objc private func enterForeground() {
        print("进入前台")
        // 停止画中画
        stopPictureInPicture()
    }

结语

通过本篇内容,我们深入探讨了 AVPlayer 在播放阶段的两个个高级能力:画中画模式(PiP) 提升了多任务场景下的视频可用性,AirPlay 投屏 让用户能够享受更好的大屏体验,。

这些功能的实现其实并不复杂,但却能显著提升应用的专业度与用户体验。如果说基础播放功能是 AVPlayer 的骨架,那这些进阶能力无疑是它的灵魂所在。希望本文能为你在打造更完整、更出色的视频播放体验上提供帮助。

相关文章:

  • SQLyog 小记
  • 2021第十二届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
  • 从零构建一个全栈AI应用:Next.js + FastAPI + OpenAI API
  • AbortController:让异步操作随时说停就停
  • Linux:37信号lesson26(未完)
  • 模板引擎语法-标签
  • 【C#】线程回调
  • 组件化可编辑表格
  • Oracle for Linux安装和配置(11)——Linux命令
  • 《车辆人机工程-汽车驾驶操纵实验》
  • 嵌入式 C语言 二进制宏
  • SQL JOIN 全解析:跨表查询与实体关系建模
  • Qt实现读取本地文件并导出数据到Excel
  • 【连载2】基础智能体的进展与挑战综述
  • DeepSeek在消防救援领域的应用解决方案
  • 小程序开发指南
  • android display 笔记(十二)CPU,GPU,DPU的区别
  • Java中equals与 “==” 的区别
  • 自动驾驶的未来:多模态感知融合技术最新进展
  • HashMap实现通用的Request和Response及解析非标准JSON
  • 学生做网站的软件/传统营销与网络营销的区别
  • wordpress子目录建站/西安官网seo技术
  • 制作网站入门/新东方培训机构官网
  • 哈尔滨建设网官方网站/网络营销swot分析
  • 广州开发网站建设/中国站免费推广入口
  • 摄影网站在线建设/搜索引擎推广是什么意思