【iOS】视频播放之AVPlayer

article/2025/10/2 10:46:40

【iOS】视频播放之AVPlayer

iOS平台使用播放视频,可用的选项一般有这四个,他们各自的作用和功能如下:

使用环境优点缺点
AVPlayerViewControllerAVKit简单易用不可定制
MPMoviePlayerControllerMediaPlayer简单易用不可定制
IJKPlayerIJKMediaFrameWork定制度高,可支持流媒体播放使用复杂
AVPlayerAVFoundation可定制度高,功能强大不支持流媒体

由此可以看出,如果我们不做直播功能AVPlayer就是一个最优的选择。

另外AVPlayer是一个可以播放任何格式的全功能影音播放器

支持视频格式: MP4,MOV,M4V,3GP,AVI等。

支持音频格式:MP3,AAC,WAV,AMR,M4A等。

详见AVPlayer支持的视频格式

如何使用

AVPlayer存在于AVFoundation框架,我们使用时需要导入:

#import <AVFoundation/AVFoundation.h>

几个播放相关的参数

在创建一个播放器之前我们需要先了解一些播放器相关的类

AVPlayer:控制播放器的播放,暂停,播放速度

AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息。

AVPlayerItem:管理资源对象,提供播放数据源

AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面

我们这片文章就围绕这几个参数展开,光说这些你可能还有点不明白,那我们就围绕一个最简单的播放器做起,一点点扩展功能,在具体讲解这几个参数的作用。

最简单的播放器

根据上面描述,我们知道AVPlayer是播放的必要条件,所以我们可以构建的简单播放器就是:

