前言###
由于什么直播、小視頻火爆之后,我們開發(fā)者也要準(zhǔn)備我們的播放器了,最常用到的肯定是自定義AVPlayer播放器,雖然本文也主要研究的也是AVPlayer,但是你知道嗎?蘋果其實(shí)為我們提供了三種播放器。
播放兩種視頻###
1、播放本地視頻
播放本地視頻其實(shí)就是播放本地的視頻文件路徑,[NSURL fileURLWithPath:videoPath];找到文件路徑并且播放
2、播放網(wǎng)絡(luò)視頻
簡單來講就是我們設(shè)置播放器url地址等參數(shù)后,播放器就會向遠(yuǎn)程url所在服務(wù)器發(fā)送請求(一般請求包括兩個值,一個是offset偏移量,另一個是length長度),然后服務(wù)器就會根據(jù)參數(shù)返回數(shù)據(jù)給播放器。
3、播放器一般支持播放格式
AVPlayer支持WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等
MPMoviePlayerController與MPMoviePlayerViewController支持MOV, MP4, .MPV、3GP等
蘋果的三種播放器播放視頻###
我寫了一個實(shí)例demo,包括
1、MPMoviePlayerController播放視頻
2、MPMoviePlayerViewController播放視頻
3、AVPlayer播放視頻當(dāng)個視頻
4、AVPlayer播放視頻多個視頻
項(xiàng)目下載地址:三種視頻播放器大總結(jié)
項(xiàng)目效果圖

