五.AV Foundation 视频播放 - 标题和字幕

引言

本篇博客主要介绍使用AV Foundation加载视频资源的时候,如何获取视频标题,获取字幕并让其显示到播放界面。

设置标题

资源标题的元数据内容,我们需要从资源的commonMetadata中获取,在加载AVPlayerItem的时候我们已经指定了需要加载commonMetadata数据,所以这里不需要做任何改动,可以在AVPlayerItem的status变为.readyToPlay的时候直接读取标题内容。

添加方法

首先我们需要在PHControlDelegate中添加两个代理方法,分别对应设置视频标题和设置设置字幕标题,并在PHControlView中实现。

protocol PHControlDelegate:NSObjectProtocol {var delegate:PHPlayerDelegate? { get set }/// 开始播放func playstart()/// 设置当前时间////// - Parameters:///   - time: 当前时间///   - duration: 总时间func setCuttentTime(time:TimeInterval,duration:TimeInterval)/// 设置视频标题////// - Parameters:///   - title: 标题func setTitle(title:String)/// 设置字幕标题////// - Parameters:///   - titles: 字幕标题数组func setSubtitle(titles:[String])/// 播放完成func playbackComplete()
}

设置标题代理方法的实现。

// 设置标题func setTitle(title: String) {titleLabel.text = title}



加载commonMetadata数据

首先来看一下创建AVPlayerItem的时候加载commonMetadata的实现。

/// 准备播放private func prepareToPlay() {let keys = ["tracks","duration","commonMetadata"]guard let asset = asset else { return }playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: keys)guard let playerItem = playerItem else { return }player = AVPlayer(playerItem: playerItem)guard let player = player else { return }playerView = PHPlayerView(player: player)self.delegate = playerView?.controlViewself.delegate?.delegate = selfplayerItem.addObserver(self, forKeyPath: status_keypath, context: &playerItemContext)}

获取标题数据

