Quantcast
Viewing latest article 6
Browse Latest Browse All 13

AVFoundation的乐趣

Image may be NSFW.
Clik here to view.
AVFoundation的乐趣

今天我们拿 AVFoundation 的冰山一角来阐述一下我个人的喜好!相信每个人都会用 iPhoneiPad 来观看“视频”,优酷、爱奇艺、搜狐?嗯!还有 Youtube

现在的产品体验已经足以让我们玩的爽,也可以玩的 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的开源库


Viewing latest article 6
Browse Latest Browse All 13

Trending Articles