AVFoundation学习笔记:视频播放相关

Posted 人生如梦91

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AVFoundation学习笔记:视频播放相关相关的知识,希望对你有一定的参考价值。

上一次学习了《AVFoundation开发秘籍》的第一至三章,现在继续学习AVFoundation.

视频播放

视频播放是AVFoundation的其中一项功能,视频播放的主要类如下所示:

视频播放综述

AVPlayer

AVFoundation的播放功能都围绕AVPlayer展开,AVPlayer是一个用来播放基于时间的视听媒体的控制器对象,支持本地,分步下载或HTTP Live Streaming协议得到的流媒体。是一个不可视组件。AVPlayer只管理一个单独资源的播放,若要管理一个资源队列,可使用AVQueuePlayer子类。

AVPlayerLayer

AVPlayerLayer是一个可视化组件,创建实例需要一个指向AVPlayer实例的指针。它是一个相当简单的类,开发者需要自定义的属性只有videoGravity,分为AVLayerVideoGravityResizeAspect(缩放保持原始宽高比),AVLayerVideoGravityResizeAspectFill(保持宽高比并填充范围)和AVLayerVideoGravityResize(拉伸视频以匹配范围)。

AVPlayerItem

视频播放的最终是使用AVPlayer来播放AVAsset指向的资源,但AVAsset只包含媒体资源的静态信息,当我们需要对一个资源及相关曲目进行播放时,首先需要通过AVPlayerItem和AVPlayerItemTrack类构建相应的动态内容。

播放基础

播放视频

播放的通用基础代码如下所示:

状态监控

AVPlayerItem具有一个名为status的AVPlayerItemStatus类型的属性,在对象创建之初,状态由AVPlayerItemStatusUnknown,当status变为AVPlayerItemStatusReadyToPlay时,具体内容才可以播放。开发者可通过KVO机制来监视status的变化。

时间处理

AVFoundation是基于Core Media的高层封装,时间处理使用的是CMTime数据结构,定义如下:

typedef struct 
    CMTimeValue value;
    CMTimeScale timescale;
    CMTimeFlags flags;
    CMTimeEpoch epoch;
CMTime;

CMTime关键的两个值是value和timescale,其中value是一个64位整数值,timescale是一个32位整数值。在时间呈现样式中分别作为分子和分母。可使用CMTimeMake建立时间。其中kCMTimeZero表示0。

AVPlayer时间监听

AVPlayer提供了两种基于时间的监听方法,让应用程序可以对时间变化进行精准的监听。

定期监听

通常情况下,我们希望以一定时间间隔获得通知,若需要随着时间的变化移动播放头位置或更新时间显示,可使用AVPlayer的addPeriodicTimeObserverForInterval:queue:usingBlock:方法,参数如下:

  • interv: 一个用于指定通知周期间隔的CMTime值
  • queue: 通知发送的顺序调度队列,若没有明确指定默认为主队列
  • block: 一个在指定的时间间隔中将会在队列上调用block。传递一个CMTime值用于指示播放器的当前时间

边界时间监听

AVPlayer还提供了一种更有针对性的方法来监听时间,应用程序可以得到播放器时间轴中多个边界点的遍历结果。这一方法主要用于同步用户界面变更或随着视频播放记录一些非可视化数据,以此判断用户播放数据,需用addBoundaryTimeObserverForTimes:queue:usingBlock:方法,参数如下:

  • times: CMTime值组成一个NSArray数组定义了需要通知的边界点。
  • queue: 用来发送通知的顺序调度队列,指定NULL默认主队列。
  • block: 每当正常播放中跨越一个边界点时就会在队列中调用这个回调块。但并不提供CMTime。

取消监听

AVPlayer的两个监听方法,会返回一个隐含id指针,指向这个监听,监听完毕之后,用户需要调用removeTimeObserver:方法,将id指针传入以解除监听。

播放结束监听

一常见的需要监听的事件就是视频播放完毕的时间,当播放完成时,AVPlayerItem会发送一个AVPlayerItemDidPlayToEndTimeNotification通知。此时调用AVPlayer的currentTime可获得当前结束时间。

生成缩略图

生成缩略图,AVFountation中提供了一个名为AVAssetImageGenerator的工具类,可从AVAsset中提取图片,AVAssetImageGenerator既可生成本地图片,也可生成持续下载的资源,但无法从HTTP Live Stream中提取图片。其中定义了两个方法,如下所示:

  • copyCGImageAtTime:actualTime:error: 允许在指定时间点捕捉图片,如果开发者希望捕捉一张图片,那此方法是最适合的,可用于在视频列表中展示缩略图。
  • generateCGImageAsynchronouslyForTimes:completionHandler: 允许按照第一个参数所指定的时间段生成一个图片序列。该方法具有很高的性能,只需要调用此一个方法,就可以生成一组图片。代码如下所示:

使用该方法需要注意以下几点:

  1. 创建一个新的AVAssetImageGenerator实例,需为其传递一个强引用的AVAsset对象,否则将导致无法调用回调
  2. AVAssetImageGenerator为配置图片生成定义了一些属性,大部分提供了合理的默认值,但maximumSize属性需要明确配置,默认情况下会保持图片的原始维度,设置该值会对图片进行缩放,提高性能。代码中设置CGSizeMake(200, 0),确保图片遵循宽度,并根据宽高比自动设置高度。
  3. result类型为AVAssetImageGeneratorResult,当值为AVAssetImageGeneratorSucceeded,则表示图片已经生成了,若为AVAssetImageGeneratorFailed,可通过NSError查询错误。

字幕显示

