在iOS中播放視頻可以使用兩個框架來實現:
MediaPlayer
框架的MPMoviePlayerController
和MPMoviePlayerViewController
AVFoundation
框架中的AVPlayer
AVKit
框架的AVPlayerViewController
【iOS8之後才有】但在近兩年的WWDC上,MediaPlayer
框架被iOS9標記為deprcated
,意味著它已經不再被蘋果繼續維護,而且該框架集成度較高,不如AVFoundation
靈活性高,所以這裡就講AVFoundation
的AVPlayer
來實現播放視頻,AVPlayerViewController
實際上就是對AVPlayer
的封裝。
下面是兩個框架的應用所在層:
AVPlayer
存在於AVFoundation
中,它更加接近於底層,所以靈活性極高。
AVPlayer
本身並不能顯示視頻,如果AVPlayer
要顯示必須創建一個播放器圖層AVPlayerLayer
用於展示,該播放器圖層繼承於CALayer
。
AVPlayerItem
,一個視頻對應一個AVPlayerItem
AVPlayer
視頻播放器對象,需要一個AVPlayerItem
進行初始化AVPlayerLayer
播放圖層對象,添加到顯示視圖上去play
,播放器暫停pause
進度條監聽是調用AVPlayer
的對象方法:
-(id)addPeriodicTimeObserverForInterval:(CMTime)interval/*監聽頻率*/
queue:(dispatch_queue_t)queue /*監聽GCD線程*/
usingBlock:(void (^)(CMTime time))block;/*監聽回調*/
/Library/WebServer/Documents
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
@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
/* 獲取播放內容對象,一個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;
}
/* 初始化視頻播放器 */
- (void)initAVPlayer {
//獲取播放內容
AVPlayerItem *item = [self getPlayItemByNum:0];
//創建視頻播放器
AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
self.player = player;
//添加播放進度監聽
[self addProgressObserver];
//添加播放內容KVO監聽
[self addObserverToPlayerItem:item];
//添加通知中心監聽播放完成
[self addNotificationToPlayerItem];
}
#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;
}
#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];
}
#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<NSString *,id> *)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);
}
}
#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];
}
}];
}
- (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];
}
一個簡單的視頻播放器就這麼搞定了,感覺還是好麻煩,而且很多功能還沒有實現。
實際上在iOS8.0之後,蘋果為我們封裝了AVPlayer
等視頻播放相關的類 ,形成了一個直接可以簡單使用的播放器控制器類,那就是AVPlayerViewController
,下面來講下你就覺得有多爽,上面那一大堆,只需要下面的一小塊代碼就可以實現了。
添加頭文件:
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
URL
AVPlayer
創建AVPlayerViewController
Over,一個功能十分齊全的播放器就好了
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
@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
而定制的非常方便的視頻播放器
我用AVPlayer
寫的視頻播放器被甩了好幾十條街,/(ㄒoㄒ)/~~。
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;
}
代碼Demo點這裡:LearnDemo裡面的MovieDemo