尊重知識洛姑,轉發(fā)請注明出處:iOS流媒體開發(fā)之一:總結系統(tǒng)提供的接口
<small> 本文參考了博文部分內容:AVPlayer 本地、網(wǎng)絡視頻播放相關 </small>
最近獨立完成了公司的一個電視直播和電臺直播的流媒體類項目,目前完成了第一版,已經上線,開發(fā)過程中收獲很多匠楚,準備寫一個流媒體系列博客總結和分享我的一些感悟。第一篇很簡單厂财,主要是總結下系統(tǒng)提供的常用的音視頻開發(fā)接口芋簿,屬于可以百度或者Google到一大堆資料的東西,幾個不支持流媒體播放的接口就不說了璃饱,一方面很簡單与斤,另一方面大多數(shù)項目用不到。了解這些技術的小伙伴就忽略吧荚恶,我這里只是簡單的總結下撩穿,沒有太多技術含量和價值。
預告
后續(xù)會分享有關自定義視頻播放器谒撼、M3U8下載食寡、M3U8回看等比較有難度的技術點,當然也不是隨便百度或者Google下就可以找到的東西廓潜,我就把這些技術點獻給簡書吧冻河。這里主要總結M3U8直播類的音視頻技術,至于MP4茉帅、3GP等等這些點播類的播放源都很簡單,無論下載還是播放都很容易實現(xiàn)锭弊,就不浪費簡書的服務器硬盤了堪澎。最近時間很緊,并且有一些技術點還需要繼續(xù)改進和優(yōu)化味滞,爭取盡快整理出比較靠譜的東西給大家樱蛤。
MPMoviePlayerController
簡介
MPMoviePlayerController既支持本地音視頻播放也支持網(wǎng)絡流媒體播放钮呀,功能已經十分完善了,流媒體項目常用的需求都可以滿足昨凡,比如播放爽醋、暫停,快進便脊、后退蚂四、監(jiān)聽播放器的播放狀態(tài)、截圖等功能哪痰,同時MPMoviePlayerController提供了一個簡單的全屏播放界面遂赠,可以輕松實現(xiàn)簡單的流媒體播放需求,如果需要深度自定義一個視頻播放器晌杰,可以將MPMoviePlayerController的view添加到自定義的控制器中跷睦,然后再講一些自定義的控件添加到相應view即可,代碼如下:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: myURL];
[player prepareToPlay];
[player.view setFrame: myView.bounds];
[myView addSubview: player.view];
[player play];-
總結
MPMoviePlayerController為音視頻開發(fā)提供了簡潔易用的接口肋演,小伙伴們自行查閱文檔即可抑诸,這里就不贅述了。需要注意的是在iOS9 以后MPMoviePlayerController被蘋果棄用了爹殊,取而代之的是AVPlayerViewController蜕乡,究其原因,iOS9以后iPad支持了畫中畫功能边灭,即使用畫中畫播放后异希,我們退出APP,視頻會在一個小的窗口繼續(xù)播放绒瘦,用戶可以在使用其他APP的同時觀看視頻称簿,AVPlayerViewController提供了完整的畫中畫技術支持,具體后面再講惰帽。畫中畫如下圖:
AVPlayer
-
簡介
如果我們只是開發(fā)只有音頻的流媒體項目憨降,MPMoviePlayerController足夠用了,可以滿足幾乎任何需求该酗,開發(fā)一個簡單的視頻播放器也足夠用授药。但是要深度自定義一個視頻播放器 使用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關聯(lián) 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); //創(chuàng)建一個UIView與AVPlayerLayer關聯(lián) 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]; }
真機運行圖如下:
-
總結
上面我們已經可以把展示我們視頻的view隨意處置了,可以為視頻播放增加常用的改變視頻比例睹晒、手勢改變音量趟庄、亮度等常用的功能括细。
注意
1、獲取AVPlayer的緩沖進度
通過監(jiān)聽loadedTimeRanges戚啥、status可以獲取緩沖進度和播放進度
代碼如下:
首先添加監(jiān)聽
[playItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
[playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
監(jiān)聽回調
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {AVPlayerItem *playerItem = (AVPlayerItem *)object; if ([keyPath isEqualToString:@"loadedTimeRanges"]){ //獲取緩沖進度 NSArray *loadedTimeRanges = [playerItem loadedTimeRanges]; // 獲取緩沖區(qū)域 CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; //開始的時間 NSTimeInterval startSeconds = CMTimeGetSeconds(timeRange.start); //表示已經緩沖的時間 NSTimeInterval durationSeconds = CMTimeGetSeconds(timeRange.duration); // 計算緩沖總時間 NSTimeInterval result = startSeconds + durationSeconds; NSLog(@"開始:%f,持續(xù):%f,總時間:%f", startSeconds, durationSeconds, result); NSLog(@"視頻的加載進度是:%%%f", durationSeconds / self.total * 100); }else if ([keyPath isEqualToString:@"status"]){ //獲取播放狀態(tài) if (playerItem.status == AVPlayerItemStatusReadyToPlay){ NSLog(@"準備播放"); //獲取視頻的總播放時長 [self.avPlayer play]; self.total = CMTimeGetSeconds(self.avPlayer.currentItem.duration); } else{ NSLog(@"播放失敗"); } } }
控制臺輸出結果
開始:0.000000,持續(xù):0.325000,總時間:0.325000 視頻的加載進度是:%inf 準備播放 開始:0.000000,持續(xù):18.203000,總時間:18.203000 視頻的加載進度是:%12.227365 開始:0.000000,持續(xù):35.108000,總時間:35.108000 視頻的加載進度是:%23.582833 開始:0.000000,持續(xù):43.119000,總時間:43.119000 視頻的加載進度是:%28.964002 開始:0.000000,持續(xù):46.068000,總時間:46.068000 視頻的加載進度是:%30.944912 開始:0.000000,持續(xù):62.160000,總時間:62.160000 視頻的加載進度是:%41.754270 開始:0.000000,持續(xù):73.283000,總時間:73.283000 視頻的加載進度是:%49.225840 開始:0.000000,持續(xù):80.064000,總時間:80.064000 視頻的加載進度是:%53.780790 開始:0.000000,持續(xù):94.298000,總時間:94.298000 視頻的加載進度是:%63.342088 開始:0.000000,持續(xù):101.682000,總時間:101.682000 視頻的加載進度是:%68.302087 開始:0.000000,持續(xù):106.977000,總時間:106.977000 視頻的加載進度是:%71.858858 開始:0.000000,持續(xù):117.775000,總時間:117.775000 視頻的加載進度是:%79.112117 開始:0.000000,持續(xù):132.172000,總時間:132.172000 視頻的加載進度是:%88.782906 開始:0.000000,持續(xù):148.871000,總時間:148.871000 視頻的加載進度是:%100.000000
從控制臺輸出的結果可以很清楚的看到視頻加載的進度奋单,這里有幾點需要注意
1、只有播放狀態(tài)變成AVPlayerItemStatusReadyToPlay時才可以獲取視頻播放的總時間猫十,提前獲取無效; 2览濒、我們判斷緩沖進度是靠比較視頻總時長self.total和已經緩沖的總時間durationSeconds作比較,如果二者相等即達到100%視頻則加載完成炫彩,這里需要注意匾七,有時這2個值在浮點數(shù)下不一定相同,有可能出現(xiàn)99.990836%這樣的情況江兢,但是視頻實際上已經加載完成昨忆,如果我們硬性的憑借100%判斷會出現(xiàn)有的視頻永遠加載不完的假象,因此在判斷的時候應該設置一個誤差值杉允,比如緩沖進度>99.95%就認為是加載完成了邑贴,具體數(shù)值可以根據(jù)項目自行設定一個合理的誤差; 3叔磷、這里顯示的所有數(shù)據(jù)拢驾,比如視頻的總時長、進度都是點播類播放源改基,非M3U8繁疤,M3U8是直播無法獲取總時長數(shù)據(jù),開始時間和緩沖時間等數(shù)據(jù)也沒有參考價值秕狰,項目中的直播視頻也不會涉及到這些稠腊。
2、獲取AVPlayer當前的播放進度
CMTime ctime = self.avPlayer.currentTime;
CGFloat currentTimeSec = ctime.value / ctime.timescale;
有了當前的播放進度鸣哀,視頻的進度條功能就可以完成架忌,很簡單,這里就不贅述了我衬。
3叹放、關于AVPlayer播放卡頓時如何獲取此時的狀態(tài)也是我遇到的難題,因為AVPlayer并沒有給出這種狀態(tài)挠羔,有人說根據(jù)AVPlayer的rate是1還是0判斷井仰,經測試這個方法不靠譜,即使卡頓時rate有時還是1破加,文章開頭連接的博文給出了一種解決辦法俱恶,可以參考下,這里大家有什么好的方法歡迎留言,感激不盡速那。
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(upadte)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
實現(xiàn)update方法
- (void)upadte
{
NSTimeInterval current = CMTimeGetSeconds(self.avPlayer.currentTime);
if (current!=self.lastTime) {
//沒有卡頓
NSLog(@"沒有卡頓");
}else{
//卡頓了
NSLog(@"卡頓了");
}
self.lastTime = current;
}
AVPlayerViewController
- 簡介
AVPlayerViewController是iOS9以后推出的新接口,這個接口在我看來主要就是為了iPad的畫中畫尿背,通過AVPlayerViewControllerDelegate實現(xiàn)端仰,沒有太多可以講的,AVPlayerViewController的使用與AVPlayer基本一樣田藐,這里給簡單的使用實例看下就可以了荔烧,其余的用法參考AVPlayer。
AVPlayerViewController示例
尾巴
這里只總結了MPMoviePlayerController汽久、AVPlayer鹤竭、AVPlayerViewController3個開發(fā)流媒體常用的接口,在開發(fā)中如何選擇呢景醇,我總結了一些經驗臀稚,僅供參考。
1三痰、如果是純音頻的流媒體項目吧寺,并且最低支持的版本在iOS9以下,使用MPMoviePlayerController最合適散劫,音頻不需要考慮MPMoviePlayerController本身自定義的界面稚机,只需要播放聲音,界面我們自己布局就可以了获搏,并且獲取播放的各種狀態(tài)也要比其余兩種方便赖条。
2、有視頻需求的流媒體項目常熙,如果只是簡單的視頻需求可以使用MPMoviePlayerController纬乍,如果要深度自定義視頻播放器,建議使用AVPlayerViewController症概,用法和AVPlayer基本是一樣的蕾额,但是AVPlayerViewController的好處是可以在后續(xù)方便的實現(xiàn)畫中畫功能,后續(xù)蘋果在更新時接口時也會為AVPlayerViewController提供更多有用方便的方法和屬性彼城,方便開發(fā)者使用诅蝶。