一、MPMoviePlayerController播放視頻,但是iOS3.2之后不建議使用
這個播放器在MediaPlayer中,所以在使用的時候,必須先導(dǎo)入
#import <MediaPlayer/MediaPlayer.h>
MPMoviePlayerController播放器是繼承于NSObject,但是內(nèi)部有個view可以展示視頻內(nèi)容,如果將該視圖添加其他控制器的view上,即可顯示視頻內(nèi)容
播放狀態(tài)需要增加觀察者來監(jiān)聽
- (MPMoviePlayerController *)player
{
if(_player == nil)
{
// 1.創(chuàng)建播放器
NSURL *url = [NSURL URLWithString:self.videoPath];
_player = [[MPMoviePlayerController alloc]initWithContentURL:url];
// 2.給播放器內(nèi)部的View設(shè)置frame
_player.view.frame = CGRectMake(0, 64, self.view.bounds.size.width, self.view.bounds.size.width * 9 / 16);
// 3.添加到控制器View中
[self.view addSubview:_player.view];
//監(jiān)聽當(dāng)前視頻播放狀態(tài)
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(loadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
// 4.設(shè)置控制面板的顯示
//_player.controlStyle = MPMovieControlStyleFullscreen;
}
return _player;
}
-(void)loadStateDidChange:(NSNotification*)sender
{
switch (self.player.loadState) {
case MPMovieLoadStatePlayable:
{
NSLog(@"加載完成,可以播放");
}
break;
case MPMovieLoadStatePlaythroughOK:
{
NSLog(@"緩沖完成,可以連續(xù)播放");
}
break;
case MPMovieLoadStateStalled:
{
NSLog(@"緩沖中");
}
break;
case MPMovieLoadStateUnknown:
{
NSLog(@"未知狀態(tài)");
}
break;
default:
break;
}
}
二、MPMoviePlayerViewController播放視頻,但是iOS3.2之后不建議使用
其實(shí)MPMoviePlayerViewController播放視頻與MPMoviePlayerController播放一樣,只是MPMoviePlayerViewController只能全屏播放視頻
并且MPMoviePlayerViewController包含MPMoviePlayerController屬性
- (instancetype)initWithContentURL:(NSURL *)contentURL NS_DESIGNATED_INITIALIZER;
@property (nonatomic, readonly) MPMoviePlayerController *moviePlayer;
并且彈出方式也是modal方式
-(void)presentMoviePlayerViewControllerAnimated:(MPMoviePlayerViewController *)moviePlayerViewController
-(void)dismissMoviePlayerViewControllerAnimated
3、AVPlayer播放視頻當(dāng)個視頻(本文重點(diǎn)講解)
由于播放器可能在以后我們經(jīng)常會用到,所以我建議封裝成工具類。
首先我們來看看AVPlayer播放器設(shè)置一些什么鬼?
@property (nonatomic, strong) AVPlayerItem *currentPlayerItem; //當(dāng)前正在播放視頻的Item
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerLayer *currentPlayerLayer; //當(dāng)前圖像層
AVPlayerItem:播放器的播放model,管理資源的對象,這個東東很重要,不僅可以可以獲得到播放狀態(tài)status,還可以看到緩沖進(jìn)度loadedTimeRange、playbackBufferEmpty、playbackLikelyToKeepUp
等屬性。
AVPlayer:播放器自身的播放工具類,可以播放play、暫停pause等
AVPlayerLayer:播放器播放的View,預(yù)覽層,把這個View增加到播放頁面顯示區(qū)域,就可以看到播放的畫面
第一步:
導(dǎo)入AVPlayer播放器框架
#import <AVFoundation/AVFoundation.h>
第二步
使用url初始化播放器AVPlayerItem、AVPlayer、AVPlayerLayer
//加載視頻的方式
if([url.absoluteString hasPrefix:@"http"])
{
//網(wǎng)絡(luò)加載數(shù)據(jù)
AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:[NSURL URLWithString:[url.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
self.currentPlayerItem = playerItem;
}
else
{
//本地視頻
self.currentPlayerItem = [AVPlayerItem playerItemWithURL:url];
}
self.player = [AVPlayer playerWithPlayerItem:self.currentPlayerItem];
self.currentPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.currentPlayerLayer.frame = CGRectMake(0, 0, superView.bounds.size.width, superView.bounds.size.height);
第三步
初始化完成,那么什么時候播放視頻呢,查看AVPlayerItem的屬性@property (nonatomic, readonly) AVPlayerItemStatus status;但是是只讀的,如果我們想要知道播放器的狀態(tài),那么就利用KVO監(jiān)聽狀態(tài)、同時監(jiān)聽播放器的緩沖進(jìn)度playbackBufferEmpty、是否加載完成(注意監(jiān)聽者可以在初始化的時候就增加)playbackLikelyToKeepUp
#define LZBVideoPlayerStatus @"status"
#define LZBVideoPlayerPlaybackBufferEmpty @"playbackBufferEmpty"
#define LZBVideoPlayerStatusplaybackLikelyToKeepUp @"playbackLikelyToKeepUp"
//增加播放器播放監(jiān)聽
- (void)addPlayerObserver
{
//播放狀態(tài)監(jiān)聽
[_currentPlayerItem addObserver:self forKeyPath:LZBVideoPlayerStatus options:NSKeyValueObservingOptionNew context:nil];
[_currentPlayerItem addObserver:self forKeyPath:LZBVideoPlayerPlaybackBufferEmpty options:NSKeyValueObservingOptionNew context:nil];
[_currentPlayerItem addObserver:self forKeyPath:LZBVideoPlayerStatusplaybackLikelyToKeepUp options:NSKeyValueObservingOptionNew context:nil];
}
//移除播放器播放監(jiān)聽
- (void)removePlayerObserver
{
[_currentPlayerItem removeObserver:self forKeyPath:LZBVideoPlayerStatus];
[_currentPlayerItem removeObserver:self forKeyPath:LZBVideoPlayerPlaybackBufferEmpty];
[_currentPlayerItem removeObserver:self forKeyPath:LZBVideoPlayerStatusplaybackLikelyToKeepUp];
}
第四步
監(jiān)聽播放器的狀態(tài)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if([keyPath isEqualToString:LZBVideoPlayerStatus])
{
[self processObserveValueStatusWithItem:(AVPlayerItem *)object];
}
else if ([keyPath isEqualToString:LZBVideoPlayerPlaybackBufferEmpty])
{
[self processObserveBuffering];
}
else if([keyPath isEqualToString:LZBVideoPlayerStatusplaybackLikelyToKeepUp])
{
[self processObserveBuffered];
}
}
//監(jiān)聽status
- (void)processObserveValueStatusWithItem:(AVPlayerItem *)playerItem
{
AVPlayerItemStatus status = playerItem.status;
switch (status) {
case AVPlayerItemStatusUnknown:{
}
break;
//監(jiān)聽到準(zhǔn)備播放
case AVPlayerItemStatusReadyToPlay:{
[self.player play]; //開始準(zhǔn)備播放
[self.showSuperView.layer insertSublayer:self.currentPlayerLayer atIndex:0]; //把預(yù)覽層增加到父類顯示區(qū)域
}
break;
case AVPlayerItemStatusFailed:{
}
break;
}
}
第五步
監(jiān)聽什么時候緩沖完成。主要邏輯:監(jiān)聽是否正在緩沖processObserveBuffering,如果正在緩沖,那么調(diào)用bufferingForSeconds,暫停播放、等過了2s之后再去播放一次并且檢查時候緩沖完成,如果緩沖完成就播放,如果沒有就繼續(xù)等2s,這樣循環(huán)到緩存完成。(只是適合播放小視頻)
//緩沖進(jìn)度
- (void)processObserveBuffering
{
if(self.currentPlayerItem.playbackBufferEmpty)
{
[self startLoadingViewInSuperView:self.showSuperView];
self.isBuffering = YES;
[self bufferingForSeconds];
}
}
//緩沖完成
- (void)processObserveBuffered
{
if (self.currentPlayerItem.playbackLikelyToKeepUp){
[self stopLoading];
[self startPlayerTimeProgress];
self.coverImageView.hidden = YES;
self.isBuffering = NO;
}
}
-(void)bufferingForSeconds{
if (self.isBuffering == NO) return;
[self.player pause];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.player play];
if (!self.currentPlayerItem.isPlaybackLikelyToKeepUp) {
[self bufferingForSeconds];
}
});
}
第六步
在播放過程中還可以獲取播放時長和已經(jīng)播放的時間
float totalDuration = CMTimeGetSeconds(self.currentPlayerItem.duration);
float currentTime = CMTimeGetSeconds(self.currentPlayerItem.currentTime);
NSInteger residueTime =(NSInteger)(totalDuration - currentTime);
第七步
播放過程中還要考慮,app的狀態(tài),并且監(jiān)聽內(nèi)存等
//增加app運(yùn)行狀態(tài)監(jiān)聽
-(void)addObserverOnce{
if (!self.isAddObserver) {
// 添加監(jiān)聽,只能增加一次
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterPlayGround) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
self.isAddObserver = YES;
}
小細(xì)節(jié):本文demo中還增加了視頻加載過程的默認(rèn)默認(rèn)背景圖等。
AVPlayer播放多個視頻留到下一篇文章,未完待續(xù)。。。。
下一站
AVPlayer播放多個小視頻切換
詳情代碼請直接下載demo查看:
項(xiàng)目下載地址:三種視頻播放器大總結(jié)
最后贈言###
如果覺得文章對您有幫助,不要忘記star哦!??,star 是對程序猿最大的鼓勵!