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

statusAVPlayerStatusFailedAVPlayer将不能播放,才是需要重新创建对象

拓展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];
    }
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