项目上线音频播放后,仍然满足不了广大学员的需求,学员想要的是音频能够在锁屏状态下也能够播放。于是和公司领导讨论完成音频的后台播放功能,现在就将我所实现音频锁屏播放的过程和遇到的坑和大家分享一下。
1、首先是集成音频播放器:(我把这个播放器放在了一个单利里面)
实现的代码如下:
#pragma mark --添加在线播放器
- (void)addOnLinePlayer {
WS(weakSelf)
[[SKAVPlayerManager PlayerManager] replaceItemWithUrlString:self.onLinePlayerUrl type:@"zaixian"];
__weak typeof(SKAVPlayerManager *) manager = [SKAVPlayerManager PlayerManager];
[SKAVPlayerManager PlayerManager].statusBlock = ^(BOOL success) {
if (success) {
// 如果是暂停状态则不进行播放
if (weakSelf.playerButton.selected != YES) {
[manager playerPlay];
[SKAVPlayerManager PlayerManager].player.rate = weakSelf.playerRate;
}
} else {
}
};
// 添加时间监听
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
// 播放一段音频
- (void)replaceItemWithUrlString:(NSString *)urlString type:(NSString *)readType {
self.ccid = urlString;
[_playerItem removeObserver:self forKeyPath:@"status"];
_playerItem = nil;
if ([readType isEqualToString:@"zaixian"]) {
_playerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:urlString]];
} else if ([readType isEqualToString:@"bendi"]){
NSURL *audioURL = [NSURL fileURLWithPath:urlString];
_playerItem = [[AVPlayerItem alloc] initWithURL:audioURL];
}
[_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[self.player replaceCurrentItemWithPlayerItem:_playerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"status"]) {
self.isNotify = YES;
switch (self.player.status) {
case AVPlayerStatusUnknown:
// KVO:未知状态,此时不能播放
if (self.statusBlock) {
self.statusBlock(NO);
}
break;
case AVPlayerStatusReadyToPlay:
//KVO:准备完毕,可以播放
if (self.statusBlock) {
self.statusBlock(YES);
}
// [self playerPlay];
// [[NSNotificationCenter defaultCenter] postNotificationName:PlayerStatusNormal object:nil];
break;
case AVPlayerStatusFailed:
// KVO:加载失败,网络或者服务器出现问题
if (self.statusBlock) {
self.statusBlock(NO);
}
break;
default:
break;
}
}
}
2、时间进度的控制如下以及进度条的更新:
- (void)timerAction {
if ([SKAVPlayerManager PlayerManager].player.currentTime.timescale == 0 || [SKAVPlayerManager PlayerManager].player.currentItem.duration.timescale == 0) {
return;
}
// 获得音乐总时长
long long int totalTime = [SKAVPlayerManager PlayerManager].player.currentItem.duration.value / [SKAVPlayerManager PlayerManager].player.currentItem.duration.timescale;
// 获得当前时间
long long int currentTime = [SKAVPlayerManager PlayerManager].player.currentTime.value / [SKAVPlayerManager PlayerManager].player.currentTime.timescale;
currentTimeLable.text = [NSString stringWithFormat:@"%02lld:%02lld", currentTime / 60, currentTime % 60];
orignalTimeLable.text = [NSString stringWithFormat:@"%02lld:%02lld", totalTime / 60, totalTime % 60];
audioSlider.maximumValue = totalTime;
audioSlider.minimumValue = 0;
audioSlider.value = currentTime;
if (currentTime == totalTime) {
// 进行下一曲播放
[self nextButtonAction:[UIButton new]];
}
if (@available(iOS 11.0, *)) {
} else {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
[self onLineRemoteControl];
}
}
}
3、这个时候音频就能正常播放了,在保证音频能正常播放的对工程进行设置:
先要注册后台播放:
然后在info.plist文件里面进行设置:
App plays audio or streams audio/video using AirPlay
4、当程序进入后台和前台的时候通过通知监听所处的状态:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
- (void)didEnterBackground {
[self onLineRemoteControl];
}
// 在线后台播放
- (void)onLineRemoteControl {
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
UIImage *lockImage = [UIImage imageNamed:@"photo1.png"];
MPMediaItemArtwork *artwork =
[[MPMediaItemArtwork alloc] initWithImage:lockImage];
NSDictionary *mediaDict =
@{
MPMediaItemPropertyTitle: self.model.title,
MPMediaItemPropertyMediaType: @(MPMediaTypeAnyAudio),
MPMediaItemPropertyPlaybackDuration:
@([SKAVPlayerManager PlayerManager].player.currentItem.duration.value / [SKAVPlayerManager PlayerManager].player.currentItem.duration.timescale),
MPNowPlayingInfoPropertyPlaybackRate: @(1.0),
MPNowPlayingInfoPropertyElapsedPlaybackTime:
@([SKAVPlayerManager PlayerManager].player.currentTime.value / [SKAVPlayerManager PlayerManager].player.currentTime.timescale),
MPMediaItemPropertyAlbumArtist:self.subscribeModel.title,
MPMediaItemPropertyArtist:self.subscribeModel.title,
MPMediaItemPropertyArtwork: artwork};
[center setNowPlayingInfo:mediaDict];
//让app支持接受远程控制事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
- (void) didBecomeActive {
//让app结束接受远程控制事件
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
5、这个时候发现是可以进行后台播放的,但是进度条无法调整,为了能够调整进度条需要添加远程控制命令中心:
#pragma mark --锁屏界面开启和监控远程控制事件
- (void)createRemoteCommandCenter{
//远程控制命令中心 iOS 7.1 之后 详情看官方文档:https://developer.apple.com/documentation/mediaplayer/mpremotecommandcenter
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
// commandCenter.togglePlayPauseCommand 耳机线控的暂停/播放
[commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.previousTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(@"上一首");
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.nextTrackCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(@"下一首");
return MPRemoteCommandHandlerStatusSuccess;
}];
//在控制台拖动进度条调节进度(仿QQ音乐的效果)
[commandCenter.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
CMTime totlaTime = [SKAVPlayerManager PlayerManager].player.currentItem.duration;
MPChangePlaybackPositionCommandEvent * playbackPositionEvent = (MPChangePlaybackPositionCommandEvent *)event;
[[SKAVPlayerManager PlayerManager].player seekToTime:CMTimeMake(totlaTime.value*playbackPositionEvent.positionTime/CMTimeGetSeconds(totlaTime), totlaTime.timescale) completionHandler:^(BOOL finished) {
}];
return MPRemoteCommandHandlerStatusSuccess;
}];
}
这个时候就都实现了所有的功能了我们所做的有如下截图:
最后说下需要注意的几点:
1、使用changePlaybackPositionCommand进行seekTime时候,控制中心的播放进度条停止了下来 使用带handler的回调,在回调处再次对info进行进度条的更新
2、一定要注意block的循环引用问题,不然会引起内存暴涨。
网友评论