在视频准备好播放后,从commonMetadata中获取我们需要的视频标题数据。

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {if context == &playerItemContext {guard let playerItem = playerItem else { return }guard let player = player else { return }if playerItem.status == .readyToPlay{playerItem.removeObserver(self, forKeyPath: status_keypath)player.play()let duration = playerItem.duration// 同步页面开始播放self.delegate?.playstart()// 同步时间self.delegate?.setCuttentTime(time: 0.0, duration: CMTimeGetSeconds(duration))// 设置标题let assetTitle = assertTitle()self.delegate?.setTitle(title: assetTitle)// 监听播放进度addPlayerItemTimeObserver()}} else {super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)}}

获取标题数据。

/// 获取func assertTitle() -> String {var title = ""guard let asset = asset else { return title }let status = asset.status(of: .commonMetadata)if case .loaded(let metatadaItems) = status {let titleItem = metatadaItems.firstlet itemStatus = titleItem?.status(of: .value)if case .loaded(let value) = itemStatus {if let itemTitle = value as? String {title = itemTitle}}}return title}

再次运行播放器会发现播放器的左上角已经显示出了视频的标题。

设置字幕

AV Foundation为显示字幕提供了非常可靠的方法,AVPlayerLayer会自动渲染这些元数据到页面上,并且还可以手动选择需要显示那种字幕。要完成这个功能需要用到AVMediaSelectionGrop和AVMediaSelectionOption这两个类。
 

AVMediaSelectionOption标识AVAsset中的备用媒体呈现方式。比如备用音频、视频或者文本轨道。想要确定存在哪些备用轨道要用到一个名为availableMediaCharacteristicsWithMediaSelectionOptions属性。它会返回一个包含字符串的数组,这些字符串用于表示保存在资源中可用选项的媒体特征,包含AVMediaCharacteristicVisual(视频),AVMediaCharacteristicAudible(音频)、AVMediaCharacteristicLegible(字幕或隐藏式字幕)。

请求可用媒体特性数据后,调用AVAsset的mediaSelectionGroupForMediaCharacteristic:方法,(iOS16后推荐使用loadMediaSelectionGroup(for mediaCharacteristic: AVMediaCharacteristic) 方法),为其传递要检索的选项的特定媒体特征。这个方法会返回一个AVMediaSelectionGroup,它作为一个或多个互斥的AVMediaSelectionOption实例的容器。

加载availableMediaCharacteristicsWithMediaSelectionOptions数据

下面就在视频准备开始播放的时候进行字幕数据的加载,在这之前呢和加载其它元数据一样需要在创建AVPlayerItem的时候指定所需加载的元数据。

 /// 准备播放private func prepareToPlay() {let keys = ["tracks","duration","commonMetadata","availableMediaCharacteristicsWithMediaSelectionOptions"]guard let asset = asset else { return }playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: keys)guard let playerItem = playerItem else { return }player = AVPlayer(playerItem: playerItem)guard let player = player else { return }playerView = PHPlayerView(player: player)self.delegate = playerView?.controlViewself.delegate?.delegate = selfplayerItem.addObserver(self, forKeyPath: status_keypath, context: &playerItemContext)}

获取字幕数据

在监听到视频状态转换为.readyToPlay时开始调用方法处理字幕数据。

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {if context == &playerItemContext {guard let playerItem = playerItem else { return }guard let player = player else { return }if playerItem.status == .readyToPlay{playerItem.removeObserver(self, forKeyPath: status_keypath)player.play()let duration = playerItem.duration// 同步页面开始播放self.delegate?.playstart()// 同步时间self.delegate?.setCuttentTime(time: 0.0, duration: CMTimeGetSeconds(duration))// 设置标题let assetTitle = assertTitle()self.delegate?.setTitle(title: assetTitle)// 设置字幕let subtitles = loadMediaOptions()self.delegate?.setSubtitle(titles: subtitles)// 监听播放进度addPlayerItemTimeObserver()}} else {super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)}}

加载字幕方法实现。

/// 加载字幕func loadMediaOptions() -> [String] {var subtitles = [String]()guard let asset = asset else { return subtitles }let mc = AVMediaCharacteristic.legibleasset.loadMediaSelectionGroup(for: mc) {[weak self] selectionGroup, error inguard let self = self, let selectionGroup = selectionGroup else { return }// 获取字幕选项for option in selectionGroup.options {let displayName = option.displayNamesubtitles.append(displayName)}}return  subtitles}

字幕选择页面实现

获取到字幕数据后,我们借助delegate将其专递到了PHControlView中供使用,目前在控制页面还没有选择字幕的按钮,先来把它添加到播放器的右上角,并实现点击事件。

实现代理方法

// 设置字幕标题func setSubtitle(titles: [String]) {subtitles = titles}

实现点击事件。

// 显示字幕列表@objc func subtitleOnclick() {guard let subtitles = subtitles else { return }guard let delegate = delegate else { return }let alertViewController = UIAlertController(title: "选择字幕", message: nil, preferredStyle: .actionSheet)for subtitle in subtitles {let action = UIAlertAction(title: subtitle, style: .default) { action indelegate.selectedSubtitle(subtitle: subtitle)}alertViewController.addAction(action)}let cancel = UIAlertAction(title: "取消字幕", style: .cancel)alertViewController.addAction(cancel)self.window?.rootViewController?.present(alertViewController, animated: true)}

显示字幕

在播放控制器内,接收选择的字幕信息开始设置字幕。

/// 指定字幕////// - Parameters:///   - subtitle: 字幕名称func selectedSubtitle(subtitle: String) {guard let asset = asset else { return }guard let playerItem = playerItem else { return }let mc = AVMediaCharacteristic.legibleasset.loadMediaSelectionGroup(for: mc) {[weak self] selectionGroup, error inguard let self = self, let selectionGroup = selectionGroup else { return }var selected = false// 获取字幕选项for option in selectionGroup.options {if option.displayName == subtitle {playerItem.select(option, in: selectionGroup)selected = truebreak}}if selected == false {playerItem.select(nil, in: selectionGroup)}}}

结语

显示标题和字幕的功能就完成了,主要就是读取AVAsset资源中的元数据并同步到视频播放的过程中,播放器的整体用户体验有了进一步的提升。到目前为止我们在处理的都是播放过程中是逻辑,但关于视频播放结束的处理也同样重要,下一篇博客就专门来介绍关于视频资源播放结束逻辑的实现。

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/702716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sentinel微服务流量治理组件实战上

目录 分布式系统遇到的问题 解决方案 Sentinel 是什么? Sentinel 工作原理 Sentinel 功能和设计理念 流量控制 熔断降级 Sentinel工作主流程 Sentinel快速开始 Sentinel资源保护的方式 基于API实现 SentinelResource注解实现 Spring Cloud Alibaba整合…

介绍 PIL+IPython.display+mtcnn for 音视频读取、标注

1. nn.NLLLoss是如何计算误差的? nn.NLLLoss是负对数似然损失函数,用于多分类问题中。它的计算方式如下:首先,对于每个样本,我们需要将其预测结果通过softmax函数转换为概率分布。softmax函数可以将一个向量映射为一个概率分布&…

第四节:Vben Admin登录对接后端getUserInfo接口

系列文章目录 第一节:Vben Admin介绍和初次运行 第二节:Vben Admin 登录逻辑梳理和对接后端准备 第三节:Vben Admin登录对接后端login接口 第四节:Vben Admin登录对接后端getUserInfo接口 文章目录 系列文章目录前言一、回顾Vben…

RK3568平台 阻塞IO和非阻塞IO

一.IO 模型的分类 IO 模型根据实现的功能可以划分为为阻塞 IO、非阻塞 IO、信号驱动IO,IO多路复用和异步 IO。根据等待 IO 的执行结果进行划分,前四个 IO 模型又被称为同步IO. 同步IO与异步IO: 以现实生活去餐馆吃饭为例,根据菜…

Linux——缓冲区封装系统文件操作

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、FILE二、封装系统接口实现文件操作1、text.c2、mystdio.c3、mystdio.h 一、FILE 因为IO相…

Typora结合PicGo + 使用Github搭建个人免费图床

文章目录 一、国内图床比较二、使用Github搭建图床三、PicGo整合Github图床1、下载并安装PicGo2、设置图床3、整合jsDelivr具体配置介绍 4、测试5、附录 四、Typora整合PicGo实现自动上传 每次写博客时,我都会习惯在Typora写好,然后再复制粘贴到对应的网…

基于springboot+vue的校园社团信息管理系统(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

自定义搭建管理系统

最近使用自己搭建的脚手架写了一个简易管理系统,使用webpackreactantd,搭建脚手架参考: 使用Webpack5搭建项目(react篇)_babel-preset-react-app-CSDN博客 搭建的思路: 1. 基建布局,使用antd的…

代码随想录算法训练营第二十五天 | 216.组合总和III,17.电话号码的字母组合 [回溯篇]

代码随想录算法训练营第二十五天 LeetCode 216.组合总和III题目描述思路参考代码总结 LeetCode 17.电话号码的字母组合题目描述思路参考代码 LeetCode 216.组合总和III 题目链接:216.组合总和III 文章讲解:代码随想录#216.组合总和III 视频讲解&#xff…

Java零基础 - 字符串连接运算符

哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…

acwing算法学习笔记 ------ 双链表

1、定义 这里可以做一个投机取巧,我们不再像单链表去用head去存头和尾,直接让r[0] 1,l[1] 0; idx 2.进行初始化, 解释一下l[N] 和 r[N] l[N]:是表示指向左面下一个节点下标, r[N]:表示指向下一个节点的下标。大家不用担心i…

皓学IT:WEB03_MySQL

今日内容介绍 Mysql数据库 SQL语句 一、数据库 1.1. 数据库概述 什么是数据库 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及…

南京观海微电子---AXI总线技术简介——ZYNQ PS和PL的互联技术

1.AXI总线介绍 AXI全称Advanced Extensible Interface,是Xilinx从6系列的FPGA开始引入的一个接口协议,主要描述了主设备和从设备之间的数据传输方式。AXI协议在Xilinx的ZYNQ系列芯片中继续使用,协议版本是AXI4。 ZYNQ为Xilinx推出的首款将高…

解决vulhub漏洞环境下载慢卡死问题即解决docker-valhub漏洞环境下载慢的问题

解决vulhub环境下载慢/卡 当前环境为:ubuntu20 1.在 cd /etc/docker/目录下创建或修改daemon.json文件 sudo touch daemon.json编辑daemon.json文件 sudo vim daemon.json2.填写阿里云镜像地址: { "registry-mirrors":["https://6kx…

11-pytorch-使用自己的数据集测试

b站小土堆pytorch教程学习笔记 import torch import torchvision from PIL import Image from torch import nnimg_path ../imgs/dog.png imageImage.open(img_path) print(image) # imageimage.convert(RGB)transformtorchvision.transforms.Compose([torchvision.transforms.…

运维SRE-15 自动化批量管理-ansible1

## 1.什么是自动化批量管理重复性工作与内容: 思考如何自动化完成. 部署环境,批量查看信息,批量检查:自动化 一般步骤:1.如何手动实现2.如何自动化管理工具,批量实现3.注意事项:想要自动化一定要先标准化(所有环境,软件,目录一致)…

复制策略深入探讨

在之前的博客中,我们讨论了复制最佳实践和不同类型的复制,例如批量、站点和存储桶。但是,随着所有这些不同类型的复制类型的出现,人们不得不想知道在哪里使用哪种复制策略?从现有 S3 兼容数据存储迁移数据时&#xff0…

C语言推荐书籍

本书详细讲解了C语言的基本概念和编程技巧。全书共17章。第1章、第2章介绍了C语言编程的预备知识。第3章~第15章详细讲解了C语言的相关知识,包括数据类型、格式化输入/输出、运算符、表达式、语句、循环、字符输入和输出、函数、数组和指针、字符和字符串…

WPF 【十月的寒流】学习笔记(1):DataGrid过滤

文章目录 相关链接代码仓库前言环境DataGrid 数据筛选项目配置使用原理主要代码(详细代码可以看我的GitHub仓库)Models.PersonDataGirdViewDataGridViewModel 实现效果 总结 相关链接 十月的寒流 在 WPF 中制作 DataGrid 的数据筛选功能 WPF 中如何制作 …

2024年上半年第一次课

文章目录 一、加入课程QQ群(一)加入QQ群(二)加群要求 二、加入超星学习通(一)安装超星学习通(二)利用学习通签到(三)查看课程内容(四)…