iOSAVPlayer实现后台连续播放视频

iOSAVPlayer实现后台连续播放视频

2023年7月25日发(作者:)

iOSAVPlayer实现后台连续播放视频最近接到⼀个需求,需要做⼀个在后台播放视频的功能。折腾了⼀下,最后总算完成了。因此写⼀篇⽂章,介绍下具体的实现步骤,也说说⾃⼰遇到的坑,算是总结和记录。前⾔当 App 退到后台时,会进⼊

suspend 状态,若此时在播放视频,则会⾃动暂停。我们需要实现的效果是,当 App 退到后台时,视频中的声⾳还能继续播放。另外,我们还同时实现视频的连续播放功能,和在锁屏界⾯控制视频播放的功能。具体怎么做,下⾯听我⼀⼀道来。注意:由于 iOS 模拟器存在 BUG,尤其是 iOS 11 的模拟器,不能在后台播放⾳频,因此以下功能最好使⽤真机测试。⼀、后台播放⾳频要实现后台播放视频功能,⾸先需要实现后台播放⾳频功能。实现后台播放⾳频很简单,只要简单配置⼀下就可以了。总共有三步:1. 修改 在

中添加

Required background modes ,并在下⾯添加⼀项

App plays audio or streams audio/video using AirPlay 。如图所⽰:2. 修改 Capabilities在

Capabilities 中开启

Background Modes 。如图所⽰:3. 修改 AppDelegate在

AppDelegate 的

application: didFinishLaunchingWithOptions: ⽅法中,添加以下代码:// 告诉app⽀持后台播放AVAudioSession *audioSession = [AVAudioSession sharedInstance];[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];[audioSession setActive:YES error:nil];⾄此就实现了后台播放⾳频的功能,但这不是我们的最终⽬的,请继续往下看。⼆、后台播放视频⽹上讲实现后台播放视频的资料并不多(可能⽐较少有这么坑的需求)。我在⽹上找了⼀圈,只有 这篇⽂章 提到了,⽅法也很简单,分为两步:1. 退到后台时移除 playerLayer 上的 player在

viewController 中添加退到后台监听:NSNotificationCenter *center = [NSNotificationCenter defaultCenter];[center addObserver:self selector:@selector(removePlayerOnPlayerLayer) name:UIApplicationDidEnterBackgroundNotification object:nil];移除

player :- (void)removePlayerOnPlayerLayer {

_ = nil;}2. 回到前台时重新添加 player在

viewController 中添加回到前台监听:NSNotificationCenter *center = [NSNotificationCenter defaultCenter];[center addObserver:self selector:@selector(resetPlayerToPlayerLayer) name:UIApplicationWillEnterForegroundNotification object:nil];重新添加

player :- (void)resetPlayerToPlayerLayer {

_ = _player;}这样简单的后台播放视频就实现了。对于上⾯的实现后台播放视频的⽅法,我的理解是,iOS 是⽀持后台播放⾳频的,⽽

AVPlayer 在播放视频时,会将图像渲染在

layer 上,因此只要取消图像的渲染,只播放⾳频,就可以实现后台播放。3. 连续播放视频后台连续播放视频的逻辑,其实和前台连续播放的逻辑⼀样。可以通过监听

playerItem 播放结束的通知来切换歌曲,则当播放结束时,需要移除对当前

playerItem 的监听,然后添加下⼀个

playerItem 的监听。这⾥直接通过判断进度条是否完成,来切换歌曲。// 监听播放进度__weak ViewController * weakSelf = self;[ addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC) queue:NULL usingBlock:^(CMTime time) {

[weakSelf updateProgressView]; }];// 更新进度条进度- (void)updateProgressView {

tDuration = CMTimeGetSeconds(_on);

CGFloat progress = CMTimeGetSeconds(_tTime) / _currentDuration;

