iOS视频播放
发布日期:2022-03-18 08:27:38 浏览次数:31 分类:技术文章

本文共 7549 字,大约阅读时间需要 25 分钟。

iOS视频播放

可参考文章:

基本

用到的类及其关系

用到的类及其关系

AVPlayer

AVPlayer是一个用来播放基于时间的视听媒体的控制器对象。支持播放从本地、分布下载或通过HTTP Live Streaming协议取得的流媒体。

AVPlayer是一个不可见组件。要将视频资源导出到用户界面上,需要使用AVPlayerLayer类。
AVPlayer只管理一个单独资源的播放,不过框架还提供了AVPlayer的一个子类AVQueuePlayer,可以用来管理一个资源队列。

AVPlayerLayer

创建一个AVPlayerLayer 需要一个指向AVPlayer实例的指针,这就将图层和播放器紧密绑定在一起。

AVPlayerLayer有一个videoGravity属性,可以定义三个不同的gravity值:

  • AVLayerVideoGravityResize - 会将视频内容拉伸来匹配承载层的范围
  • AVLayerVideoGravityResizeAspect - 会在承载层的范围内缩放视频大小来保持视频的原始宽高比
  • AVLayerVideoGravityResizeAspectFill - 将保留视频的宽高比,并使其通过缩放填满层的范围区域

AVAsset

AVAsset是一个抽象类和不可变类,定义了媒体资源混合呈现的方法,将媒体资源的静态属性模块化成一个整体,比如它们的标题、时长和元数据等。

根据AVAsset获取标题,需要用到Common键空间:

@implementation AVAsset (THAdditions)- (NSString *)title{    AVKeyValueStatus status = [self statusOfValueForKey:@"commonMetadata" error:nil];    if (status == AVKeyValueStatusLoaded) {        NSArray *items = [AVMetadataItem metadataItemsFromArray:self.commonMetadata                                           withKey:AVMetadataCommonKeyTitle                                          keySpace:AVMetadataKeySpaceCommon];        if (items.count > 0) {            AVMetadataItem *titleItem = [items firstObject];            return (NSString *)titleItem.value;        }    }    return nil;}@end

AVPlayerItem

AVAsset模型只包含媒体资源的静态信息,这些不变的属性用来描述对象的静态状态。当我们需要对一个资源及其相关曲目进行播放时,首先需要通过AVPlayerItemAVPlayerItemTrack类构建相应的动态内容。

AVPlayerItem由一个或多个媒体曲目组成,由AVPlayerItemTrack类建立模型。AVPlayerItemTrack实例用于表示播放器条目中的类型统一的媒体流,比如音频或视频。AVPlayerItem中的曲目直接与基础AVAsset 中的AVAssetTrack实例相对应。

NSURL *assetURL = [[NSBundle mainBundle] URLForResource:@"hubblecast" withExtension:@"m4v"];    AVAsset *asset = [AVAsset assetWithURL:assetURL];    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];    [playerItem addObserver:self forKeyPath:@"status" options:0 context:&PlayerItemStatusContext];    self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];    [playerLayer setFrame:self.view.bounds];    [self.view.layer addSublayer:playerLayer];

AVPlayerItem具有一个名为statusAVPlayerItemStatus类型的属性。在对象创建之初,statusAVPlayerItemStatusUnknown。需要通过KVO进制监测status属性值的来跟踪这一变化过程。需要等待状态由AVPlayerItemStatusUnknown变为AVPlayerItemStatusReadyToPlay

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    if (context == &PlayerItemStatusContext) {        AVPlayerItem *playerItem = (AVPlayerItem *)object;        if (playerItem.status == AVPlayerItemStatusReadyToPlay  ) {            [self.player play];        }    }}

时间处理

AVFoundation使用一种可靠性更高的方法来展示时间信息CMTime

CMTime的解释请参考

获取时间duration,返回类型为CMTimeCMTimeGetSeconds函数将CMTime的值转换为秒。

CMTime duration = self.playerItem.duration;

时间监听

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

行精准的监听。

定期监听

方法为- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;

  • interval - 通知周期间隔的CMTime值
  • queue - 通知发送的顺序调度队列
  • block - 一个在指定时间间隔中将会在队列上调用的回调块
