iOS學習筆記26-視頻播放 – iPhone手機開發 iPhone軟體開發教學課程

一、視頻

在iOS中播放視頻可以使用兩個框架來實現:
1. MediaPlayer框架的MPMoviePlayerControllerMPMoviePlayerViewController
2. AVFoundation框架中的AVPlayer
3. AVKit框架的AVPlayerViewController【iOS8之後才有】

但在近兩年的WWDC上,MediaPlayer框架被iOS9標記為deprcated,意味著它已經不再被蘋果繼續維護,而且該框架集成度較高,不如AVFoundation靈活性高,所以這裡就講AVFoundationAVPlayer來實現播放視頻,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 = @[@"https://192.168.6.147/1.mp4",
                             @"https://192.168.6.147/2.mp4",
                             @"https://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:@"https://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”https://www.2cto.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:@"https://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;
}

 

發佈留言