您好,登錄后才能下訂單哦!
iOS中怎么實(shí)現(xiàn)視頻播放,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
1.播放視頻
MPMoviewPlayerController繼承于NSObject,使用它播放視頻需要將其自帶的視頻View添加到視圖控制器的View上才能顯示視頻,使用步驟如下:
第一步:引用MediaPlayer框架,聲明視圖控制器屬性PlayerController #import <mediaplayer mediaplayer.h> @property(nonatomic,strong)MPMoviePlayerController *playerController;
//第二步:獲取視頻路徑,創(chuàng)建播放器 //本地視頻路徑 NSString* localFilePath=[[NSBundle mainBundle]pathForResource:@"不能說(shuō)的秘密" ofType:@"mp4"]; NSURL *localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //網(wǎng)絡(luò)視頻路徑 NSString *webVideoPath = @"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1115.mp4"; NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath]; self.playerController =[[MPMoviePlayerController alloc]initWithContentURL:webVideoUrl];
//第三步:設(shè)置Frame將播放器View添加到視圖控制器View上 self.playerController.view.frame = CGRectMake(0, 10, kDeviceWidth, 300); [self.view addSubview: self.playerController.view];
//第四步:設(shè)置播放器屬性 //設(shè)置控制面板風(fēng)格:無(wú),嵌入,全屏,默認(rèn) self.playerController.controlStyle = MPMovieControlStyleDefault; //設(shè)置是否自動(dòng)播放(默認(rèn)為YES) self.playerController.shouldAutoplay = NO; //設(shè)置播放器顯示模式,類似于圖片的處理,設(shè)置Fill有可能造成部分區(qū)域被裁剪 self.playerController.scalingMode = MPMovieScalingModeAspectFit; //設(shè)置重復(fù)模式 self.playerController.repeatMode = MPMovieRepeatModeOne;
//第五步:播放視頻 //播放前的準(zhǔn)備,會(huì)中斷當(dāng)前正在活躍的音頻會(huì)話 [ self.playerController prepareToPlay]; //播放視頻,設(shè)置了自動(dòng)播放之后可以不調(diào)用此方法 //[ self.playerController play];
//第六步:在退出界面的時(shí)候,關(guān)閉播放器,移除通知 - (void)dealloc{ //當(dāng)前視圖控制器pop之后并不會(huì)關(guān)閉播放,需要手動(dòng)關(guān)閉 [self.playerController stop]; self.playerController = nil; //移除播放器相關(guān)的通知 [[NSNotificationCenter defaultCenter] removeObserver:self]; }
2.視頻播放相關(guān)的通知
MPMoviePlayerController有關(guān)視頻播放的很多狀態(tài)控制都是通過(guò)通知完成的,尤其是播放在線視頻的時(shí)候,我們不僅監(jiān)控視頻加載是否成功,也會(huì)監(jiān)控是視頻緩存進(jìn)度等。這里演示一些常用的通知如下:
//關(guān)于通知的使用(還有很多通知可以監(jiān)聽,可查看SDK) NSNotificationCenter *notificaionCenter = [NSNotificationCenter defaultCenter]; //監(jiān)聽播放器狀態(tài)的變化 [notificaionCenter addObserver:self selector:@selector(playerStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; //監(jiān)聽播放完成 [notificaionCenter addObserver:self selector:@selector(playerFinished) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; //監(jiān)聽切換到全屏 [notificaionCenter addObserver:self selector:@selector(palyerChangeFullScreen) name:MPMoviePlayerDidEnterFullscreenNotification object:nil]; //監(jiān)聽截屏操作完成 [notificaionCenter addObserver:self selector:@selector(playerCaptureFinished:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:nil];
#pragma mark - 監(jiān)聽通知的響應(yīng)方法 //播放狀態(tài)變化,注意播放完成時(shí)的狀態(tài)是暫停 - (void)playerStateChanged:(NSNotification *)notificaion{ switch (self.playerController.playbackState) { case MPMoviePlaybackStateStopped:{ NSLog(@"播放停止"); break; } case MPMoviePlaybackStatePlaying:{ NSLog(@"播放器正在播放"); break; } case MPMoviePlaybackStatePaused:{ NSLog(@"播放器暫停"); break; } case MPMoviePlaybackStateInterrupted:{ NSLog(@"播放器中斷"); break; } case MPMoviePlaybackStateSeekingForward:{ NSLog(@"播放器快進(jìn)"); break; } case MPMoviePlaybackStateSeekingBackward:{ NSLog(@"播放器快退"); break; } default: break; } } //視頻播放結(jié)束 - (void)playerFinished{ NSLog(@"playerFinished:播放結(jié)束"); } //播放器切換到了全屏 - (void)palyerChangeFullScreen{ NSLog(@"palyerChangeFullScreen:播放器進(jìn)入全屏"); } //播放器截屏結(jié)束 - (void)playerCaptureFinished:(NSNotification *)notification{ //獲取并顯示截圖 UIImage *image=notification.userInfo[MPMoviePlayerThumbnailImageKey]; self.captureImgView.image = image; }
3.實(shí)現(xiàn)截屏
//添加一個(gè)按鈕,點(diǎn)擊開始截屏 _captureBtn = [[UIButton alloc] initWithFrame:CGRectMake(30, CGRectGetMaxY(self.playerController.view.frame) + 30, kDeviceWidth - 30 * 2, 50)]; _captureBtn.backgroundColor = [UIColor purpleColor]; [_captureBtn setTitle:@"截圖當(dāng)前屏幕" forState: UIControlStateNormal]; [_captureBtn addTarget:self action:@selector(captureCurrentScreenImg) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_captureBtn]; //添加一個(gè)ImgView 顯示截屏后的圖片 _captureImgView = [[UIImageView alloc] initWithFrame:CGRectMake((kDeviceWidth - 150)/2, CGRectGetMaxY(_captureBtn.frame) + 20, 150, 150)]; _captureImgView.contentMode = UIViewContentModeScaleAspectFit; _captureImgView.backgroundColor = [UIColor grayColor]; [self.view addSubview:_captureImgView];
//截取當(dāng)前屏幕 - (void)captureCurrentScreenImg{ [self.playerController requestThumbnailImagesAtTimes:@[@(self.playerController.currentPlaybackTime)] timeOption:MPMovieTimeOptionNearestKeyFrame]; }
//監(jiān)聽通知:播放器截屏結(jié)束,得到圖片并顯示截圖 - (void)playerCaptureFinished:(NSNotification *)notification{ UIImage *image=notification.userInfo[MPMoviePlayerThumbnailImageKey]; self.captureImgView.image = image; }
MPMovicePlayerViewControlle只能全屏幕播放視頻,它是一個(gè)包含了MPMoviePlayerController類型屬性的特殊視圖控制器,因此它是通過(guò)模態(tài)視圖彈出的方式顯示視頻的。理解了這個(gè),我們就可以知道在使用MPMovicePlayerViewController的時(shí)候我們可以通過(guò)它的MPMoviePlayerController屬性設(shè)置很多播放器的屬性了,具體用法和MPMoviePlayerController相同,就不過(guò)多的解釋了,播放視頻的代碼示例如下;
//第一步:獲取視頻路徑 //本地視頻 NSString* localFilePath=[[NSBundle mainBundle]pathForResource:@"不能說(shuō)的秘密" ofType:@"mp4"]; NSURL *localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //在線視頻 //NSString *webVideoPath = @"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1115.mp4"; //NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath]; //第二步:創(chuàng)建視頻播放器 MPMoviePlayerViewController *playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:localVideoUrl]; //第三步:設(shè)置播放器屬性 //通過(guò)moviePlayer屬性設(shè)置播放器屬性(與MPMoviePlayerController類似) playerViewController.moviePlayer.scalingMode = MPMovieScalingModeFill; //第四步:跳轉(zhuǎn)視頻播放界面 [self presentViewController:playerViewController animated:YES completion:nil];
AVPlayer相比上述兩種方式,播放視頻功能更加強(qiáng)大,使用也十分靈活,因?yàn)樗咏咏讓印5茿VPlayer本身是不能直接顯示視頻的,必須創(chuàng)建一個(gè)播放層AVPlayerLayer并將其添加到其他的視圖Layer上才能顯示。
1. 使用AVPlayer需要了解的常用類
AVAsset:一個(gè)用于獲取多媒體信息的抽象類,但不能直接使用
AVURLAsset:AVAsset的子類,可以根據(jù)一個(gè)URL路徑創(chuàng)建一個(gè)包含媒體信息的AVURLAsset對(duì)象
AVPlayerItem:一個(gè)媒體資源管理對(duì)象,用于管理視頻的基本信息和狀態(tài),一個(gè)AVPlayerItem對(duì)應(yīng)一個(gè)視頻資源
AVPlayer:負(fù)責(zé)視頻播放、暫停、時(shí)間控制等操作
AVPlayerLayer:負(fù)責(zé)顯示視頻的圖層,如果不設(shè)置此屬性,視頻就只有聲音沒(méi)有圖像
2. AVPlayer的使用步驟
//第一步:引用AVFoundation框架,添加播放器屬性 #import <AVFoundation/AVFoundation.h> @property (nonatomic,strong)AVPlayer *player;//播放器對(duì)象 @property (nonatomic,strong)AVPlayerItem *currentPlayerItem;
//第二步:獲取播放地址URL //本地視頻路徑 NSString* localFilePath=[[NSBundle mainBundle]pathForResource:@"不能說(shuō)的秘密" ofType:@"mp4"]; NSURL *localVideoUrl = [NSURL fileURLWithPath:localFilePath]; //網(wǎng)絡(luò)視頻路徑 NSString *webVideoPath = @"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1129.mp4"; NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath];
//第三步:創(chuàng)建播放器(四種方法) //如果使用URL創(chuàng)建的方式會(huì)默認(rèn)為AVPlayer創(chuàng)建一個(gè)AVPlayerItem //self.player = [AVPlayer playerWithURL:localVideoUrl]; //self.player = [[AVPlayer alloc] initWithURL:localVideoUrl]; //self.player = [AVPlayer playerWithPlayerItem:playerItem]; AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:webVideoUrl]; self.currentPlayerItem = playerItem; self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
//第四步:創(chuàng)建顯示視頻的AVPlayerLayer,設(shè)置視頻顯示屬性,并添加視頻圖層 //contentView是一個(gè)普通View,用于放置視頻視圖 /* AVLayerVideoGravityResizeAspectFill等比例鋪滿,寬或高有可能出屏幕 AVLayerVideoGravityResizeAspect 等比例 默認(rèn) AVLayerVideoGravityResize 完全適應(yīng)寬高 */ AVPlayerLayer *avLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; avLayer.videoGravity = AVLayerVideoGravityResizeAspect; avLayer.frame = _containerView.bounds; [_containerView.layer addSublayer:avLayer];
//第六步:執(zhí)行play方法,開始播放 //本地視頻可以直接播放 //網(wǎng)絡(luò)視頻需要監(jiān)測(cè)AVPlayerItem的status屬性為AVPlayerStatusReadyToPlay時(shí)方法才會(huì)生效 [self.player play];
3. 添加屬性觀察
一個(gè)AVPlayerItem對(duì)象對(duì)應(yīng)著一個(gè)視頻,我們需要通過(guò)AVPlayerItem來(lái)獲取視頻屬性。但是AVPlayerItem必須是在視頻資源加載到可以播放的時(shí)候才能使用,這是受限于網(wǎng)絡(luò)的原因。解決這一問(wèn)題,我們需要使用KVO監(jiān)測(cè)AVPlayerItem的status屬性,當(dāng)其為AVPlayerItemStatusReadyToPlay的時(shí)候我們才能獲取視頻相關(guān)屬性。相關(guān)的代碼示例如下:
//1.注冊(cè)觀察者,監(jiān)測(cè)播放器屬性 //觀察Status屬性,可以在加載成功之后得到視頻的長(zhǎng)度 [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //觀察loadedTimeRanges,可以獲取緩存進(jìn)度,實(shí)現(xiàn)緩沖進(jìn)度條 [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//2.添加屬性觀察 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { AVPlayerItem *playerItem = (AVPlayerItem *)object; if ([keyPath isEqualToString:@"status"]) { //獲取playerItem的status屬性最新的狀態(tài) AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; switch (status) { case AVPlayerStatusReadyToPlay:{ //獲取視頻長(zhǎng)度 CMTime duration = playerItem.duration; //更新顯示:視頻總時(shí)長(zhǎng)(自定義方法顯示時(shí)間的格式) self.totalNeedPlayTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(duration)]; //開啟滑塊的滑動(dòng)功能 self.sliderView.enabled = YES; //關(guān)閉加載Loading提示 [self showaAtivityInDicatorView:NO]; //開始播放視頻 [self.player play]; break; } case AVPlayerStatusFailed:{//視頻加載失敗,點(diǎn)擊重新加載 [self showaAtivityInDicatorView:NO];//關(guān)閉Loading視圖 self.playerInfoButton.hidden = NO; //顯示錯(cuò)誤提示按鈕,點(diǎn)擊后重新加載視頻 [self.playerInfoButton setTitle:@"資源加載失敗,點(diǎn)擊繼續(xù)嘗試加載" forState: UIControlStateNormal]; break; } case AVPlayerStatusUnknown:{ NSLog(@"加載遇到未知問(wèn)題:AVPlayerStatusUnknown"); break; } default: break; } } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { //獲取視頻緩沖進(jìn)度數(shù)組,這些緩沖的數(shù)組可能不是連續(xù)的 NSArray *loadedTimeRanges = playerItem.loadedTimeRanges; //獲取最新的緩沖區(qū)間 CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; //緩沖區(qū)間的開始的時(shí)間 NSTimeInterval loadStartSeconds = CMTimeGetSeconds(timeRange.start); //緩沖區(qū)間的時(shí)長(zhǎng) NSTimeInterval loadDurationSeconds = CMTimeGetSeconds(timeRange.duration); //當(dāng)前視頻緩沖時(shí)間總長(zhǎng)度 NSTimeInterval currentLoadTotalTime = loadStartSeconds + loadDurationSeconds; //NSLog(@"開始緩沖:%f,緩沖時(shí)長(zhǎng):%f,總時(shí)間:%f", loadStartSeconds, loadDurationSeconds, currentLoadTotalTime); //更新顯示:當(dāng)前緩沖總時(shí)長(zhǎng) _currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime]; //更新顯示:視頻的總時(shí)長(zhǎng) _totalNeedLoadTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(self.player.currentItem.duration)]; //更新顯示:緩沖進(jìn)度條的值 _progressView.progress = currentLoadTotalTime/CMTimeGetSeconds(self.player.currentItem.duration); } }
//轉(zhuǎn)換時(shí)間格式的方法 - (NSString *)formatTimeWithTimeInterVal:(NSTimeInterval)timeInterVal{ int minute = 0, hour = 0, secend = timeInterVal; minute = (secend % 3600)/60; hour = secend / 3600; secend = secend % 60; return [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, secend]; }
4. 獲取當(dāng)前播放時(shí)間與總時(shí)間
在此之前我們需要首先了解一個(gè)數(shù)據(jù)類型,也就是上述操作中的CMTime, 在AVPlayer的使用中我們會(huì)經(jīng)常用到它,其實(shí)CMTime是一個(gè)結(jié)構(gòu)體如下:
typedef struct{ CMTimeValue value; // 幀數(shù) CMTimeScale timescale; // 幀率(影片每秒有幾幀) CMTimeFlags flags; CMTimeEpoch epoch; } CMTi
在上面的操作中我們看到AVPlayerItem的Duration屬性就是一個(gè)CMTime類型的數(shù)據(jù)。所以獲取視頻的總時(shí)長(zhǎng)(秒)需要duration.value/duration.timeScale。當(dāng)然系統(tǒng)也為我們提供了CMTimeGetSeconds函數(shù)更加方便計(jì)算:
總時(shí)長(zhǎng): duration.value == CMTimeGetSeconds(duration) 。
在快進(jìn)視頻到某一個(gè)位置的時(shí)候我們也需要?jiǎng)?chuàng)建CMTime作為參數(shù),那么CMTime的創(chuàng)建方法有兩種:
//方法1: CMTimeMakeWithSeconds(Flout64 seconds, int32_t scale) //方法2: CMTimeMake(int64_t value, int32_t scale) //注:兩者的區(qū)別在于方法一的第一個(gè)參數(shù)可以是float
至于獲取視頻的總時(shí)間在上述代碼中已有體現(xiàn),是在檢測(cè)播放狀態(tài)變?yōu)锳VPlayerStatusReadyToPlay的時(shí)候獲取的
//視頻總時(shí)長(zhǎng),在AVPlayerItem狀態(tài)為AVPlayerStatusReadyToPlay時(shí)獲取 CMTime duration = self.player.currentItem.duration; CGFloat totalTime = CMTimeGetSeconds(duration); //當(dāng)前AVPlayer的播放時(shí)長(zhǎng) CMTime cmTime = self.player.currentTime; CGFloat currentTime = CMTimeGetSeconds(cmTime);
5. 播放進(jìn)度與狀態(tài)的刷新
實(shí)時(shí)更新當(dāng)前播放時(shí)間,這時(shí)候我們不必使用定時(shí)器,因?yàn)锳VPlayer已經(jīng)提供了方法:
addPeriodicTimeObserverForInterval: queue: usingBlock。當(dāng)播放進(jìn)度改變的時(shí)候方法中的回調(diào)會(huì)被執(zhí)行。我們可以在這里做刷新時(shí)間的操作,代碼示例如下:
__weak __typeof(self) weakSelf = self; [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { //當(dāng)前播放的時(shí)間 NSTimeInterval currentTime = CMTimeGetSeconds(time); //視頻的總時(shí)間 NSTimeInterval totalTime = CMTimeGetSeconds(weakSelf.player.currentItem.duration); //設(shè)置滑塊的當(dāng)前進(jìn)度 weakSelf.sliderView.value = currentTime/totalTime; //設(shè)置顯示的時(shí)間:以00:00:00的格式 weakSelf.currentTimeLabel.text = [weakSelf formatTimeWithTimeInterVal:currentTime]; }];
6. 滑塊拖拽修改視頻播放進(jìn)度
//UISlider的響應(yīng)方法:拖動(dòng)滑塊,改變播放進(jìn)度 - (IBAction)sliderViewChange:(id)sender { if(self.player.status == AVPlayerStatusReadyToPlay){ NSTimeInterval playTime = self.sliderView.value * CMTimeGetSeconds(self.player.currentItem.duration); CMTime seekTime = CMTimeMake(playTime, 1); [self.player seekToTime:seekTime completionHandler:^(BOOL finished) { }]; } }
AVPlayerViewController是iOS8新增視頻框架AVKit中的一個(gè)播放器類。由于iOS9棄用前兩種播放器類的原因,AVPlayerViewController也將變得更加常用。AVPlayerViewController適合開發(fā)播放界面要求不是很高的應(yīng)用。其相比AVPlayer的使用更加方便,但是原理上還是AVPlayerViewController包含了一個(gè)AVPlayer對(duì)象。
AVPlayerViewController有兩種播放視頻的方式:
第一種:直接彈出模態(tài)視圖控制器播放
//步驟1:獲取視頻路徑 NSString *webVideoPath = @"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1213.mp4"; NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath]; //步驟2:創(chuàng)建AVPlayer AVPlayer *avPlayer = [[AVPlayer alloc] initWithURL:webVideoUrl]; //步驟3:使用AVPlayer創(chuàng)建AVPlayerViewController,并跳轉(zhuǎn)播放界面 AVPlayerViewController *avPlayerVC =[[AVPlayerViewController alloc] init]; avPlayerVC.player = avPlayer; [self presentViewController:avPlayerVC animated:YES completion:nil];
第二種:添加AVPlayerViewController的View到父視圖上播放。
使用這種方式播放的優(yōu)點(diǎn)在于可以指定播放界面的原始尺寸大小,但是值得注意的是AVPlayerViewController必須被當(dāng)前視圖控制器所持有,以防止被當(dāng)做局部變量被釋放。為了滿足這一條件,我們可以將AVPlayerViewController作為屬性,也可以使用addChildViewController方法將其作為當(dāng)前視圖控制器的子視圖控制器,示例代碼如下:
//步驟1:獲取視頻路徑 NSString *webVideoPath = @"http://api.junqingguanchashi.net/yunpan/bd/c.php?vid=/junqing/1213.mp4"; NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath]; //步驟2:創(chuàng)建AVPlayer AVPlayer *avPlayer = [[AVPlayer alloc] initWithURL:webVideoUrl]; //步驟3:使用AVPlayer創(chuàng)建AVPlayerViewController,并跳轉(zhuǎn)播放界面 AVPlayerViewController *avPlayerVC =[[AVPlayerViewController alloc] init]; avPlayerVC.player = avPlayer; //步驟4:設(shè)置播放器視圖大小 avPlayerVC.view.frame = CGRectMake(25, 0, 320, 300); //特別注意:AVPlayerViewController不能作為局部變量被釋放,否則無(wú)法播放成功 //解決1.AVPlayerViewController作為屬性 //解決2:使用addChildViewController,AVPlayerViewController作為子視圖控制器 [self addChildViewController:avPlayerVC]; [self.view addSubview:avPlayerVC.view];
關(guān)于iOS中怎么實(shí)現(xiàn)視頻播放問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。