NSURL*url=[NSURLfileURLWithPath:@"视频地址"];AVPlayerItem*playerItem=[AVPlayerItemplayerItemWithURL:url];self.player=[AVPlayer playerWithPlayerItem:playerItem];
[self.player addObserver:self forKeyPath:@"status"options:NSKeyValueObservingOptionNew context:nil];
self.player.actionAtItemEnd=AVPlayerActionAtItemEndNone;self.playerLayer=[AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.videoGravity=AVLayerVideoGravityResizeAspect;
self.playerLayer.frame=self.view.bounds;
[self.view.layer addSublayer:self.playerLayer];
[self.player play];

为完成这些,我们需要对AVPlayerItem和AVPlayerLayer进一步了解一下。

一、AVPlayer的控制

前面讲过该类是控制视频播放行为的,他的使用比较简单。

播放视频:

[self.player play];

暂停视频:

[self.player pause];

更改速度:

self.player.rate =1.5;//注意更改播放速度要在视频开始播放之后才会生效

还有一下其他的控制,我们可以调转到系统API进行查看

二、AVPlayerItem的控制

AVPlayerItem作为资源管理对象,它控制着视频从创建到销毁的诸多状态。

1. 播放状态 status

typedef NS_ENUM(NSInteger,AVPlayerItemStatus) {AVPlayerItemStatusUnknown,//未知AVPlayerItemStatusReadyToPlay,//准备播放AVPlayerItemStatusFailed//播放失败};

我们使用KVO监测playItem.status,可以获取播放状态的变化

[self.playerItem addObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];

在监听回调中:

- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context{if([object isKindOfClass:[AVPlayerItemclass]]) {if([keyPath isEqualToString:@"status"]) {switch(_playerItem.status) {caseAVPlayerItemStatusReadyToPlay://推荐将视频播放放这里                                                                                     [self play];break;caseAVPlayerItemStatusUnknown:NSLog(@"AVPlayerItemStatusUnknown");break;caseAVPlayerItemStatusFailed:NSLog(@"AVPlayerItemStatusFailed");break;default:break;       }           }}

虽然设置完播放配置我们可以直接调用[self.player play];进行播放,但是更稳妥的方法是在回调收到AVPlayerItemStatusReadyToPlay时进行播放

2. 视频的时间信息

在AVPlayer中时间的表示有一个专门的结构体CMTime

typedef struct{CMTimeValuevalue;// 帧数CMTimeScaletimescale;// 帧率(影片每秒有几帧)CMTimeFlagsflags;CMTimeEpochepoch;    }CMTime;

CMTime是以分数的形式表示时间,value表示分子,timescale表示分母,flags是位掩码,表示时间的指定状态。

获取当前播放时间,可以用value/timescale的方式:

floatcurrentTime =self.playItem.currentTime.value/item.currentTime.timescale;

还有一种利用系统提供的方法,我们用它获取视频总时间:

floattotalTime  =CMTimeGetSeconds(item.duration);

如果我们想要添加一个计时的标签不断更新当前的播放进度,有一个系统的方法:

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullabledispatch_queue_t)queue usingBlock:(void(^)(CMTimetime))block;

方法名如其意, “添加周期时间观察者” ,参数1 interal 为CMTime 类型的,参数2 queue为串行队列,如果传入NULL就是默认主线程,参数3 为CMTime 的block类型。

简而言之就是,每隔一段时间后执行 block。

比如:我们把interval设置成CMTimeMake(1, 10),在block里面刷新label,就是一秒钟刷新10次。

正常观察播放进度一秒钟一次就行了,所以可以这么写:

[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1,1) queue:nilusingBlock:^(CMTimetime) {AVPlayerItem*item = WeakSelf.playerItem;NSIntegercurrentTime = item.currentTime.value/item.currentTime.timescale;NSLog(@"当前播放时间:%ld",currentTime);}];

3. loadedTimeRange 缓存时间

获取视频的缓存情况我们需要监听playerItem的loadedTimeRanges属性

[self.playerItem addObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];

在KVO的回调里:

if([keyPath isEqualToString:@"loadedTimeRanges"]){NSArray*array = _playerItem.loadedTimeRanges;//本次缓冲时间范围CMTimeRangetimeRange = [array.firstObject CMTimeRangeValue];floatstartSeconds =CMTimeGetSeconds(timeRange.start);floatdurationSeconds =CMTimeGetSeconds(timeRange.duration);NSTimeIntervaltotalBuffer = startSeconds + durationSeconds;//缓冲总长度NSLog(@"当前缓冲时间:%f",totalBuffer);}

4. playbackBufferEmpty

监听playbackBufferEmpty我们可以获取当缓存不够,视频加载不出来的情况:

[self.playerItem addObserver:selfforKeyPath:@"playbackBufferEmpty"options:NSKeyValueObservingOptionNewcontext:nil];

在KVO回调里:

if([keyPath isEqualToString:@"playbackBufferEmpty"]) {//some code show loading }

5. playbackLikelyToKeepUp

playbackLikelyToKeepUp和playbackBufferEmpty是一对,用于监听缓存足够播放的状态

[self.playerItem addObserver:selfforKeyPath:@"playbackLikelyToKeepUp"options:NSKeyValueObservingOptionNewcontext:nil];if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {//由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放  [_player play];     }

AVURLAsset

播放视频只需一个url就能进行这样太不安全了,别人可以轻易的抓包盗链,为此我们需要为视频链接做一个请求头的认证,这个功能可以借助AVURLAsset完成。

AVPlayerItem除了可以用URL初始化,还可以用AVAsset初始化,而AVAsset不能直接使用,我们看下AVURLAsset的一个初始化方法:

/*!

@param URL

        An instance of NSURL that references a media resource.

@param options

        An instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See AVURLAssetPreferPreciseDurationAndTimingKey and AVURLAssetReferenceRestrictionsKey above.
*/+ (instancetype)URLAssetWithURL:(NSURL*)URL options:(nullableNSDictionary *)options;

AVURLAssetPreferPreciseDurationAndTimingKey.这个key对应的value是一个布尔值, 用来表明资源是否需要为时长的精确展示,以及随机时间内容的读取进行提前准备。

除了这个苹果官方介绍的功能外,他还可以设置请求头,这个算是隐藏功能了,因为苹果并没有明说这个功能,我是费了很大劲找到的。

NSMutableDictionary* headers = [NSMutableDictionary dictionary];[headers setObject:@"yourHeader"forKey:@"User-Agent"];self.urlAsset = [AVURLAsset URLAssetWithURL:self.videoURL options:@{@"AVURLAssetHTTPHeaderFieldsKey": headers}];// 初始化playerItemself.playerItem = [AVPlayerItem playerItemWithAsset:self.urlAsset];

6.系统音量相关

/***  获取系统音量*/
- (void)configureVolume
{MPVolumeView *volumeView = [[MPVolumeView alloc] init];_volumeViewSlider = nil;for (UIView *view in [volumeView subviews]){if ([view.class.description isEqualToString:@"MPVolumeSlider"]){_volumeViewSlider = (UISlider *)view;break;}}// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音NSError *setCategoryError = nil;BOOL success = [[AVAudioSession sharedInstance]setCategory: AVAudioSessionCategoryPlaybackerror: &setCategoryError];if (!success) { /* handle the error in setCategoryError */ }// 监听耳机插入和拔掉通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
}/***  耳机插入、拔出事件*/
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{NSDictionary *interuptionDict = notification.userInfo;NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];switch (routeChangeReason) {case AVAudioSessionRouteChangeReasonNewDeviceAvailable:// 耳机插入break;case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:{// 耳机拔掉// 拔掉耳机继续播放[self play];}break;case AVAudioSessionRouteChangeReasonCategoryChange:// called at start - also when other audio wants to playNSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");break;}
}

7.其它App播放声音打断

如果用户当时在后台听音乐,如QQ音乐,或者喜马拉雅这些App,这个时候播放视频后,其会被我们打断,当我们不再播放视频的时候,自然需要继续这些后台声音的播放。

首先,我们需要先向设备注册激活声音打断

 AudioSessionSetActive(YES);

当然我们也可以通过

 [AVAudioSession sharedInstance].otherAudioPlaying;

这个方法来判断还有没有其它业务的声音在播放。 当我们播放完视频后,需要恢复其它业务或App的声音,这时我们可以在退到后台的事件中调用如下方法:

 - (void)applicationDidEnterBackground:(UIApplication *)application {NSError *error =nil;AVAudioSession *session = [AVAudioSession sharedInstance];// [session setCategory:AVAudioSessionCategoryPlayback error:nil];BOOL isSuccess = [session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];if (!isSuccess) {NSLog(@"__%@",error);}else{NSLog(@"成功了");}}

播放相关通知

1、声音类:

//声音被打断的通知(电话打来)

AVAudioSessionInterruptionNotification

//耳机插入和拔出的通知

AVAudioSessionRouteChangeNotification

根据userInfo判断具体状态

2、播放类

//播放完成

AVPlayerItemDidPlayToEndTimeNotification

//播放失败

AVPlayerItemFailedToPlayToEndTimeNotification

//异常中断

AVPlayerItemPlaybackStalledNotification

对于播放完成的通知:

[[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(playerMovieFinish:) name:AVPlayerItemDidPlayToEndTimeNotificationobject:[self.player currentItem]];

3、系统状态

//进入后台

UIApplicationWillResignActiveNotification

//返回前台

UIApplicationDidBecomeActiveNotification

http://chatgpt.dhexx.cn/article/yeUpnCiG.shtml

相关文章

【iOS】AVPlayer 播放音视频

1、常见的音视频播放器 iOS开发中不可避免地会遇到音视频播放方面的需求。 常用的音频播放器有 AVAudioPlayer、AVPlayer 等。不同的是&#xff0c;AVAudioPlayer 只支持本地音频的播放&#xff0c;而 AVPlayer 既支持本地音频播放&#xff0c;也支持网络音频播放。 常用的视…

【iOS】AVPlayer 视频播放

视频播放器的类别 iOS开发中不可避免地会遇到音视频播放方面的需求。 常用的音频播放器有 AVAudioPlayer、AVPlayer 等。不同的是&#xff0c;AVAudioPlayer 只支持本地音频的播放&#xff0c;而 AVPlayer 既支持本地音频播放&#xff0c;也支持网络音频播放。 常用的视频播放…

iOS音视频播放-AVPlayer简单使用

按公司需求需要对音频文件进行后台播放,借此机会对音频播放做了个总结.主要针对 AVPlayer 进行详细说明. iOS 各播放器比较 名称使用环境优点确点System Sound ServicesAVFoundationC语言的底层写法&#xff0c;节省内存支持的格式有限&#xff0c;音量无法通过音量键控制&…

iOS音视频播放指南(二)

1. 让你的App支持画中画 画中画指可以让视频在小窗中播放,可以一边看视频一边刷知乎 你可以使用AVPlayerViewController或者AVPictureInPictureController来实现画中画播放。 其中AVPictureInPictureController支持你自定义一些播放控件 在支持画中画播放之前,确保你按照iOS音视…

iOS音视频播放指南(一)

1. 简介 苹果目前提供两个框架用来处理音视频播放 1.AVFoundation AVFoundation用于播放、处理音视频。可以通过结构图看到AVFoundation位于UIKit之下,很好理解AVFoundation并不提供用户界面,你可以自己自己构建用户界面来控制媒体的播放处理等功能。 但是苹果更推荐使用AVKit来…

iOS视频播放的基本方法

本文总结了iOS中最常见的视频播放方法&#xff0c;不同的方法都各具特点&#xff0c;我希望能够总结它们的不同&#xff0c;方便在开发中选择合适的技术方案。 Apple为我们提供了多种方法来实现视频播放&#xff0c;包括MPMoviePlayerController&#xff0c;MPMoviePlayerView…

【计算机系统1】4 Nim游戏

目录 目的与要求 内容与方法 步骤与过程 程序总体设计 核心数据结构及算法流程 核心代码 调试过程 界面展示子程序DISPLAY&#xff08;嵌套&#xff1a;球数展示子程序PUTBALL&#xff09; 游戏子程序GAME&#xff08;嵌套&#xff1a;单人每轮子程序PLAY&#xff09; 结论或体…

java nim游戏_LeetCode 292. Nim游戏

题目描述&#xff1a; 你和你的朋友&#xff0c;两个人一起玩 Nim游戏&#xff1a;桌子上有一堆石头&#xff0c;每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。 你们是聪明人&#xff0c;每一步都是最优解。 编写一个函数&#xff0c;来判断你…

Nim游戏、3的幂、4的幂

&#x1f345; Java学习路线&#xff1a;Java学习路线 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、CSDN哪吒公众号作者✌ 、Java架构师奋斗者&#x1f4aa; &#x1f345; 百日刷题计划&#xff1a;第 12 / 100 天。 &#x1f345; 扫描主页左侧二维码&a…

【数论】博弈论 —— nim游戏

知识点 一 . nim游戏的数学定义 Nim游戏是博弈论中最经典的模型&#xff0c;它又有着十分简单的规则和无比优美的结论 。 Nim游戏是组合游戏(Combinatorial Games)的一种&#xff0c;准确来说&#xff0c;属于“Impartial Combinatorial Games”&#xff08;以下简称ICG&#…

【模板题】几种常见的Nim游戏(博弈论)

一、AcWing 891. Nim游戏 【题目描述】 给定 n n n堆石子&#xff0c;两位玩家轮流操作&#xff0c;每次操作可以从任意一堆石子中拿走任意数量的石子&#xff08;可以拿完&#xff0c;但不能不拿&#xff09;&#xff0c;最后无法进行操作的人视为失败。 问如果两人都采用最优…

Kafka 为什么能那么快 | Kafka高效读写数据的原因

点击上方“服务端思维”&#xff0c;选择“设为星标” 回复”669“获取独家整理的精选资料集 回复”加群“加入全国服务端高端社群「后端圈」 无论 kafka 作为 MQ 也好&#xff0c;作为存储层也罢&#xff0c;无非就是两个功能&#xff08;好简单的样子&#xff09;&#xff0c…

【人人都懂密码学】一篇最易懂的Java密码学入门教程

密码与我们的生活息息相关&#xff0c;远到国家机密&#xff0c;近到个人账户&#xff0c;我们每天都在跟密码打交道&#xff1a; 那么&#xff0c;密码从何而来&#xff1f;生活中常见的加密是怎么实现的&#xff1f;怎么保证个人信息安全&#xff1f;本文将从这几方面进行浅谈…

Kafka必须掌握的核心技术--为什么吞吐量大、速度快?

点击上方“服务端思维”&#xff0c;选择“设为星标” 回复”669“获取独家整理的精选资料集 回复”加群“加入全国服务端高端社群「后端圈」 Kafka是大数据领域无处不在的消息中间件&#xff0c;目前广泛使用在企业内部的实时数据管道&#xff0c;并帮助企业构建自己的流计算应…

哪些软件问题也可导致硬盘录像机死机

硬盘录像机死机除了一些硬件上的问题之外&#xff0c;也有不少是由软件引起的。如&#xff1a; 1、病毒感染 病毒是计算机操作的大患&#xff0c;几乎人人恶之。病毒可以使计算机工作效率急剧下降&#xff0c;造成频繁死机、数据丢失、系统崩溃&#xff0c;甚至损坏主板、硬盘、…

导致硬盘录像机卡死的十大原因分析

硬盘录像机卡死除了一些技术上的问题之外&#xff0c;也有不少是由软件引起的。如&#xff1a; 1、病毒感染 病毒是硬盘录像机操作的大患&#xff0c;几乎人人恶之。病毒可以使硬盘录像机工作效率急剧下降&#xff0c;造成频繁死机、数据丢失、系统崩溃&#xff0c;甚至损坏主板…

谈项目管理和软件测试过程

谈项目管理和软件测试过程&#xff08;一&#xff09; 1. 软件测试在公司的组织保障是基础 1.1 研发部组织结构介绍 以华友公司研发部的组织结构为例&#xff0c;测试部门属于研发部副总裁直接管理&#xff0c;见如下结构图 公司研发部的组织结构图 …

NoSQL初探之人人都爱Redis:(1)Redis简介与简单安装

一、NoSQL的风生水起 1.1 后Web2.0时代的发展要求 随着互联网Web2.0网站的兴起&#xff0c;传统的关系数据库在应付Web2.0网站&#xff0c;特别是超大规模和高并发的SNS类型的Web2.0纯动态网站已经显得力不从心&#xff0c;暴露了很多难以克服的问题&#xff1a; &#xff08;1…

软件公司面试总结

文章目录 面试1面试2面试3面试4面试5面试6 面试1 1、你先做个简单的自我介绍吧 我叫张三&#xff0c;2015年在重庆邮电大学毕业。我读的专业是电子信息。工作已经快5年了。 我上家公司的主营业务是在柬埔寨做移动支付钱包。 最近做的一个项目是聚合支付的项目&#xff0c;主要…

腾讯的硬盘里,有互联网的昨天今天和明天

作者&#xff1a;史中 来源&#xff1a;浅黑科技&#xff08;qianheikeji&#xff09; 2018年1月1日&#xff0c;太阳照常升起。 世界上所有的时钟合谋&#xff0c;把最后一个90后推过了18岁的门槛。对于这些年轻的面孔来说&#xff0c;自由的风终于如期而至&#xff0c;只是其…