前言
AVPlayer属于AVFoundation框架,不仅能够播放音频,还可以播放视频,支持播放本地和网络音视频,更加接近底层,定制也更加灵活。
首先了解一下音频播放的实现级别:
(1)离线播放:这里并不是指应用不联网,而是指播放本地音频文件,包括先下完完成音频文件在进行播放的情况,这种使用AVFoundation里的AVAudioPlayer可以满足。
(2)在线播放:使用AVFoundation的AVPlayer可以满足。
(3)在线播放同时储存文件:使用AudioFileStreamer + AudioQueue可以满足。
(4)在线播放且带有音效处理:使用AudioFileStreamer + AudioQueue + 音效模块(系统自带或者自行开发)来满足。
本文主要针对第二种级别,介绍如何使用AVPlayer实现网络音频的播放。
什么是AVPlayer
AVPlayer继承NSObject,存在于AVFoundation,单独使用AVPlayer时是无法显示视频的,必须将视频图层添加到AVPlayerLayer中方能显示视频。使用AVPlayer首先了解一下几个常用的类:
1、AVAsset:AVAsset类专门用于获取多媒体的相关信息,包括获取多媒体的画面、声音等信息,属于一个抽象类,不能直接使用。
2、AVURLAsset:AVAsset的子类,可以根据一个URL路径创建一个包含媒体信息的AVURLAsset对象。
3、AVPlayerItem:一个媒体资源管理对象,管理者视频的一些基本信息和状态,一个AVPlayerItem对应着一个视频资源。
4、AVPlayer:播放器。
5、CMTime:是一个结构体,里面存储着当前的播放进度,总的播放时长。
AVPlayer的使用
1.实例化一个AVPlayer:
- (AVPlayer *)player {
if (_player == nil) {
_player = [[AVPlayer alloc] init];
_player.volume = 1.0; // 默认最大音量
}
return _player;
}
2.播放一个音频:
@property (nonatomic, strong) AVPlayerItem *currentPlayerItem;
#pragma mark - play - (void)playWithURL:(NSString *)playItemURL{ //缓存实现播放,可自行查找AVAssetResourceLoader资料,或采用AudioQueue实现 _currentPlayerItem = [AVPlayerItem playerItemWithURL:playItemURL]; _player = [[AVPlayer alloc]initWithPlayerItem:_currentPlayerItem]; [self addMusicTimeMake]; //监听时间变化 _isPlay = YES; [_player play]; //需要注意的是初始化完player之后不一定会马上开始播放,需要等待player的状态变为ReadyToPlay才会进行播放。 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_currentPlayerItem]; //播放完后进行回调。播放完后一般都会进行播放下一首的操作。 }
3.监听self.player.currentItem:
1、监听status有三种状态:
typedef NS_ENUM(NSInteger, AVPlayerStatus) {
AVPlayerStatusUnknown,
AVPlayerStatusReadyToPlay,
AVPlayerStatusFailed
};
2、监听loadedTimeRanges(缓冲进度),可以进行缓冲进度条的设置
注意:一般初始化player到播放都会经历Unknown到ReadyToPlay这个过程,网络情况良好时可能不会出现Unknown状态的提示,网络情况差的时候Unknown的状态可能会持续比较久甚至可能不进入ReadyToPlay状态,针对这种情况我们要做特殊的处理。
状态查询代码:
/** 状态查询 */
- (NSInteger )playerStatus{
if (_currentPlayerItem.status == AVPlayerItemStatusReadyToPlay) {
return 1;
}else{
return 0;
}
}
清空播放器监听属性代码:
//清空播放器监听属性
- (void)releasePlayer{
if (!self.currentPlayerItem) {
return;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self.player removeObserver:self forKeyPath:@"status"];
self.currentPlayerItem = nil;
}
4.播放器的常用操作:
暂停:
[_player pause];
上一首、下一首:
这里我们有两种方式可以实现,一种是有你自行控制下一首歌曲的item,将其替换到当前播放的item:
/** 播放下一曲 */
- (void)playNextMusic{
if (_currentPlayerItem) {
if (_indexPathRow < _rowNumber-1) {
_indexPathRow++;
}else{
_indexPathRow = 0;
}
}
NSURL *musicURL = [self.tracksVM playURLForRow:_indexPathRow];
_currentPlayerItem = [AVPlayerItem playerItemWithURL:musicURL];
_player = [[AVPlayer alloc]initWithPlayerItem:_currentPlayerItem];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self addMusicTimeMake];
_isPlay = YES;
[_player play];
[self.delegate changeMusic];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];
}
另一种是使用AVPlayer的子类AVQueuePlayer来播放多个item,调用 advanceToNextItem来播放下一首:
NSArray * items = @[item1, item2, item3 ....];
AVQueuePlayer * queuePlayer = [[AVQueuePlayer alloc]initWithItems:items];
5.NowPlayingCenter:
NowPlayingCenter可以在锁屏界面展示音乐的信息,达到增强用户体验的作用,他可以控制的范围包括:
锁屏界面上显示的歌曲播放信息和图片
iOS7之后控制中心上显示的歌曲播放信息
iOS7之前双击home键后出现的进程中向左滑动出现的歌曲播放信息
AppleTV,AirPlay中显示的播放信息
车载系统中显示的播放信息
这些信息的显示都由MPNowPlayingInfoCenter类来控制。
#pragma mark - 锁屏时候的设置,效果需要在真机上才可以看到
- (void)updateLockedScreenMusic{
// 播放信息中心
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
// 初始化播放信息
NSMutableDictionary *info = [NSMutableDictionary dictionary];
// 专辑名称
info[MPMediaItemPropertyAlbumTitle] = [self playMusicName];
// 歌手
info[MPMediaItemPropertyArtist] = [self playSinger];
// 歌曲名称
info[MPMediaItemPropertyTitle] = [self playMusicTitle];
// 设置图片
info[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc] initWithImage:[self playCoverImage]];
// 设置持续时间(歌曲的总时间)
[info setObject:[NSNumber numberWithFloat:CMTimeGetSeconds([self.player.currentItem duration])] forKey:MPMediaItemPropertyPlaybackDuration];
// 设置当前播放进度
[info setObject:[NSNumber numberWithFloat:CMTimeGetSeconds([self.player.currentItem currentTime])] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
// 切换播放信息
center.nowPlayingInfo = info;
}
NowPlayingCenter并不需要每一秒都去刷新(设置),它是根据你设置的PlaybackRate来计算进度条展示的进度,比如你PlaybackRate传1,那就是1秒刷新一次进度显示,当然暂停播放的时候它也会自动暂停。那什么时候设置Now Playing Center比较合适呢?对于播放网络音乐来说,需要刷新的有几个时间点:当前播放的歌曲变化时(如切换到下一首)、当前歌曲信息变化时(如从Unknown到ReadyToPlay)、当前歌曲拖动进度时。如果有读者是使用百度音乐听歌的话,会发现其带有锁屏歌词,其实它是采用“将歌词和封面合成新的图片设置为NowPlayingCenter的封面 + 歌词跃进时刷新NowPlayingCenter来实现的。
6.Remote Control控制音乐的播放:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
重写方法,成为第一响应者:
- (BOOL)canBecomeFirstResponder {
return YES;
}
对事件进行处理:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
//播放
[self.player startPlay];
break;
case UIEventSubtypeRemoteControlPause:
//暂停
[self.player pausePlay];
break;
case UIEventSubtypeRemoteControlNextTrack:
//下一首
[self.player playNextSong];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
//耳机的播放或暂停
self.player.isPlaying ? [self.player pausePlay] : [self.player startPlay];
break;
default:
break; }
}
示例代码
本文demo。
后记
对于AVPlayer的简单介绍就到这吧!有啥疑问大家可以评论留言都能看到或者指正我的错误。我会及时改正。