web-dev-qa-db-ja.com

iOS:AVPlayer-ビデオの現在のフレームのスナップショットを取得する

私は一日を過ごし、多くのSO回答、Apple参照、ドキュメントなどを調べましたが、成功しませんでした。

簡単なことをしたい:AVPlayerを使用してビデオを再生していて、一時停止して現在のフレームをUIImageとして取得したい。それでおしまい。

私のビデオはインターネット上にあるm3u8ファイルで、通常はAVPlayerLayerで問題なく再生されます。

私は何を試しましたか:

  1. AVAssetImageGenerator機能していません。メソッドcopyCGImageAtTime:actualTime: error:はnull画像参照を返します。答えによると ここAVAssetImageGeneratorはストリーミングビデオでは機能しません。
  2. プレーヤービューのスナップショットを作成します。最初にAVPlayerLayerrenderInContext:を試しましたが、レンダリングされていないことに気付きました。この種の「特別な」レイヤー。次に、iOS7で導入された新しいメソッド-drawViewHierarchyInRect:afterScreenUpdates:を見つけました。これは、特別なレイヤーもレンダリングできるはずですが、運が悪かったので、ビデオが表示される空白の黒い領域でUIスナップショットを取得しました。
  3. AVPlayerItemVideoOutputAVPlayerItemのビデオ出力を追加しましたが、hasNewPixelBufferForItemTime:を呼び出すたびにNOを返します。問題は再びビデオのストリーミングであると思います 私は一人ではありません この問題で。
  4. AVAssetReader試してみようと思っていたのですが、関連する質問を見つけて時間を無駄にしないことにしました こちら

とにかく今画面に表示されているもののスナップショットを取得する方法はありませんか?信じられない。

9
frangulyan

AVPlayerItemVideoOutputは、m3u8からは問題なく動作します。 hasNewPixelBufferForItemTimeに相談せず、単にcopyPixelBufferForItemTimeに電話したからかもしれませんか?このコードは、CVPixelBufferの代わりにUIImageを生成しますが、 その方法を説明してください という答えがあります。

この答えは主に ここ から切り取られました

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

@property (nonatomic) AVPlayer *player;
@property (nonatomic) AVPlayerItem *playerItem;
@property (nonatomic) AVPlayerItemVideoOutput *playerOutput;

@end

@implementation ViewController
- (void)setupPlayerWithLoadedAsset:(AVAsset *)asset {
    NSDictionary* settings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
    self.playerOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:settings];
    self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
    [self.playerItem addOutput:self.playerOutput];
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame = self.view.frame;
    [self.view.layer addSublayer:playerLayer];

    [self.player play];
}

- (IBAction)grabFrame {
    CVPixelBufferRef buffer = [self.playerOutput copyPixelBufferForItemTime:[self.playerItem currentTime] itemTimeForDisplay:nil];
    NSLog(@"The image: %@", buffer);
}

- (void)viewDidLoad {
    [super viewDidLoad];


    NSURL *someUrl = [NSURL URLWithString:@"http://qthttp.Apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"];
    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:someUrl options:nil];

    [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler:^{

        NSError* error = nil;
        AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error];
        if (status == AVKeyValueStatusLoaded)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self setupPlayerWithLoadedAsset:asset];
            });
        }
        else
        {
            NSLog(@"%@ Failed to load the tracks.", self);
        }
    }];
}

@end
6

AVAssetImageGeneratorは、ビデオのスナップショットを作成するための最良の方法です。このメソッドは、非同期でUIImageを返します:

import AVFoundation

// ...

var player:AVPlayer? = // ...

func screenshot(handler:@escaping ((UIImage)->Void)) {
    guard let player = player ,
        let asset = player.currentItem?.asset else {
            return
    }

    let imageGenerator = AVAssetImageGenerator(asset: asset)
    imageGenerator.appliesPreferredTrackTransform = true
    let times = [NSValue(time:player.currentTime())]

    imageGenerator.generateCGImagesAsynchronously(forTimes: times) { _, image, _, _, _ in
        if image != nil {
            handler(UIImage(cgImage: image!))
        }
    }
}

(それはSwift 4.2)

6
Axel Guilmin