if (progress == 1.0f) { [self playNextVideo]; // 播放下⼀个视频 } else { [_viewVideoProgress setValue:progress]; // 更新进度条 }}下⾯插播⼀条

CMTime 的⼴告。可跳过。上⾯监听播放进度的时候,⽤到了⼀个叫

CMTime 的东西,这⾥简单地讲⼀下我的理解。⼀般我们⽤

CMTime 的时候,都是使⽤

CMTimeGetSeconds(time) 将它转成秒数。那为何不直接使⽤

NSTimeInterval 来表⽰时间就好了?原因只有⼀个 —— 精度。浮点数没有办法进⾏准确的加减运算,当多次加减后,可能会出现较⼤误差。因此在视频⼀般⽤

CMTime 来表⽰时间,因为

CMTime 可以规定最⼩的精度,从⽽保证累加后时间的准确性。CMTime 的构造⽅法

CMTimeMakeWithSeconds(seconds, timescale),

seconds 表⽰秒数,

1 / timescale 表⽰最⼩精度。另⼀个构造⽅法

CMTimeMake(value, timescale) ,其中

seconds =

value /

timescale。即

CMTimeMakeWithSeconds(1, 1000) 等价于

CMTimeMake(1000, 1000) ,都表⽰ 1 秒,最⼩精度为 0.001 。注意:需要满⾜

seconds >=

1 / timescale ,即

value > 1,这也是精度存在的意义。三、添加远程控制1. ⽤ MPNowPlayingInfoCenter 显⽰歌曲信息先上代码:// 更新锁屏界⾯信息- (void)updateLockScreenInfo {

if (!_player) { return; }

// 1.获取锁屏中⼼ MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter]; // 初始化⼀个存放⾳乐信息的字典 NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];

// 2、设置歌曲名 [playingInfoDict setObject:[NSString stringWithFormat:@"歌曲%ld", (long)_currentIndex + 1] forKey:MPMediaItemPropertyTitle]; [playingInfoDict setObject:[NSString stringWithFormat:@"专辑%ld", (long)_currentIndex + 1] forKey:MPMediaItemPropertyAlbumTitle];

// 3、设置封⾯的图⽚ UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"cover%", (long)_currentIndex + 1]]; if (image) { MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image]; [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork]; }

// 4、设置歌曲的时长和已经消耗的时间 NSNumber *playbackDuration = @(CMTimeGetSeconds(_on)); NSNumber *elapsedPlaybackTime = @(CMTimeGetSeconds(_tTime)); if (!playbackDuration || !elapsedPlaybackTime) { return; } [playingInfoDict setObject:playbackDuration forKey:MPMediaItemPropertyPlaybackDuration]; [playingInfoDict setObject:elapsedPlaybackTime forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; [playingInfoDict setObject:@(_) forKey:MPNowPlayingInfoPropertyPlaybackRate];

//⾳乐信息赋值给获取锁屏中⼼的nowPlayingInfo属性 yingInfo = playingInfoDict;}注意:

updateLockScreenInfo 不需要频繁调⽤,锁屏界⾯的进度条会⾃⼰计时,只需要在关键的时刻去同步这个已播放时长。⼀般需要调⽤的时刻有,切换歌曲、暂停、播放、拖动进度条等。这⾥有个坑。我们知道

player 有个

rate 属性,为 0 的时候表⽰暂停,为 1.0 的时候表⽰播放。相应的,

nowPlayingInfo也有个

MPNowPlayingInfoPropertyPlaybackRate 属性。前⾯说到,「锁屏界⾯的进度条会⾃⼰计时」,它是否在计时就是取决于这个属性。坑的地⽅在于,这个属性和

player 的

rate 并不同步。也就是说,单纯地在锁屏界⾯点暂停后,

player 会暂停,

rate 也会变成 0 ,但是

MPNowPlayingInfoPropertyPlaybackRate 却不为 0 。导致的结果是,在锁屏界⾯点击了暂停按钮,这个时候进度条表⾯看起来停⽌了⾛动,但是其实还是在计时,所以再点击播放的时候,锁屏界⾯进度条的光标会发⽣位置闪动。解决⽅法:在视频暂停和播放的时候,同步视频的已播放时长

_tTime 和

MPNowPlayingInfoPropertyElapsedPlaybackTime 、视频的当前播放速率

_ 和

MPNowPlayingInfoPropertyPlaybackRate 。2. ⽤ MPRemoteCommandCenter 实现播放控制先上代码:// 添加远程控制- (void)createRemoteCommandCenter {

MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];

MPRemoteCommand *pauseCommand = [commandCenter pauseCommand]; [pauseCommand setEnabled:YES]; [pauseCommand addTarget:self action:@selector(remotePauseEvent)];

MPRemoteCommand *playCommand = [commandCenter playCommand]; [playCommand setEnabled:YES]; [playCommand addTarget:self action:@selector(remotePlayEvent)];

MPRemoteCommand *nextCommand = [commandCenter nextTrackCommand]; [nextCommand setEnabled:YES]; [nextCommand addTarget:self action:@selector(remoteNextEvent)];

MPRemoteCommand *previousCommand = [commandCenter previousTrackCommand]; [previousCommand setEnabled:YES]; [previousCommand addTarget:self action:@selector(remotePreviousEvent)];

if (@available(iOS 9.1, *)) { MPRemoteCommand *changePlaybackPositionCommand = [commandCenter changePlaybackPositionCommand]; [changePlaybackPositionCommand setEnabled:YES]; [changePlaybackPositionCommand addTarget:self action:@selector(remoteChangePlaybackPosition:)]; }}在 iOS 7.1 之后,可以通过

MPRemoteCommandCenter 来控制⾳频播放。每个控制操作都封装为⼀个

MPRemoteCommand 对象,给

MPRemoteCommand 添加响应事件有两种⽅式:⼀种是通过

addTargetWithHandler:,以

Block 的⽅式传⼊响应事件,需要返回

MPRemoteCommandHandlerStatusSuccess 来告知响应成功。另⼀种是通过

addTarget: action:,因为

MPRemoteCommandCenter 是个单例,所以在

target 的

dealloc 中要记得调⽤

removeTarget: 。如下所⽰:- (void)dealloc { [self removeCommandCenterTargets];}- (void)removeCommandCenterTargets { MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; [[commandCenter playCommand] removeTarget:self]; [[commandCenter pauseCommand] removeTarget:self]; [[commandCenter nextTrackCommand] removeTarget:self]; [[commandCenter previousTrackCommand] removeTarget:self];

if (@available(iOS 9.1, *)) { [PlaybackPositionCommand removeTarget:self]; }}注意:因为

changePlaybackPositionCommand 在 iOS 9.1 以后才可⽤,所以这⾥加了系统判断。到这⾥就实现了锁屏界⾯的播放控制。源码请到 GitHub 上查看完整例⼦。参考iOS AVPlayer之后台连续播放视频AVPlayer ⾳乐播放后台播放,以及锁屏主题设置这可能是最详细的CMTime教程获取更佳的阅读体验,请访问原⽂地址 【Lyman's Blog】iOS AVPlayer 实现后台连续播放视频

发布者:admin,转转请注明出处:http://www.yc00.com/web/1690228712a318021.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信