iOS學習筆記26-視頻播放
一、視頻
在iOS中播放視頻可以使用兩個架構來實現:
1. MediaPlayer
架構的MPMoviePlayerController
和MPMoviePlayerViewController
2. AVFoundation
架構中的AVPlayer
3. AVKit
架構的AVPlayerViewController
【iOS8之後才有】
但在近兩年的WWDC上,MediaPlayer
架構被iOS9標記為deprcated
,意味著它已經不再被蘋果繼續維護,而且該框架組成度較高,不如AVFoundation
靈活性高,所以這裡就講AVFoundation
的AVPlayer
來實現播放視頻,AVPlayerViewController
實際上就是對AVPlayer
的封裝。
下面是兩個架構的應用所在層:
二、AVPlayer
AVPlayer
存在於AVFoundation
中,它更加接近於底層,所以靈活性極高。
AVPlayer
本身並不能顯示視頻,如果AVPlayer
要顯示必須建立一個播放器圖層AVPlayerLayer
用於展示,該播放器圖層繼承於CALayer
。
AVPlayer視頻播放使用步驟:建立視頻資源地址URL,可以是網路URL 通過URL建立視頻內容對象
AVPlayerItem
,一個視頻對應一個
AVPlayerItem
建立
AVPlayer
視頻播放器對象,需要一個
AVPlayerItem
進行初始化建立
AVPlayerLayer
播放圖層對象,添加到顯示視圖上去播放器播放
play
,播放器暫停
pause
添加通知中樞監聽視頻播放完成,使用KVO監聽播放內容的屬性變化進度條監聽是調用
AVPlayer
的對象方法:
-(id)addPeriodicTimeObserverForInterval:(CMTime)interval/*監聽頻率*/ queue:(dispatch_queue_t)queue /*監聽GCD線程*/ usingBlock:(void (^)(CMTime time))block;/*監聽回調*/
測試環境搭建:利用終端開啟Apache服務,使得手機可以通過網路訪問本機資源
下載視頻MP4到Apache的Web資來源目錄
預設的Apache的Web資來源目錄是
/Library/WebServer/Documents
查看本機伺服器的IP
別忘了進入info.plist設定HTTP網路解禁
下面是一個具體的項目:ViewController屬性
#import "ViewController.h"#import @interface ViewController ()@property (strong, nonatomic) AVPlayer *player;//視頻播放器@property (strong, nonatomic) AVPlayerLayer *playerLayer;//視頻播放圖層@property (strong, nonatomic) IBOutlet UIView *movieView;//播放[內容] 檢視@property (strong, nonatomic) IBOutlet UIProgressView *progressView;//進度條@property (strong, nonatomic) IBOutlet UISegmentedControl *segmentView;//選擇欄@property (strong, nonatomic) NSArray *playerItemArray;//視頻播放URL列表@end
1. 初始化AVPlayerItem視頻內容對象
/* 擷取播放內容對象,一個AVPlayerItem對應一個視頻檔案 */- (AVPlayerItem *)getPlayItemByNum:(NSInteger)num { if (num >= self.playerItemArray.count) { return nil; } //建立URL NSString *urlStr = self.playerItemArray[num]; urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:urlStr]; //建立播放內容對象 AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url]; return item;}
2. 初始化AVPlayer視頻播放器對象
/* 初始化視頻播放器 */- (void)initAVPlayer { //擷取播放內容 AVPlayerItem *item = [self getPlayItemByNum:0]; //建立視頻播放器 AVPlayer *player = [AVPlayer playerWithPlayerItem:item]; self.player = player; //添加播放進度監聽 [self addProgressObserver]; //添加播放內容KVO監聽 [self addObserverToPlayerItem:item]; //添加通知中樞監聽播放完成 [self addNotificationToPlayerItem];}
3. 初始化AVPlayerLayer播放圖層對象
#pragma mark - 初始化/* 初始化播放器圖層對象 */- (void)initAVPlayerLayer { //建立視頻播放器圖層對象 AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.player]; layer.frame = self.movieView.bounds;//尺寸大小 layer.videoGravity = AVLayerVideoGravityResizeAspect;//視頻填充模式 //添加進控制項圖層 [self.movieView.layer addSublayer:layer]; self.playerLayer = layer; self.movieView.layer.masksToBounds = YES;}
4. 通知中樞監聽播放完成
#pragma mark - 通知中樞- (void)addNotificationToPlayerItem { //添加通知中樞監聽視頻播放完成 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerDidFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];}- (void)removeNotificationFromPlayerItem { //移除通知中樞的通知 [[NSNotificationCenter defaultCenter] removeObserver:self];}/* 播放完成後會調用 */- (void)playerDidFinished:(NSNotification *)notification { //自動播放下一個視頻 NSInteger currentIndex = self.segmentView.selectedSegmentIndex; self.segmentView.selectedSegmentIndex = (currentIndex + 1)%self.playerItemArray.count; [self segmentValueChange:self.segmentView];}
5. KVO屬性監聽
#pragma mark - KVO監聽屬性/* 添加KVO,監聽播放狀態和緩衝載入狀況 */- (void)addObserverToPlayerItem:(AVPlayerItem *)item { //監控狀態屬性 [item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //監控緩衝載入情況屬性 [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];}/* 移除KVO */- (void)removeObserverFromPlayerItem:(AVPlayerItem *)item { [item removeObserver:self forKeyPath:@"status"]; [item removeObserver:self forKeyPath:@"loadedTimeRanges"];}/* 屬性發生變化,KVO響應函數 */- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ AVPlayerItem *playerItem = (AVPlayerItem *)object; if ([keyPath isEqualToString:@"status"]) {//狀態發生改變 AVPlayerStatus status = [[change objectForKey:@"new"] integerValue]; if (status == AVPlayerStatusReadyToPlay) { NSLog(@"現正播放..,視頻總長度為:%.2f",CMTimeGetSeconds(playerItem.duration)); } } else if ( [keyPath isEqualToString:@"loadedTimeRanges"] ) {//緩衝區域變化 NSArray *array = playerItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//已緩衝範圍 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩衝總長度 NSLog(@"共緩衝:%.2f",totalBuffer); }}
6. 進度條監聽
#pragma mark - 進度監聽- (void)addProgressObserver { AVPlayerItem *item = self.player.currentItem; UIProgressView *progress = self.progressView; //進度監聽 [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { //CMTime是表示視頻時間資訊的結構體,包含視頻時間點、每秒畫面格數等資訊 //擷取當前播放至的秒數 float current = CMTimeGetSeconds(time); //擷取視頻總播放秒數 float total = CMTimeGetSeconds(item.duration); if (current) { [progress setProgress:(current/total) animated:YES]; } }];}
7. UI點擊事件以及視圖控制器載入
- (void)viewDidLoad { [super viewDidLoad]; //屬性初始化 self.segmentView.selectedSegmentIndex = 0; self.progressView.progress = 0; self.playerItemArray = @[@"http://192.168.6.147/1.mp4", @"http://192.168.6.147/2.mp4", @"http://192.168.6.147/3.mp4"]; //視頻播放器初始化 [self initAVPlayer]; //視頻播放器顯示圖層初始化 [self initAVPlayerLayer]; //視頻開始播放 [self.player play];}- (void)dealloc { //移除監聽和通知 [self removeObserverFromPlayerItem:self.player.currentItem]; [self removeNotificationFromPlayerItem];}#pragma mark UI點擊/* 點擊播放按鈕 */- (IBAction)playMovie:(UIButton *)sender { sender.enabled = NO; if ( self.player.rate == 0 ) {//播放速度為0,表示播放暫停 sender.titleLabel.text = @"暫停"; [self.player play];//啟動播放 } else if ( self.player.rate == 1.0 ) {//播放速度為1.0,表示現正播放 sender.titleLabel.text = @"播放"; [self.player pause];//暫停播放 } sender.enabled = YES;}/* 選擇視頻播放清單 */- (IBAction)segmentValueChange:(UISegmentedControl *)sender { //先移除對AVPlayerItem的所有監聽 [self removeNotificationFromPlayerItem]; [self removeObserverFromPlayerItem:self.player.currentItem]; //擷取新的播放內容 AVPlayerItem *playerItem = [self getPlayItemByNum:sender.selectedSegmentIndex]; //添加屬性監聽 [self addObserverToPlayerItem:playerItem]; //替換視頻內容 [self.player replaceCurrentItemWithPlayerItem:playerItem]; //添加播放完成監聽 [self addNotificationToPlayerItem];}
三、AVPlayerViewController
一個簡單的視頻播放器就這麼搞定了,感覺還是好麻煩,而且很多功能還沒有實現。
實際上在iOS8.0之後,蘋果為我們封裝了AVPlayer
等視頻播放相關的類 ,形成了一個直接可以簡單使用的播放器控制器類,那就是AVPlayerViewController
,下面來講下你就覺得有多爽,上面那一大堆,只需要下面的一小塊代碼就可以實現了。
使用步驟:匯入架構:
添加標頭檔:
#import #import
建立
URL
建立
AVPlayer
建立
AVPlayerViewController
Over,一個功能十分齊全的播放器就好了
下面是全部代碼【/(ㄒoㄒ)/~~淚奔】:
#import "ViewController.h"#import #import @interface ViewController ()@property (strong, nonatomic) AVPlayerViewController *playerVC;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //建立URL NSURL *url = [NSURL URLWithString:@"http://192.168.6.147/1.mp4"]; //直接建立AVPlayer,它內部也是先建立AVPlayerItem,這個只是快捷方法 AVPlayer *player = [AVPlayer playerWithURL:url]; //建立AVPlayerViewController控制器 AVPlayerViewController *playerVC = [[AVPlayerViewController alloc] init]; playerVC.player = player; playerVC.view.frame = self.view.frame; [self.view addSubview:playerVC.view]; self.playerVC = playerVC; //調用控制器的屬性player的開始播放方法 [self.playerVC.player play];}@end
這酸爽不敢相信,不過這個是iOS9才有的,就是為了替代
MediaPlayer
架構的MPMoviePlayerViewController
而定製的非常方便的視頻播放濃ky"http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vc8YnIgLz4NCs7S08M8Y29kZT5BVlBsYXllcjwvY29kZT7QtLXEytPGtbKlt8XG97G7y6bBy7rDvLjKrsz1vdajrC8oqNJvqNIpL35+oaM8L3A+DQo8aDMgaWQ9"四擴充產生影片縮圖">四、擴充–產生影片縮圖
AVFoundation
架構還提供了一個類AVAssetImageGenerator
,用於擷取視頻。
應用情境:播放視頻時,拖動進度條時,可以顯示影片縮圖,查看視頻播放至哪個畫面了選擇某個視頻播放的時候,可以使用影片縮圖,點擊視頻縮放圖,進入真正的播放視頻介面一些有意思的視頻情境需要截屏留念的時候,可以使用影片縮圖具體使用步驟:建立
AVURLAsset
對象,該對象主要用於擷取媒體資訊,包括視頻、聲音。根據
AVURLAsset
建立
AVAssetImageGenerator
對象使用對象方法
copyCGImageAtTime:
獲得指定時間點的
-(CGImageRef)copyCGImageAtTime:(CMTime)requestedTime /* 要在視頻的哪個時間點產生縮圖 */ actualTime:(CMTime *)actualTime /* 實際產生縮圖的媒體時間 */ error:(NSError **)outError;/* 錯誤資訊 */
下面是實際代碼:
/* 擷取影片縮圖 */- (UIImage *)getThumbailImageRequestAtTimeSecond:(CGFloat)timeBySecond { //視頻檔案URL地址 NSURL *url = [NSURL URLWithString:@"http://192.168.6.147/2.mp4"]; //建立媒體資訊對象AVURLAsset AVURLAsset *urlAsset = [AVURLAsset assetWithURL:url]; //建立影片縮圖產生器對象AVAssetImageGenerator AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset]; //建立影片縮圖的時間,第一個參數是視頻第幾秒,第二個參數是每秒畫面格數 CMTime time = CMTimeMake(timeBySecond, 10); CMTime actualTime;//實際產生影片縮圖的時間 NSError *error = nil;//錯誤資訊 //使用對象方法,產生影片縮圖,注意產生的是CGImageRef類型,如果要在UIImageView上顯示,需要轉為UIImage CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error]; if (error) { NSLog(@"截取影片縮圖發生錯誤,錯誤資訊:%@",error.localizedDescription); return nil; } //CGImageRef轉UIImage對象 UIImage *image = [UIImage imageWithCGImage:cgImage]; //記得釋放CGImageRef CGImageRelease(cgImage); return image;}