前言
標題必須要浮夸!要感覺像是一個大新聞舅巷。長者如是說羔味。
其實是前幾天去面試的時候,被要求說必須做過視頻播放相關(guān)項目钠右。有點鬧心之余赋元,就花了點時間在家寫了一個簡單播放器,基本實現(xiàn)了主流播放器的大致功能。之前項目沒有需求用到過視頻播放搁凸,所以寫的時候難免會遇到一些坑媚值,花了一些時間解決。
蘋果在視頻播放方面提供了多個框架供我們選擇使用护糖。分別為:
- 基于mediaPlayer類庫的MPMediaPlayerController(iOS9后遭到廢棄褥芒,被AVPlayerViewController所替代)
- 基于AVFounditon類庫的AVPlayer
- 基于AVKit類庫的AVPlayerViewController(iOS8后才可使用)
正文
AVPlayer與MPMediaPlayerController比較:
- AVplayer有更多的靈活性,當然嫡良,也需要你去自定義構(gòu)建UI锰扶。還有一大優(yōu)勢,例如其擴展的AVQueuePlayer皆刺,可以實現(xiàn)視頻無縫隊列播放少辣、多視頻同時播放、視頻轉(zhuǎn)換羡蛾、編解碼等功能漓帅。
- MPMediaPlayerController實際上是基于AVPlayer的簡單UI封裝,對于一般的播放要求痴怨,幾行代碼就可實現(xiàn)忙干,省心省事。
因為MPMediaPlayerController是對AVPlayer進行的單例封裝浪藻,所以不能進行多視頻播放捐迫。
播放器Demo(全屏)已實現(xiàn)功能點:
- push到播放器頁面,橫屏顯示爱葵。
- 單機隱藏or顯示上方標題欄與下方操作欄施戴。
- 呼出右側(cè)設(shè)置欄。
- 視頻播放操作與進度條設(shè)置萌丈。
- 在屏幕上左右拖動赞哗,進行視頻快進與快退。
- 在屏幕左側(cè)上下拖動辆雾,進行亮度調(diào)整肪笋。
- 在屏幕右側(cè)上下拖動,進行音量調(diào)整度迂。
想到但是暫未實現(xiàn)的功能點:(大多為優(yōu)化或與業(yè)務(wù)相關(guān))
- 屏幕或進度條拖動快進操作時藤乙,添加提示框進行快進時間的實時提示。
- 用戶無操作兩三秒之后自動隱藏上下View惭墓。
- 視頻清晰度調(diào)整按鈕坛梁。(更換視頻源)
- 操作加鎖按鈕。(加鎖后未進行解鎖操作之前不可進行操作)
- 彈幕相關(guān)腊凶。
- 用戶允許橫屏狀態(tài)下划咐,橫屏豎屏自動進行頁面切換與動畫效果等毅人。
- 網(wǎng)絡(luò)視頻的緩存、下載等尖殃。
- 軟硬解碼模式切換等。
筆者Demo選擇使用了AVPlayer進行視頻播放器的構(gòu)建划煮。由于UI的代碼實現(xiàn)送丰,加上略蛋疼的邏輯代碼,播放器頁面的代碼量達到400多行弛秋,之后有時間的話會再進行優(yōu)化器躏。這里只貼出部分代碼,想要查看或借鑒完整Demo蟹略,可以到本人github去下載登失。
使用AVPlayer構(gòu)建播放器
1.導入頭文件
#import <AVFoundation/AVFoundation.h>
2.其實沒什么可說的,很簡單挖炬,先初始化AVPlayer揽浙,然后添加到AVPlayerLayer,最后將其添加到視圖的layer層意敛。
#pragma mark - Demo中此視圖的屬性
#define TopViewHeight 55
#define BottomViewHeight 72
#define mainWidth [UIScreen mainScreen].bounds.size.width
#define mainHeight [UIScreen mainScreen].bounds.size.height
//上層建筑
@property (nonatomic,strong)UIView *topView;
@property (nonatomic,strong)UIButton *backBtn;
@property (nonatomic,strong)UILabel *titleLabel;
@property (nonatomic,strong)UIButton *settingsBtn;
//經(jīng)濟基礎(chǔ)
@property (nonatomic,strong)UIView *bottomView;
@property (nonatomic,strong)UIButton *playBtn;
@property (nonatomic,strong)UILabel *textLabel;
@property (nonatomic,assign)BOOL isPlay;
@property (nonatomic,strong)UISlider *movieProgressSlider;//進度條
@property (nonatomic,assign)CGFloat ProgressBeginToMove;
@property (nonatomic,assign)CGFloat totalMovieDuration;//視頻總時間
//核心軀干
@property (nonatomic,strong)AVPlayer *player;
//神之右手
@property (nonatomic,strong)UIView *settingsView;
@property (nonatomic,strong)UIView *rightView;
@property (nonatomic,strong)UIButton *setTestBtn;
//touch evens
@property (nonatomic,assign)BOOL isShowView;
@property (nonatomic,assign)BOOL isSettingsViewShow;
@property (nonatomic,assign)BOOL isSlideOrClick;
@property (nonatomic,strong)UISlider *volumeViewSlider;
@property (nonatomic,assign)float systemVolume;//系統(tǒng)音量值
@property (nonatomic,assign)float systemBrightness;//系統(tǒng)亮度
@property (nonatomic,assign)CGPoint startPoint;//起始位置坐標
@property (nonatomic,assign)BOOL isTouchBeganLeft;//起始位置方向
@property (nonatomic,copy)NSString *isSlideDirection;//滑動方向
@property (nonatomic,assign)float startProgress;//起始進度條
#pragma mark - 播放器軀干
- (void)createAvPlayer{
//設(shè)置靜音狀態(tài)也可播放聲音
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
CGRect playerFrame = CGRectMake(0, 0, self.view.layer.bounds.size.height, self.view.layer.bounds.size.width);
AVURLAsset *asset = [AVURLAsset assetWithURL: _url];
Float64 duration = CMTimeGetSeconds(asset.duration);
//獲取視頻總時長
_totalMovieDuration = duration;
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset: asset];
_player = [[AVPlayer alloc]initWithPlayerItem:playerItem];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
playerLayer.frame = playerFrame;
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.view.layer addSublayer:playerLayer];
//需要設(shè)置自動播放的直接play即可
//[_player play];
}
屏幕單擊手勢與視頻快進
屏幕單擊
1.符合條件的情況下(手指按下后離開屏幕馅巷,并且沒有拖動)通過BOOL值判斷,隱藏或顯示上下View
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint point = [[touches anyObject] locationInView:self.view];
if (_isShowView) {
//上下View為顯示狀態(tài),此時點擊上下View直接return
if ((point.y>CGRectGetMinY(self.topView.frame)&&point.y< CGRectGetMaxY(self.topView.frame))||(point.y<CGRectGetMaxY(self.bottomView.frame)&&point.y>CGRectGetMinY(self.bottomView.frame))) {
return
}
_isShowView = NO;
[UIView animateWithDuration:0.5 animations:^{
_topView.alpha = 0;
_bottomView.alpha = 0;
}];
}else{
_isShowView = YES;
[UIView animateWithDuration:0.5 animations:^{
_topView.alpha = 1;
_bottomView.alpha = 1;
}];
}
}
2.右側(cè)View顯示的狀態(tài)下草姻,點擊屏幕左半空白區(qū)域钓猬,隱藏右側(cè)View
if (_isSettingsViewShow) {
if (point.x>CGRectGetMinX(_rightView.frame)&&point.x< CGRectGetMaxX(_rightView.frame)) {
return;
}
_settingsView.alpha = 0;
_isSettingsViewShow = NO;
}
拖動快進
1.計算后得出拖動方向為橫向拖動。
CGPoint location = [[touches anyObject] locationInView:self.view];
CGFloat changeY = location.y - _startPoint.y;
CGFloat changeX = location.x - _startPoint.x;
if(fabs(changeX) > fabs(changeY)){
_isSlideDirection = @"橫向";//設(shè)置為橫向
}else if(fabs(changeY)>fabs(changeX)){
_isSlideDirection = @"縱向";//設(shè)置為縱向
}else{
_isSlideOrClick = NO;
NSLog(@"不在五行中撩独。");
}
2.根據(jù)手指按下與離開屏幕后敞曹,橫向位移的坐標值,對視頻播放進度進行刷新综膀。
if (_isSlideOrClick) {
_isSlideDirection = @"";
_isSlideOrClick = NO;
CGFloat changeY = point.y - _startPoint.y;
CGFloat changeX = point.x - _startPoint.x;
//如果位置改變 刷新進度條
if(fabs(changeX) > fabs(changeY)){
[self scrubberIsScrolling];
}
return;
}
//拖動進度條
-(void)scrubberIsScrolling{
//計算出拖動的當前秒數(shù)(總長*當前百分比)
NSInteger dragedSeconds = floorf(_totalMovieDuration * _movieProgressSlider.value);
CMTime newCMTime = CMTimeMake(dragedSeconds, 1);
[_player seekToTime:newCMTime completionHandler:^(BOOL finished) {
[_player play];
[_playBtn setTitle:@"暫停" forState:UIControlStateNormal];
}];
}
MPMediaPlayerController與AVPlayerViewController的使用介紹
MPMediaPlayerController與AVPlayerViewController澳迫,兩者都是基于AVPlayer的簡單UI封裝,如果只是需要簡單的視頻播放功能僧须,可以使用這兩個類快速的構(gòu)建視頻播放器纲刀。
MPMediaPlayerController
1.導入頭文件
#import <MediaPlayer/MediaPlayer.h>
2.初始化mp,幾行代碼既可以實現(xiàn)担平。
@property (nonatomic,strong)MPMoviePlayerController *mp;
NSURL *url1 = [[NSBundle mainBundle]URLForResource:@"chenyifaer" withExtension:@"mp4"];
_mp = [[MPMoviePlayerController alloc] initWithContentURL:url1];
_mp.controlStyle = MPMovieControlStyleNone;
_mp.view.frame = CGRectMake(0, 0, self.view.layer.bounds.size.height, self.view.layer.bounds.size.width);
[self.view addSubview:_mp.view];
[_mp play];
controlStyle屬性有三個值:
- MPMovieControlStyleNone, //無控制
- MPMovieControlStyleEmbedded, //有全屏按鈕與控制
- MPMovieControlStyleFullscreen, // 默認全屏示绊,有退出和控制
當然還有一些其他屬性,有需要可以自行進行設(shè)置暂论。
AVPlayerViewController
1.導入框架與頭文件
#import <AVKit/AVKit.h>
2.初始化AVPlayerViewController,創(chuàng)建一個AVPlayer添加上面褐。然后將其添加到視圖上,再將View添加到self.View上取胎,然后play即可
NSURL *url1 = [[NSBundle mainBundle]URLForResource:@"chenyifaer" withExtension:@"mp4"];
AVPlayer * player = [AVPlayer playerWithURL:url1];
AVPlayerViewController *playerController = [[AVPlayerViewController alloc]init];
playerController.player = player;
[self addChildViewController:playerController];
[self.view addSubview:playerController.view];
playerController.view.frame = CGRectMake(0, 0, self.view.layer.bounds.size.height, self.view.layer.bounds.size.width);
[player play];
同樣幾行代碼展哭,即可實現(xiàn)湃窍。
音量調(diào)整
1.導入頭文件
#import <MediaPlayer/MediaPlayer.h>
2.借助MPVolumeView類來獲取到其音量進度條,進而進行音量獲取與控制
@property (nonatomic,strong)UISlider *movieProgressSlider;//進度條
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
_volumeViewSlider = nil;
for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
_volumeViewSlider = (UISlider *)view;
break;
}
3.觸摸屏幕時匪傍,記錄手指按下的位置您市、獲取按下時系統(tǒng)的音量(實現(xiàn)touchesBegan方法)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
_startProgress = _movieProgressSlider.value;
}
4.手指在規(guī)定行為下(手指按下位置為視圖右半?yún)^(qū),且縱向滑動)持續(xù)滑動時役衡,動態(tài)改變系統(tǒng)音量(實現(xiàn)touchesMoved方法)
//手指持續(xù)滑動茵休,此方法會持續(xù)調(diào)用
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint location = [[touches anyObject] locationInView:self.view];
int index = location.y - _startPoint.y;
if(index>0){
[_volumeViewSlider setValue:_systemVolume - (abs(index)/10 * 0.05) animated:YES];
[_volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
}else{
[_volumeViewSlider setValue:_systemVolume + (abs(index)/10 * 0.05) animated:YES];
[_volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
亮度調(diào)整
1.觸摸屏幕時,記錄手指按下的位置手蝎、按下時屏幕的亮度(實現(xiàn)touchesBegan方法)
2.手指在規(guī)定行為下(手指按下位置為視圖左半?yún)^(qū)榕莺,且縱向滑動)持續(xù)滑動時,不斷動態(tài)處理(實現(xiàn)touchesMoved方法)
3.改變屏幕亮度:[UIScreen mainScreen].brightness = X (0~1);
//手指持續(xù)滑動棵介,此方法會持續(xù)調(diào)用
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint location = [[touches anyObject] locationInView:self.view];
int index = location.y - _startPoint.y;
if(index>0){
[UIScreen mainScreen].brightness = _systemBrightness - abs(index)/10 * 0.01;
}else{
_movieProgressSlider.value = _startProgress - abs(index)/10 * 0.008;
}
}
屏幕旋轉(zhuǎn)
1.設(shè)置應(yīng)用支持橫屏(默認支持)钉鸯。
2.在根視圖中設(shè)置默認豎屏(Nav、TabBar邮辽、VC基類)
- (BOOL)shouldAutorotate{
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
3.在需要橫屏的VC中重寫下列方法即可
//允許橫屏旋轉(zhuǎn)
- (BOOL)shouldAutorotate{
return YES;
}
//支持左右旋轉(zhuǎn)
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeRight|UIInterfaceOrientationMaskLandscapeLeft;
}
//默認為右旋轉(zhuǎn)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationLandscapeRight;
}
源碼
點此下載:github源碼
作者其他文章推薦:iOS 使用CIDetector掃描相冊二維碼唠雕、原生掃描
結(jié)語
怎么樣,實現(xiàn)一個簡單的視頻播放器是不是很簡單吨述。當然及塘,要做一個好的視頻播放器,需要更多的優(yōu)化處理锐极、UI處理笙僚、業(yè)務(wù)功能擴展、動畫效果....
筆者Demo中關(guān)于timer與進度條的互動之間不甚完美灵再,還有些小問題肋层。
總之,初次嘗試寫翎迁,水平有限栋猖,如有錯誤,還望指正汪榔。
參考:
1.http://www.techotopia.com/index.php/iOS8AVPlayerAndPlayerViewController
2.http://stackoverflow.com/questions/8146942/avplayer-and-mpmovieplayercontroller-differences
3.http://www.reibang.com/p/e64fe3c7f9ab
4.http://www.th7.cn/Program/IOS/201504/439086.shtml
其他小問題多在stackoverflow查詢解決蒲拉。
PS:最后吐槽一下。因為寫的稍微有點多痴腌,想做個快速電梯樹進行頁內(nèi)跳轉(zhuǎn)雌团,結(jié)果發(fā)現(xiàn)簡書竟然不支持<span id="jump">正文</span>這樣使用錨點進行頁內(nèi)跳轉(zhuǎn)的語法。簡直坑爹士聪。