AVFoundation中的元数据
Apple环境下的媒体类型主要有四种:
- QuickTime(mov)
- MPEG-4 video(mp4和m4v)
- MPEG-4 audio(m4a)
- MPEG-Layer III audio(mp3)
使用元数据
大部分使用AVAsset提供元数据,但是当涉及获取曲目一级元数据等情况时也会使用AVAssetTrack
获取资源中包含的所有元数据
NSURL *url = [NSURL URLWithString:@"url"];
AVAsset *asset = [AVAsset assetWithURL:url];
//需要加载的属性数组
NSArray *keys = @[@"availableMetadataFormats"];
//异步加载属性
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
//获取属性的加载状态
AVKeyValueStatus status = [asset statusOfValueForKey:@"availableMetadataFormats"error:nil];
if(status == AVKeyValueStatusLoaded){
//将资源的元数据放入数组
NSMutableArray *metadata = [NSMutableArray array];
for(NSString *format in asset.availableMetadataFormats){
[metadata addObject:format];
}
}
}];
筛选元数据
NSArray *metadata = @[];//AVMetadataItems
//常用的键空间
//AVMetadataFormatQuickTimeMetadata,
//AVMetadataFormatiTunesMetadata,
//AVMetadataFormatID3Metadata
NSString *keySpace = AVMetadataKeySpaceiTunes;
NSString *artisKey = AVMetadataiTunesMetadataKeyArtist;
NSString *albumKey = AVMetadataiTunesMetadataKeyAlbum;
//筛选获取演唱者的元数据
NSArray *artisMetadata = [AVMetadataItem metadataItemsFromArray:metadata withKey:artisKey keySpace:keySpace];
//获取唱片的元数据
NSArray *albumMetadata = [AVMetadataItem metadataItemsFromArray:metadata withKey:albumKey keySpace:keySpace];
AVMetadataItem *artisItem,*albumItem;
if(artisMetadata.count > 0){
artisItem = artisMetadata[0];
}
if(albumMetadata.count > 0){
albumItem = albumMetadata[0];
}
1.AVPlayer
AVPlayer可以支持播放本地、分布下载或通过HTTP Live Streaming协议得到的流媒体
注: AVPlayer只管理单独资源的播放,AVQueuePlayer可以管理一个资源队列
创建对象
//两个类方法创建
+ (instancetype)playerWithURL:(NSURL *)URL;
+ (instancetype)playerWithPlayerItem:(AVPlayerItem *)item;
//两个实例方法
- (instancetype)initWithURL:(NSURL *)URL;
- (instancetype)initWithPlayerItem:(AVPlayerItem *)item;
属性
//状态
@property (nonatomic, readonly) AVPlayerStatus status;
//AVPlayerStatusUnknown,
//AVPlayerStatusReadyToPlay
//AVPlayerStatusFailed
当status为AVPlayerStatusFailed时AVPlayer将不能播放,才是需要重新创建对象
拓展AVPlayerPlaybackControl
//当前播放速度,值为0.0时停止播放,值为1.0时为正常播放速度
@property (nonatomic) float rate;
//播放和暂停的方法
- (void)play;
- (void)pause;
拓展AVPlayerItemControl
//当前播放的项目
@property (nonatomic, readonly, nullable) AVPlayerItem *currentItem;
//替换当前播放的项目,可以用于切换视频或音频,当需要替换的item和currentItem为同一个时不做任何操作,音视频据需播放,没有任何影响
- (void)replaceCurrentItemWithPlayerItem:(nullable AVPlayerItem *)item;
//当播放完毕时的操作
@property (nonatomic) AVPlayerActionAtItemEnd actionAtItemEnd;
//AVPlayerActionAtItemEndAdvance 该属性只有AVQueuePlayer才可以用,表示继续播放下一个项目
//AVPlayerActionAtItemEndPause 播放完毕后暂停
//AVPlayerActionAtItemEndNone 不错任何处理
拓展AVPlayerTimeControl
//当前的播放时间
- (CMTime)currentTime;
//播放到指定时间位置
- (void)seekToDate:(NSDate *)date;
- (void)seekToDate:(NSDate *)date completionHandler:(void (^)(BOOL finished))completionHandler
- (void)seekToTime:(CMTime)time;
//直接播放到一个时间的范围
- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter;
拓展AVPlayerMediaControl
//当前播放的声音,如果想做媒体播放声音的进度条不要用这个,可以使用MPVolumeView,这个只是控制当前item播放音量而不是所有的item音量
@property (nonatomic) float volume
2.AVPlayerLayer
//需要一个AVPlayer对象做参数获取一个AVPlayerLayer实例
//AVPlayerLayer负责展示画面,AVPlayer负责链接资源
+ (AVPlayerLayer *)playerLayerWithPlayer:(nullable AVPlayer *)player
@property (nonatomic, retain, nullable) AVPlayer *player
//播放画面的缩放比例,和iamgeview的contentmode差不多
//AVLayerVideoGravityResizeAspect 默认值
//AVLayerVideoGravityResizeAspectFill
//AVLayerVideoGravityResize
@property(copy) NSString *videoGravity
//当前视频图像的大小位置
@property (nonatomic, readonly) CGRect videoRect
3.CMTime
//CMTime的定义
typedef struct
{
CMTimeValue value; 分子
CMTimeScale timescale; 分母
CMTimeFlags flags;
CMTimeEpoch epoch;
} CMTime;
//0.5秒
CMTime halfSecond = CMTimeMake(1, 2);
//5秒
CMTime fiveSecond = CMTimeMake(5, 1);
//44.1kHZ
CMTime oneSample = CMTimeMake(1, 44100);
4.播放视频
//根据url获得视频资源
_asset = [AVAsset assetWithURL:url];
//需要加载的属性数组
NSArray *keys = @[@"tracks",
@"duration",
@"commonMetadata",
@"availableMediaCharacteristicsWithMediaSelectionOptions"];
//这种初始化方法可以同时加载属性
_playerItem = [AVPlayerItem playerItemWithAsset:_asset automaticallyLoadedAssetKeys:keys];
//AVPlayerItem对象初始化AVPlayer
_player = [AVPlayer playerWithPlayerItem:_playerItem];
//为AVPlayerLayer设置AVPlayer
[(AVPlayerLayer *)self.layer setPlayer:_player];
//监听AVPlayerItem的播放状态
[_playerItem addObserver:self forKeyPath:@"status" options:0 context:&PlayerStatus];
监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if(context == &PlayerStatus){
//因为不知道在哪个线程,所以指定组线程执行
dispatch_async(dispatch_get_main_queue(), ^{
if(_playerItem.status == AVPlayerStatusReadyToPlay){
//移除监听状态
[self.playerItem removeObserver:self forKeyPath:@"status"];
//1.获取标题
//2.获取播放时间,设置进度条
//3.添加播放时间的监听
//4.监听播放结束事件
//播放
[_player play];
}
});
}
}
设置标题
//首先获取commonMetadata属性的加载状态
AVKeyValueStatus status = [_asset statusOfValueForKey:@"commonMetadata" error:nil];
if(status == AVKeyValueStatusLoaded){
//获取标题项数据
NSArray *items = [AVMetadataItem metadataItemsFromArray:_asset.commonMetadata withKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon];
if(items.count > 0){
AVMetadataItem *titleItem = items[0];
self.titleLabel.text = titleItem.stringValue;
}else{
self.titleLabel.text = @"";
}
}
获取播放总时间
CMTime duration = _playerItem.duration;
fload durationTime = CMTimeGetSeconds(duration);
添加播放时间的监听
CMTime interval = CMTimeMakeWithSeconds(1, 2);
__block typeof(self) weakSelf = self;
//这个返回的对象要强引用
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
[weakSelf setSliderWithCurrentTime:CMTimeGetSeconds(time) durationTime:CMTimeGetSeconds(weakSelf.playerItem.duration)];
}];
播放结束通知
//注意强引用
self.playEndObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
拖动进度条
- (void)beginSlider {
//可以存储播放速度
//停止播放然后移除播放时间监听
[self.player pause];
[self.player removeTimeObserver:self.timeObserver];
}
- (void)progressSlider {
//取消上一次的查找
[self.playerItem cancelPendingSeeks];
//移动到播放对应时间
[self.player seekToTime:CMTimeMakeWithSeconds(self.slider.value, 1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
- (void)endSlider {
//存储拖动前的播放速度前提下,可以根据播放速度进一步进行处理
//添加播放时间监听
[self addTimeObserver];
[self.player play];
}
获取对应时间的图像
//需要强引用对象
self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:_asset];
//指定获取图片的大小,图片会根据宽度自动调整高度
self.imageGenerator.maximumSize = CGSizeMake(200, 0);
CMTime durationTime = _asset.duration;
//存储需要获取图片的时间点
NSMutableArray *times = [NSMutableArray array];
//将影片时间分为20个点
CMTimeValue increment = durationTime.value/20;
CMTimeValue currentValue = kCMTimeZero.value;
while (currentValue <= durationTime.value) {
CMTime currentTime = CMTimeMake(currentValue, durationTime.timescale);
[times addObject:[NSValue valueWithCMTime:currentTime]];
currentValue += increment;
}
//请求图片
[self.imageGenerator generateCGImagesAsynchronouslyForTimes:times completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
if(result == AVAssetImageGeneratorSucceeded){
//主线线程处理
}
}];
显示字幕
//加载字幕或者语音需要先加载资源的availableMediaCharacteristicsWithMediaSelectionOptions属性
AVMediaSelectionGroup *group = [self.asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
//获取和本地一样的语言字幕
NSArray *options = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:group.options withLocale:[NSLocale currentLocale]];
[_playerItem selectMediaOption:options[0] inMediaSelectionGroup:group];
//遍历循环查找英文字幕
for(AVMediaSelectionOption *option in group.options){
if([option.displayName isEqualToString:@"English"]){
[_playerItem selectMediaOption:option inMediaSelectionGroup:group];
}
}