进新公司之后接手的项目里有一个功能是网络音频的在线播放功能,这个功能之前是用DOUAudioStreamer框架做的,但是这个框架已经停止更新快一年了,有很多的 BUG 都没有修复,而且这个框架利用的是流缓存策略进行的在线播放,经常会出现数据较短就无法播放或者拖动进度条到未缓存区域缓存计算错误的问题
于是决定自己写一个新的播放器来完成在线音频的播放,在查阅了相关资料之后,发现苹果官方新出的 AVPlayer 可以很好的完成在线播放功能,AVPlayer自由度很高,可以自定义视频播放器, AVPlayer本身无法显示视频,需要借助AVPlayerLayer显示,初始化以及播放
基本代码如下:
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
@property (strong, nonatomic) AVPlayer *avPlayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//这个链接是M3U8的,你看到此博客的时候此链接可能已经失效,请自行找链接测试
AVPlayerItem *playItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:@"http://cctv1.vtime.cntv.cloudcdn.net/cache/12_/seg0/index.m3u8?AUTH=KDumCPYYPzSTcmtewPt/u78MdD6mwSpceXl98vdwcN8RIWA7hZqDK8s3RWkdW3PymV7TkVLHQ5UJp1gHXtkWGg=="]];
//初始化AVPlayer
self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:playItem];
//设置AVPlayer关联
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
//设置视频模式
playerLayer.videoGravity = AVLayerVideoGravityResize;
playerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width * 9.0 / 16.0);
//创建一个UIView与AVPlayerLayer关联
UIView *playerView = [[UIView alloc] initWithFrame:CGRectMake(0, 20, CGRectGetWidth(playerLayer.frame), CGRectGetHeight(playerLayer.frame))];
playerView.backgroundColor = [UIColor blackColor];
[playerView.layer addSublayer:playerLayer];
[self.view addSubview:playerView];
//开始播放(请在真机上运行)
[self.avPlayer play];
}
当然我们在项目里肯定不会只是一个播放界面那么简单,我们可以简单的搭建一个 XIB, 拖一些控件进来完成基本的进度管理和播放暂停
830A8809-7BBE-41AB-AC1D-C515BC817E10.png我们把控件拖进对应的 Controller
8BD5C546-2857-4D8C-810B-18B43EAA31CA.png
然后设置好各控件的监听方法
// KVO方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if ([keyPath isEqualToString:@"status"]) {
if ([playerItem status] == AVPlayerStatusReadyToPlay) {
NSLog(@"AVPlayerStatusReadyToPlay");
self.stateButton.enabled = YES;
CMTime duration = self.playerItem.duration;// 获取视频总长度
CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒
_totalTime = [self convertTime:totalSecond];// 转换成播放时间
[self customVideoSlider:duration];// 自定义UISlider外观
NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration));
[self monitoringPlayback:self.playerItem];// 监听播放状态
} else if ([playerItem status] == AVPlayerStatusFailed) {
NSLog(@"AVPlayerStatusFailed");
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
NSLog(@"Time Interval:%f",timeInterval);
CMTime duration = _playerItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
[self.videoProgress setProgress:timeInterval / totalDuration animated:YES];
}
}
之后我们就要创建一个playerItem,playerItem就是框架里对一个播放对象的封装,一个 item 对应一个播放来源,当一个 item 播放完毕,更换下一个 item 就可以完成播放数据的切换
NSURL *videoUrl = [NSURL URLWithString:@"http://yckj-data1.oss-cn-hangzhou.aliyuncs.com/pro002/foyue/%E4%B8%89%E5%8D%83%E4%BD%9B%E6%B4%AA%E5%90%8D%E5%AE%9D%E5%BF%8F%EF%BC%88%E6%9C%AA%E6%9D%A5%E5%8D%83%E4%BD%9B%EF%BC%895%5B%E6%96%87%E6%AE%8A%E8%AE%B2%E5%A0%82%5D.mp3"];
self.playerItem = [AVPlayerItem playerItemWithURL:videoUrl];
[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性
[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];// 监听loadedTimeRanges属性
这样我们只要把 item 放进 player 里就可以了
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
self.playerView.player = _player;
上面的代码里 playerView 是对AVPlayerLayer的封装,只有AVPlayerLayer才能显示视频,如果只是音频播放可以不用封装,封装代码如下
@interface PlayerView : UIView
@property (nonatomic ,strong) AVPlayer *player;
@end
@implementation PlayerView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
之后只要获取当前播放时间,总时间以及缓存数据进行监听,就可以完成进度的滚动了
当然还有最重要的后台播放功能
第一步:
937405-3b79a84028c34413.png
第二步:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
//默认情况下扬声器播放
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
return YES;
}
到此一个简单的在线播放器就完成了
网友评论