AVFoundation的樂趣

今天我們拿 AVFoundation 的冰山一角來闡述一下我個人的喜好!相信每個人都會用 iPhoneiPad 來觀看“視頻”,優酷、愛奇藝、搜狐?嗯!還有 Youtube

原文點擊這里:http://www.zengxianhua.com/2016/02/19/avfoundationde-le-qu/

現在的產品體驗已經足以讓我們玩的爽,也可以玩的 High

于是我開始研究他們的細節,果然不錯,在體驗和兼容性上確實有一定的難度。

不想用 MPMoviePlayerController 的同學,都會想到 AVFoundation 去自定義,其實重載 MPMoviePlayerController 也可以 自定義UI,但我還是喜歡自己琢磨。

我想用自己寫的播放器來看視頻,可以嗎?當然我就這么干了!

先理解幾個名稱的基本概念。

AVPLayer

我理解的官方解釋:你可以使用 AVPlayer 對象實現控制和自定義UI的單個或多個播放。

這意味著你項目如果有多個播放的需求,這不就幫你解決了嗎?

AVPlayer支持本地與遠程的多媒體文件,正好,我本意就是可以緩存到本地看,也可以在線看。

我們需要怎樣呈現可視內容呢?

AVPlayerLayer

我理解的官方解釋:APLayerLayerCALayer 的子類,AVPLayer可以通過它指導視頻輸出和可視化。

那音頻需要嗎?沒有可視化內容,使用 AVPlayer 就可以播放啦!

AVAsset

我理解的官方解釋:AVAsset是定時的視聽媒體,它可以是視頻、影片、歌曲、播客節目;可以是本地或者遠程的;也可以是限定或者非限定的流;

AVPlayerItem

我理解的官方解釋:協調AVPlayer和AVAsset,同樣具有AVPlayerItemTrack對象,可以控制音量、播放速率等等。

基本實現流程

了解 AVPlayerAVPlayerLayerAVPlayerItemAVAsset 基本概念之后,那如何定制自己的播放器呢?

  • 第一步首先需要一個展示的容器,繼承 UIView,隨你喜歡取個類名唄!( VideoLayerView )做以下操作:

      .h
      @property (nonatomic, strong) AVPlayer *player;
      @property (nonatomic, readonly) AVPlayerLayer *playerLayer;
    
      @property (nonatomic, copy) NSString *videoFillMode;
    
      .m
      + (Class)layerClass {
          return [AVPlayerLayer class];
      }
    
      - (void)commit {
          self.playerLayer.backgroundColor = [[UIColor blackColor] CGColor];
          self.videoFillMode = AVLayerVideoGravityResizeAspect;
      }
    
      - (instancetype)initWithFrame:(CGRect)frame {
          self = [super initWithFrame:frame];
          if (self) {
              [self commit];
          }
          return self;
      }
    
      - (void)awakeFromNib {
          [self commit];
      }
    
      - (void)setPlayer:(AVPlayer *)player {
          [(AVPlayerLayer *)[self layer] setPlayer:player];
      }
    
      - (AVPlayer *)player {
          return [(AVPlayerLayer *)[self layer] player];
      }
    
      - (AVPlayerLayer *)playerLayer {
          return (AVPlayerLayer *)self.layer;
      }
    
      - (void)setVideoFillMode:(NSString *)videoFillMode {
          [self playerLayer].videoGravity = videoFillMode;
      }
    
      - (NSString *)videoFillMode {
          return [self playerLayer].videoGravity;
      }
    
  • 第二步創建 AVAsset 進行加載多媒體文件

你的是遠程地址?我的是本地路徑?OMG,這些都不是問題

    NSURL *mediaURL = [NSURL URLWithString:mediaPath];

    if (!mediaURL || ![mediaURL scheme]) {
         mediaURL = [NSURL fileURLWithPath:mediaPath];
    }

    AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:mediaURL options:nil];

    AVURLAsset 是 AVAsset的子類
  • 第三步通過 AVAssetloadValuesAsynchronouslyForKeys: completionHandler: 方法得到 AVPlayerItem,我們暫時只需要playableduration 這兩個key,這里是異步加載數據,你需要判斷加載的狀態。整理如下:

      NSArray *keys = @[@“playable", @“duration"];
      __weak typeof(self.mediaAsset) weakAsset = urlAsset;
      [urlAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
          dispatch_async(dispatch_get_main_queue(), ^{
          // check the keys
          for (NSString *key in keys) {
             NSError *error = nil;
             AVKeyValueStatus keyStatus = [weakAsset statusOfValueForKey:key error:&error];
             if (keyStatus == AVKeyValueStatusFailed) {
                NSLog(@"error (%@)", [[error userInfo] objectForKey:AVPlayerItemFailedToPlayToEndTimeErrorKey]);
                return;
             }
         }
    
         // check playable
         if (!weakAsset.playable) {
             return;
         }
    
         // get AVPlayerItem
         AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:weakAsset];
         });
      }];
    
  • 第四步通過異步得到的 AVPlayerItem 進行創建AVPlayer

      AVPlayer *player = [AVPLayer playerWithPlayerItem:playerItem];
      VideoLayerView *layerView = [[VideoLayerView alloc] initWithFrame:frame];
      layerView.player = player;
      [player play];
    

這樣就初步完成播放本地、遠程多媒體的播放器了,如果想趕上大公司的產品體驗,還需好好打磨一下。

期待下一回的優化唄!

下一期:打造一個上百Star的開源庫

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容