AVFoundation-シンプルなビデオプレーヤーの実装

デモ

コアクラス

AVAsset-ビデオリソースオブジェクト、ローカルまたはネットワークのURLを渡す必要があります

AVPlayerItem-ビデオの長さとさまざまな状態を取得します。作成したAVAssetオブジェクトを渡す必要があります

AVPlayer-ビデオ再生クラス。作成したAVPlayerItemオブジェクトを渡す必要があります

AVPlayerLayer-ビデオ表示レイヤー。ビューツリーに置き換えたり挿入したりできます。

AVAssetImageGenerator-ビデオの特定のフレームのサムネイルを取得するか、特定の時間に取得するか、バッチで取得することができます

ステップ1:初期化

1.创建AVAsset
_asset = [AVAsset assetWithURL:assetURL];

2.创建AVPlayerItem,可以传入所需要监听的属性,例如视频时长duration
NSArray *keys = @[
        @"tracks",
        @"duration",
        @"commonMetadata",
        @"availableMediaCharacteristicsWithMediaSelectionOptions"
    ];
self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset automaticallyLoadedAssetKeys:keys];

3.通过KVO给AVPlayerItem添加视频播放状态的监听
[self.playerItem addObserver:self forKeyPath:@"status" options:0 context:&PlayerItemStatusContext];   

4.创建AVPlayer
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

5.创建显示视频用的AVPlayerLayer,并设置之前创建好的AVPlayer为视频播放器,插入到当前视图树中
AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:player];
复制代码

ステップ2:初めてプレイする

[self.player play];

在第一步的KVO监听中监听到AVPlayerItem的播放状态为AVPlayerItemStatusReadyToPlay,调用AVPlayer的play方法开始播放,这里还写了其它方法,例如监听当前播放时长,批量获取缩略图等,后面会放具体实现

if (context == &PlayerItemStatusContext) {

        dispatch_async(dispatch_get_main_queue(), ^{
            //移除监听
            [self.playerItem removeObserver:self forKeyPath:@"status"];

            if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) {
                //添加视频当前播放时间的监听
                [self addPlayerItemTimeOberver];
                //视频总时长
                CMTime duration = self.playerItem.duration;
                [self.delegate playerDidChangeTime:CMTimeGetSeconds(kCMTimeZero) withAssertDuration:CMTimeGetSeconds(duration)];
                //播放
                [self.player play];
                //批量获取缩略图
                [self generateThumbnails];
            }
        });
    }
复制代码

ステップ3:一時停止

[self.player pause];

ステップ4:一時停止後も再生を続行します。倍速が設定されている場合は、一時停止後にリセットされます。ここで速度をリセットする必要があり、再生方法の後に配置する必要があります。そうしないと、効果がありません。

[self.player play]; self.player.rate = _playRate

ステップ5:現在の再生時間を取得し、初期化されたKVOモニターでAVPlayerの次のメソッドを呼び出します

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block

- (void)addPlayerItemTimeOberver
{

    CMTime interval = CMTimeMakeWithSeconds(0.5f, NSEC_PER_SEC);

    dispatch_queue_t queue = dispatch_get_main_queue();

    __weak typeof(self)weakSelf = self;

    self.timeObserver = [self.player addPeriodicTimeObserverForInterval:interval queue:queue usingBlock:^(CMTime time) {

        NSTimeInterval currentTime = CMTimeGetSeconds(time);

        NSTimeInterval duration = CMTimeGetSeconds(weakSelf.playerItem.duration);

        [weakSelf.delegate playerDidChangeTime:currentTime withAssertDuration:duration];

    }];

}
复制代码

ステップ6:再生を評価し、AVPlayerのレート属性を設定します

self.player.rate = playRate

ステップ7:再生する特定の時点に移動します。詳細は次のとおりです。指を押すと、再生を停止して再生時間の通知を削除する必要があります。指を離すと、再生を続行し、現在の再生を監視します。間隔

[self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];

//手指按下
- (void)progressSliderDidStartChange
{

    [self.player pause];

    [self.player removeTimeObserver:self.timeObserver];

    self.timeObserver = nil;

}
//开始移动
- (void)progressSliderSeekToTime:(NSTimeInterval)time
{

    [self.playerItem cancelPendingSeeks];

    [self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];

}
//手指离开
- (void)progressSliderDidEnd
{

    [self addPlayerItemTimeOberver];

    [self.player play];

    self.player.rate = _playRate;

}
复制代码

ステップ8:サムネイルをバッチで取得し、視覚的なプログレスバーを作成します

AVAssetImageGeneratorを作成し、AVAssetタイプを渡します

AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];

サムネイルのバッチ取得メソッド

- (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler

ある時点でサムネイルを取得する

- (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError

- (void)generateThumbnails
{

    AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];

    imageGenerator.maximumSize = CGSizeMake(200, 0);

    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 NSInteger imageCount = times.count;

    __block NSMutableArray *images = [NSMutableArray array];

    [imageGenerator generateCGImagesAsynchronouslyForTimes:times completionHandler:^(CMTime requestedTime, CGImageRef  _Nullable imageRef, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {

        if (result == AVAssetImageGeneratorSucceeded) {

            UIImage *image = [UIImage imageWithCGImage:imageRef];

            QXThumbnail *thumbnail = [QXThumbnail thumbilWithImage:image time:actualTime];

            [images addObject:thumbnail];

        } else {

            NSLog(@"Error:%@",error.localizedDescription);

        }

        if (--imageCount == 0) {

            dispatch_async(dispatch_get_main_queue(), ^{

                [self.delegate playerDidGenerateThumbnails:[images copy]];

            });

        }

    }];

}
复制代码

この時点で、簡単なビデオプレーヤーが完成します。もちろん、対処すべき詳細はまだたくさんあります。これは、友人が理解する必要があります。オーディオとビデオの学習は、長くて興味深い道である必要があります。

おすすめ

転載: juejin.im/post/7085551882071965709