视频播放

AVFoundation 是 Objective-C 中创建及编辑视听媒体文件的几个框架之一,其提供了检查、创建、编辑或重新编码媒体文件的接口,也使得从设备获取的视频实时数据可操纵。
在 iOS 中,对于视频的播放我们会直接采用 AVFoundation。

AVAsset

AVAsset 是 AVFoundation 框架中的核心的类,它提供了基于时间的音视频数据(如电影文件、视频流),一个asset包含很多轨道的结合。
它是一个或者多个媒体数据的集合,描述的是整个集合的属性,如标题、时长、大小等,并且没有特定的数据格式。集合的每一个媒体数据都是统一的数据类型,称之为 track。简单的情况是一种数据是音频数据,一种是视频数据,而较复杂的情况是一种数据交织着音频和视频数据。
需要明白的是在 AVFoundation 中,初始化了 asset 及 track 后,并不意味着资源已经可用,因为若资源本身并不携带自身信息时,那么系统需要自己计算相关信息,这个过程会阻塞线程,所以应该使用异步方式进行获取资源信息后的操作。

  1. NSURL *url = <#资源路径#>;
  2. AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
  3. NSArray *keys = @[@"duration",@"tracks"];
  4. [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
  5. NSError *error = nil;
  6. AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"tracks" error:&error];
  7. //根据相应的属性状态进行对应的处理
  8. switch (tracksStatus) {
  9. case AVKeyValueStatusUnknown:
  10. //TODO
  11. break;
  12. case AVKeyValueStatusLoading:
  13. //TODO
  14. break;
  15. case AVKeyValueStatusLoaded:
  16. //TODO
  17. break;
  18. case AVKeyValueStatusFailed:
  19. //TODO
  20. break;
  21. case AVKeyValueStatusCancelled:
  22. //TODO
  23. break;
  24. }
  25. }];

AVFoundation 中可以使用 compositions 将多个媒体数据(video/audio tracks)合成为一个 asset ,这个过程中,可以添加或移除 tracks ,调整它们的顺序,或者设置音频的音量和变化坡度,视频容量等属性。这些媒体数据的集合保存在内存中,直到使用 export session 将它导出到本地文件中。另外,还可以使用 asset writer 创建 asset 。

AVPlayer

在开发中,视频资源一般直接使用 AVPlayer 进行播放,单纯的使用 AVPlayer 类是无法现实视频的播放的,要将视频添加到AVPlayerLayer中,这样才能将视频展示来。

  1. // 媒体资源
  2. NSURL *url = [NSURL URLWithString:@"url"];
  3. AVAsset *asset = [AVAsset assetWithURL:url];
  4. // 播放资源文件
  5. AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
  6. // 播放器
  7. AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
  8. // 播放界面
  9. AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
  10. playerLayer.frame= self.view.bounds;
  11. [self.view.layer addSublayer:playerLayer];
  • AVPlayerItem 是一个视频资源的载体,承载着 AVAsset,AVPlayerItem实例来管理整个 Asset 的表示状态,然后通过 AVPlayer 进行播放
  • AVPlayer 是一个控制器对象,你可以使用它来管理媒体资源的播放,例如启动和停止回放,以及查找特定的时间。您使用AVPlayer实例来播放单个资源。你也可以使用 AVQueuePlaye 对象按顺序播放许多项资源。
  • AVPlayerLayer 视频画面展示图层,首先我们之所以能够看到视频是因为 AVPlayerLayer 帮我们把视频呈现出来了,可以说是 AVPlayerLayer 就是一个视频播放器的载体,它负责需要播放的画面。

应用例子

首先我们先准备承载播放界面的 View,可以通过传入 AVPlayer 进行初始化我们的播放界面:

  1. @implementation PlayerView
  2. // 将 view 本身的 layer 指定成 AVPlayerLayer
  3. + (Class)layerClass {
  4. return [AVPlayerLayer class];
  5. }
  6. // 在初始化的时候,将传入的 AVPlayer,赋值给 AVPlayerLayer
  7. - (instancetype)initWithPlayer:(AVPlayer *)player {
  8. self = [super initWithFrame:CGRectZero];
  9. if (self) {
  10. self.backgroundColor = [UIColor blackColor];
  11. [(AVPlayerLayer *) [self layer] setPlayer:player];
  12. }
  13. return self;
  14. }
  15. @end

在视图准备完成之后,可以进行初始化播放器的步骤:

  1. NSURL *url = [[NSBundle mainBundle] URLForResource:@"cat.mp4" withExtension:nil];
  2. self.asset = [AVAsset assetWithURL:url];
  3. self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
  4. self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
  5. self.playerView = [[PlayerView alloc] initWithPlayer:self.player];
  6. [self.player play];

播放控制

通过调用 player 的 play 、pause 、setRate: 方法,可以控制 item 的播放,这些方法都会改变 player 的属性 rate 的值,该值为 1 表示 item 按正常速率播放,为 0 表示 item 暂停播放,0~1 表示低速播放,大于 1 表示高速播放,小于 0 表示从后向前播放。

这里我们可以通过监听 AVPlayer 的 status 属性,获取当前播放状态的变化:

  1. // 通过KVO来观察status属性的变化,来获得播放之前的错误信息
  2. [self.player addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  3. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  4. if ([keyPath isEqualToString:@"status"]) {
  5. AVPlayerItemStatus status = [change[NSKeyValueChangeNewKey]intValue];
  6. switch (status) {
  7. case AVPlayerItemStatusFailed:
  8. //TODO
  9. break;
  10. case AVPlayerItemStatusReadyToPlay:
  11. //TODO
  12. break;
  13. case AVPlayerItemStatusUnknown:
  14. //TODO
  15. break;
  16. default:
  17. break;
  18. }
  19. }
  20. }

可以使用下面的方法监听播放时间的变化:

  1. - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
  2. - (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(void))block;

切换 AVPlayer 的进度

  1. //第二个方法能够进行更准确的跳转,但是需要进行额外的计算
  2. - (void)seekToDate:(NSDate *)date;
  3. - (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter; (tolerance: 公差,前后公差)
  4. //这两个方法传入了一个回调,当一个时间跳转请求被新的请求或其他操作打断时,回调也会被执行但是此时 finished 参数值为 NO
  5. - (void)seekToTime:(CMTime)time completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0);
  6. - (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0);

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

更多精彩分享