AV Foundation专题

AVFoundation:视频播放

2020-09-01  本文已影响0人  MonKey_Money

视频播放所设计到的类

image.png

1、AVPlayer
AVPlayer用来播放基于时间的视听媒体的控制器对象。支持播放从本地、分步下载或通过HTTP Live Streaming协议得到的流媒体,并在多种播放场景中播放这些视频资源。
AVPlayer是一个不可见组件,如果播放MP3或AAC音频文件,那么没有可视化的用户界面不会有什么问题。如果是要播放一个QuickTime电影或一个MPEG-4视频,就会导致非常不好的用户体验,要将视频资源导出到用户界面的目标位置,需要使用AVPlayerLayer类。
AVPlayer管理一个单独资源的播放。AVqueuePlayer可以用来管理一个资源队列,当需要在一个序列中播放多个条目或者为视频、音频设置播放循环时可以使用。

2、AVPlayerLayer
AVPlayerLayer构建于Core Animation之上,是AV Foundation中位数不多的可见组件。
Core Animation本身有基于时间的属性,并且基于OpenGL,有很好的性能,能够满足AV Foundation的各种需求。
AVPlayerLayer扩展了Core Animation的CALayer类,通过框架在屏幕上显示视频内容。创建他需要一个指向AVPlayer实例的指针,将图层和播放器绑定在一起,保证当播放器基于时间的方法出现时使二者保持同步。它与CALayer一样,可以设置为UIView的备用层或者手动添加到一个已有的层继承关系中。

AVPlayerLayer中可以自定义的领域只有Video gravity,确定在承载层的范围内视频可以拉伸或缩放的程度。

AVLayerVideoGravityResizeAspect --在承载层范围内缩放视频大小来保持视频原始宽高比,默认值,适用于大部分情况
AVLayerVideoGravityResizeAspectFill --保留视频宽高比,通过缩放填满层的范围区域,会导致视频图片被部分裁剪。
AVLayerVideoGravityResize --拉伸视频内容拼配承载层的范围,会导致图片扭曲,funhouse effect效应。
3、AVPlayerItem
AVAsset只包含媒体资源静态信息,无法实现播放功能。需要对一个资源及其相关曲目进行播放时,首先需要通过AVPlayerItem和APlayerItemTrack来构建相应的动态内容。
AVPlayerItem建立媒体资源动态视角的数据模型并保存AVPlayer在播放资源时的呈现状态。AVPlayerItem由一个或多个媒体曲目组成,由AVPlayerItemTrack建立模型。AVPlayerItemTrack实例用于表示播放器条目中的类型统一的媒体流。AVPlayerItem中曲目直接与AVAsset中的AVAssetTrack实例相对应。

示例

    AVAsset *   asset = [AVAsset assetWithURL:assetURL];//创建AVAsset对象
  NSArray *keys = @[
        @"tracks",
        @"duration",
        @"commonMetadata",
    ];
 //keys为AVAsset的属性,在状态为AVPlayerItemStatusReadyToPlay时这些信息被加载完成。
    self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset automaticallyLoadedAssetKeys:keys]; //创建AVPlayerItem对象
    [self.playerItem addObserver:self forKeyPath:@"status" options:0 context:&PlayerItemStatusContext]; //监听status属性,当加载完成我们开始播放
    self.avPlayer = [AVPlayer playerWithPlayerItem:self.playerItem]; //创建player对象
    self.playerView = [[THPlayerView alloc] initWithPlayer:self.avPlayer]; //THPlayerView的layerClass为AVPlayerLayer。
//当AVPlayerItem状态为AVPlayerItemStatusReadyToPlay,开始播放视频
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    
    // Listing 4.7
    if (context == &PlayerItemStatusContext) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) {
                [self.avPlayer play];
            }
        });
    }
    
}

定期监听

我们可以通过监听时间来定制化UI,

- (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);
//更新UI的进度条
    };
    
    // Add observer and store pointer for future use
    self.timeObserver =                                                     // 5
        [self.player addPeriodicTimeObserverForInterval:interval
                                                  queue:queue
                                             usingBlock:callback];
}

边界时间监听

//times 值为CMTime值组成的一个NSArray ,我们可以设置数组,在每次值时会有回调方法
- (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(void))block;

条目结束监听

   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];

生成图片

 self.imageGenerator =                                                   // 1
        [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];
    
    // Generate the @2x equivalent
    self.imageGenerator.maximumSize = CGSizeMake(200.0f, 0.0f);             // 2

    CMTime duration = self.asset.duration;

    NSMutableArray *times = [NSMutableArray array];                         // 3
    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;                            // 4
    __block NSMutableArray *images = [NSMutableArray array];

    AVAssetImageGeneratorCompletionHandler handler;                         // 5
    
    handler = ^(CMTime requestedTime,
                CGImageRef imageRef,
                CMTime actualTime,
                AVAssetImageGeneratorResult result,
                NSError *error) {
//注意blcok内是异步
        if (result == AVAssetImageGeneratorSucceeded) {                     // 6
            UIImage *image = [UIImage imageWithCGImage:imageRef];
            id thumbnail =
                [THThumbnail thumbnailWithImage:image time:actualTime];
            [images addObject:thumbnail];
        } else {
            NSLog(@"Error: %@", [error localizedDescription]);
        }
//需要回到主线程更新UI
    };

    [self.imageGenerator generateCGImagesAsynchronouslyForTimes:times       // 8
                                              completionHandler:handler];

显示字幕

key中加availableMediaCharacteristicsWithMediaSelectionOptions

- (void)loadMediaOptions {
    NSString *mc = AVMediaCharacteristicLegible;                            // 1
    AVMediaSelectionGroup *group =
        [self.asset mediaSelectionGroupForMediaCharacteristic:mc];          // 2
    if (group) {
        NSMutableArray *subtitles = [NSMutableArray array];                 // 3
        for (AVMediaSelectionOption *option in group.options) {
            [subtitles addObject:option.displayName];
        }
        [self.transport setSubtitles:subtitles];                            // 4
    } else {
        [self.transport setSubtitles:nil];
    }
}

Airplay

AirPlay(隔空播放)是苹果公司的在iOS和macOS中的无线投屏技术,可以将iPhone、iPod Touch、iPad及Mac的视频或屏幕镜像传送到支持Airplay的设备
AVPlayer有一个属性allowsExternalPlayback,允许启用或禁用AirPlay播放功能,该属性默认是YES,即在不做任何额外编码的情况下,播放器应用程序也会自动支持Airplay功能。虽然通常AirPlay功能是需要的,不过如果由于某些强制的原因要禁用该功能,可以通过设置allowsExternalPlayback为NO来实现。

上一篇 下一篇

猜你喜欢

热点阅读