AVPlayer为字幕的显示提供了可靠的方法,AVPlayerLayer会自动渲染这些元素,完成这些操作需要用到AVMediaSelectionGroup和AVMediaSelectionOption两个类。

AVMediaSelectionOption表示AVAsset中的备用媒体的呈现方式,比如备用音频、视频或文本轨道。确定这些需要用到一个名为avaliableMediaCharacteristicsWithMediaSelectionOptions的AVAsset属性,这个属性数组包含的字符串值为AVMediaCharacteristicVisual(视频)、AVMediaCharacteristicAudible(音频)、AVMediaCharacteristicLegible(字幕或隐藏式字幕)。调用AVAsset的mediaSelectionGroupForMediaCharacteristic: 方法,会返回一个AVMediaSelectionGroup。示例代码如下:

airplay

AVPlayer有一个属性allowsExternalPlayback,允许启用或禁用AirPlay功能。该属性默认为YES,ios并没有AirPlay框架或API供开发者使用,取代的是MediaPlayer框架中的MPVolumeView实例,代码如下:

MPVolumeView* volumeView = [[MPVolumeView alloc] init];
volumeView.showsVolumeSlider = NO;
[volumeView sizeToFit];
[self.view addSubview:volumeView];

获取标题

从AVAsset中可以提取出资源的标题,通用代码如下:

在初始化AVPlayerItem的时候,可以换用以下方法,加载commonMetadata键值,然后依据上述方法,获得标题:

+(AVPlayerItem*)playerItemWithAsset:(AVAsset*)asset automaticallyLoadedAssetKeys:(NSArray*)keys;

章节数据

AVFoundation可以通过AVTimedMetadataGroup类直接处理章数据。在使用该数据时,首先要载入availableChapterLocales键,AVAsset定义了两个方法可以获取相关数据:

- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsBestMatchingPreferredLanguages:(NSArray<NSString *> *)preferredLanguages;
- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsWithTitleLocale:(NSLocale *)locale containingItemsWithCommonKeys:(NSArray<NSString *> *)commonKeys;

AVTimedMetadataGroup包含两个属性,timeRange和items,timeRange属性保存着一个CMTimeRange结构,该结构包含用于表示时间范围起点的CMTime值和定义资源时长的CMTime值。章节的标题和缩略图可在items属性中找到。items属性包含了一个由来自于通用键空间AVMetadataKeySpaceCommon对象的NSArray。先定义THChapter,示例代码如下:

@interface THChapter : NSObject

+ (instancetype)chapterWithTime:(CMTime)time
                         number:(NSUInteger)number
                          title:(NSString *)title;

@property (readonly) CMTime time;
@property (readonly) NSUInteger number;
@property (readonly, copy) NSString *title;

- (BOOL)isInTimeRange:(CMTimeRange)range;
- (BOOL)hasValidTime;

@end

#import "THChapter.h"

@interface THChapter ()
@property CMTime time;
@property NSUInteger number;
@property (copy) NSString *title;
@end

@implementation THChapter

+ (instancetype)chapterWithTime:(CMTime)time
                         number:(NSUInteger)number
                          title:(NSString *)title 
    return [[THChapter alloc] initWithTime:time number:number title:title];


- (id)initWithTime:(CMTime)time
            number:(NSUInteger)number
             title:(NSString *)title 
    self = [super init];
    if (self) 
        _time = time;
        _number = number;
        _title = [title copy];
    
    return self;


- (BOOL)isInTimeRange:(CMTimeRange)timeRange 
    return CMTIME_COMPARE_INLINE(_time, >, timeRange.start) &&
            CMTIME_COMPARE_INLINE(_time, <, timeRange.duration);


- (BOOL)hasValidTime 
    return CMTIME_IS_VALID(_time);


- (NSString *)debugDescription 
    NSString *format = @"time:%@ number:%lu title:%@";
    NSString *strTime = (__bridge NSString *)CMTimeCopyDescription(NULL, _time);
    return [NSString stringWithFormat:format, strTime, _number, _title];


@end

再从中获取章节的相关数据,如下:

查找章节

查找章节的相关代码如下所示:

注意在查找前一章节数据的时候,因为随意视频的播放,我们用CMTimeSubtract减掉一点时间,否则不管怎么跳,也只会跳转到当前章节,而不会跳转到上一章节。调用isInTimeRange:方法判断章的起点和终点是否在范围之内,返回匹配的章节。这里需要注意一下,随着视频的播放,时间是一直向前的,所以如果查找前一章节,要减掉一个时间,否则只会找到当前章节。

启用修剪

AVPlayerView使用以下方法来启用修剪,但在使用前需要先查询canBeginTrimming属性,如果返回YES,才可以继续修剪。

- (void)beginTrimmingWithCompletionHandler:(void (^)(AVPlayerViewTrimResult result))handler;

兼容传统资源

QuickTime支持很多编解码方式和媒体类型,但大部都已经过时了。我们在使用Mac的QuickTime的时候,如果打开一个较新的媒体,会显示一个“转换中…”的对话框,我们可以使用这个功能。首先包含以下头文件:

#import <QTKit/QTMovieModernizer.h>

转换的方法如下所示:

以上是关于AVFoundation学习笔记:视频播放相关的主要内容,如果未能解决你的问题,请参考以下文章

AVFoundation学习笔记: 媒体捕捉读取及写入

AVFoundation学习笔记: 媒体的创建与编辑

AVFoundation - 视频没有在 AVplayer 的 AVMutablecomposition 中播放

如何防止使用 AVFoundation 录制视频中断当前正在播放的任何全局音频(Swift)?

[iOS开发]AVFoundation浅学习

[iOS开发]AVFoundation浅学习