今天我们拿 AVFoundation 的冰山一角来阐述一下我个人的喜好!相信每个人都会用 iPhone、iPad 来观看“视频”,优酷、爱奇艺、搜狐?嗯!还有 Youtube。
原文点击这里:http://www.zengxianhua.com/2016/02/19/avfoundationde-le-qu/
现在的产品体验已经足以让我们玩的爽,也可以玩的 High!
于是我开始研究他们的细节,果然不错,在体验和兼容性上确实有一定的难度。
不想用 MPMoviePlayerController 的同学,都会想到 AVFoundation 去自定义,其实重载 MPMoviePlayerController 也可以 自定义UI,但我还是喜欢自己琢磨。
我想用自己写的播放器来看视频,可以吗?当然我就这么干了!
先理解几个名称的基本概念。
AVPLayer
我理解的官方解释:你可以使用 AVPlayer 对象实现控制和自定义UI的单个或多个播放。
这意味着你项目如果有多个播放的需求,这不就帮你解决了吗?
AVPlayer支持本地与远程的多媒体文件,正好,我本意就是可以缓存到本地看,也可以在线看。
我们需要怎样呈现可视内容呢?
AVPlayerLayer
我理解的官方解释:APLayerLayer 是 CALayer 的子类,AVPLayer可以通过它指导视频输出和可视化。
那音频需要吗?没有可视化内容,使用 AVPlayer 就可以播放啦!
AVAsset
我理解的官方解释:AVAsset是定时的视听媒体,它可以是视频、影片、歌曲、播客节目;可以是本地或者远程的;也可以是限定或者非限定的流;
AVPlayerItem
我理解的官方解释:协调AVPlayer和AVAsset,同样具有AVPlayerItemTrack对象,可以控制音量、播放速率等等。
基本实现流程
了解 AVPlayer、AVPlayerLayer、AVPlayerItem、AVAsset 基本概念之后,那如何定制自己的播放器呢?
-
第一步首先需要一个展示的容器,继承 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的子类
-
第三步通过 AVAsset 的 loadValuesAsynchronouslyForKeys: completionHandler: 方法得到 AVPlayerItem,我们暂时只需要playable、 duration 这两个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的开源库