- (void)addPlayerItemTimeObserver {    // Create 0.5 second refresh interval - REFRESH_INTERVAL == 0.5    CMTime interval =        CMTimeMakeWithSeconds(REFRESH_INTERVAL, NSEC_PER_SEC);              // 1    // Main dispatch queue    dispatch_queue_t queue = dispatch_get_main_queue();                     // 2    // Create callback block for time observer    __weak THPlayerController *weakSelf = self;                             // 3    void (^callback)(CMTime time) = ^(CMTime time) {        NSTimeInterval currentTime = CMTimeGetSeconds(time);        NSTimeInterval duration = CMTimeGetSeconds(weakSelf.playerItem.duration);        [weakSelf.transport setCurrentTime:currentTime duration:duration];  // 4    };    // Add observer and store pointer for future use    self.timeObserver =                                                     // 5        [self.player addPeriodicTimeObserverForInterval:interval                                                  queue:queue                                             usingBlock:callback];}

边界时间监听

方法为- (id)addBoundaryTimeObserverForTimes:(NSArray *)times queue:(dispatch_queue_t)queue usingBlock:(void (^)(void))block; 主要用于同步用户界面变更或随着视频播放记录一些非可视化数据。比如,可以定义25%、50%和75%边界的标记,以此判断用户播放进度。

  • times - CMTime值组成的一个NSArray数组定义了需要通知的边界点
  • queue - 队列
  • block 回调

条目结束监听

另一常见的需要监听的事件是条目播放完毕的时间。当播放完成时,AVPlayerItem会发送一个AVPlayerItemDidPlayToEndTimeNotification通知。

- (void)addItemEndObserverForPlayerItem {    NSString *name = AVPlayerItemDidPlayToEndTimeNotification;    NSOperationQueue *queue = [NSOperationQueue mainQueue];    __weak THPlayerController *weakSelf = self;                             // 1    void (^callback)(NSNotification *note) = ^(NSNotification *notification) {        [weakSelf.player seekToTime:kCMTimeZero                             // 2                  completionHandler:^(BOOL finished) {            [weakSelf.transport playbackComplete];                          // 3        }];    };    self.itemEndObserver =                                                  // 4        [[NSNotificationCenter defaultCenter] addObserverForName:name                                                          object:self.playerItem                                                           queue:queue                                                      usingBlock:callback];}

生成图片

AVAssetImageGenerator可以用来从一个AVAsset视频曲目中提取图片。

  • - (CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(CMTime *)actualTime error:(NSError **)outError 如果开发者希望捕捉一张图片,此方法最适合,可能用于在视频列表中展示视频缩略图
  • - (void)generateCGImagesAsynchronouslyForTimes:(NSArray *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler; 按照第一个参数所指定的时间段生成一个图片序列。
- (void)generateThumbnails {    self.imageGenerator =  [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];    // 指定一个width值为200、height值为0的CGSize,确保生产的图片都遵循一定的宽度    //并且会根据视频的宽高比自动设置高度值    self.imageGenerator.maximumSize = CGSizeMake(200.0f, 0.0f);    //计算生成CMTime值的集合    CMTime duration = self.asset.duration;    NSMutableArray *times = [NSMutableArray array];    CMTimeValue increment = duration.value / 20;    CMTimeValue currentValue = 2.0 * duration.timescale;    while (currentValue <= duration.value) {        CMTime time = CMTimeMake(currentValue, duration.timescale);        [times addObject:[NSValue valueWithCMTime:time]];        currentValue += increment;    }    __block NSUInteger imageCount = times.count;    __block NSMutableArray *images = [NSMutableArray array];    //回调块    AVAssetImageGeneratorCompletionHandler handler;    handler = ^(CMTime requestedTime,                CGImageRef imageRef,                CMTime actualTime,                AVAssetImageGeneratorResult result,                NSError *error) {        if (result == AVAssetImageGeneratorSucceeded) {            UIImage *image = [UIImage imageWithCGImage:imageRef];            id thumbnail = [THThumbnail thumbnailWithImage:image time:actualTime];            [images addObject:thumbnail];        } else {            NSLog(@"Error: %@", [error localizedDescription]);        }        // If the decremented image count is at 0, we're all done.        if (--imageCount == 0) {            dispatch_async(dispatch_get_main_queue(), ^{                NSString *name = THThumbnailsGeneratedNotification;                NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];                [nc postNotificationName:name object:images];            });        }    };    [self.imageGenerator generateCGImagesAsynchronouslyForTimes:times completionHandler:handler];}

转载地址:https://windzen.blog.csdn.net/article/details/52588556 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:iOS播放器常用功能
下一篇:iOS Layer动画 一(Swift)

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月05日 00时21分14秒