你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS編程技術 >> iOS學習筆記26-視頻播放

iOS學習筆記26-視頻播放

編輯:IOS編程技術

一、視頻

在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視頻播放使用步驟:
  1. 創建視頻資源地址URL,可以是網絡URL
  2. 通過URL創建視頻內容對象AVPlayerItem,一個視頻對應一個AVPlayerItem
  3. 創建AVPlayer視頻播放器對象,需要一個AVPlayerItem進行初始化
  4. 創建AVPlayerLayer播放圖層對象,添加到顯示視圖上去
  5. 播放器播放play,播放器暫停pause
  6. 添加通知中心監聽視頻播放完成,使用KVO監聽播放內容的屬性變化
  7. 進度條監聽是調用AVPlayer的對象方法:

    -(id)addPeriodicTimeObserverForInterval:(CMTime)interval/*監聽頻率*/ 
                                     queue:(dispatch_queue_t)queue /*監聽GCD線程*/
                                usingBlock:(void (^)(CMTime time))block;/*監聽回調*/
測試環境搭建:
  1. 利用終端開啟Apache服務,使得手機可以通過網絡訪問本機資源
  2. 下載視頻MP4到Apache的Web資源目錄
    默認的Apache的Web資源目錄是/Library/WebServer/Documents
  3. 查看本地服務器的IP
  4. 別忘了進入info.plist設置HTTP網絡解禁
下面是一個具體的項目:
ViewController屬性
#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
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<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);
    }
}
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,下面來講下你就覺得有多爽,上面那一大堆,只需要下面的一小塊代碼就可以實現了。

使用步驟:
  1. 導入框架:
  2. 添加頭文件:

    #import <AVFoundation/AVFoundation.h>
    #import <AVKit/AVKit.h>
  3. 創建URL
  4. 創建AVPlayer
  5. 創建AVPlayerViewController

Over,一個功能十分齊全的播放器就好了

下面是全部代碼【/(ㄒoㄒ)/~~淚奔】:
#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,用於獲取視頻截圖。

應用場景:
  • 播放視頻時,拖動進度條時,可以顯示視頻縮略圖,查看視頻播放到哪個畫面了
  • 選擇某個視頻播放的時候,可以使用視頻縮略圖,點擊視頻縮放圖,進入真正的播放視頻界面
  • 一些有意思的視頻場景需要截屏留念的時候,可以使用視頻縮略圖
具體使用步驟:
  1. 創建AVURLAsset對象,該對象主要用於獲取媒體信息,包括視頻、聲音。
  2. 根據AVURLAsset創建AVAssetImageGenerator對象
  3. 使用對象方法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

如果有什麼問題可以在下方評論區中提出!O(∩_∩)O哈!
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved