私は一日を過ごし、多くのSO回答、Apple参照、ドキュメントなどを調べましたが、成功しませんでした。
簡単なことをしたい:AVPlayerを使用してビデオを再生していて、一時停止して現在のフレームをUIImage
として取得したい。それでおしまい。
私のビデオはインターネット上にあるm3u8ファイルで、通常はAVPlayerLayer
で問題なく再生されます。
私は何を試しましたか:
AVAssetImageGenerator
。機能していません。メソッドcopyCGImageAtTime:actualTime: error:
はnull画像参照を返します。答えによると ここAVAssetImageGenerator
はストリーミングビデオでは機能しません。AVPlayerLayer
でrenderInContext:
を試しましたが、レンダリングされていないことに気付きました。この種の「特別な」レイヤー。次に、iOS7で導入された新しいメソッド-drawViewHierarchyInRect:afterScreenUpdates:
を見つけました。これは、特別なレイヤーもレンダリングできるはずですが、運が悪かったので、ビデオが表示される空白の黒い領域でUIスナップショットを取得しました。AVPlayerItemVideoOutput
。AVPlayerItem
のビデオ出力を追加しましたが、hasNewPixelBufferForItemTime:
を呼び出すたびにNO
を返します。問題は再びビデオのストリーミングであると思います 私は一人ではありません この問題で。AVAssetReader
。試してみようと思っていたのですが、関連する質問を見つけて時間を無駄にしないことにしました こちら 。とにかく今画面に表示されているもののスナップショットを取得する方法はありませんか?信じられない。
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
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)