iOS: AVPlayer - 获取视频当前帧的快照
iOS: AVPlayer - getting a snapshot of the current frame of a video
我花了一整天的时间查看了很多 SO 答案、Apple 参考资料、文档等,但没有成功。
我想要一个简单的东西:我正在使用 AVPlayer 播放视频,我想暂停它并获取当前帧 UIImage
。就是这样。
我的视频是网上的m3u8文件,在AVPlayerLayer
中正常播放没有任何问题。
我尝试了什么:
AVAssetImageGenerator
. 它不起作用,方法 copyCGImageAtTime:actualTime: error:
returns 空图像参考。根据答案 AVAssetImageGenerator
不适用于流媒体视频。
- 拍摄播放器视图的快照。 我先在
renderInContext:
上尝试了 AVPlayerLayer
,但后来我意识到它不会渲染这种 "special"层。然后我发现了 iOS 7 中引入的一种新方法 - drawViewHierarchyInRect:afterScreenUpdates:
应该也可以渲染特殊层,但运气不好,仍然得到了带有空白黑色区域的 UI 快照显示视频。
AVPlayerItemVideoOutput
. 我已经为我的 AVPlayerItem
添加了视频输出,但是每当我调用 hasNewPixelBufferForItemTime:
它时 returns NO
。我想问题又出在流式视频和 I am not alone 上。
AVAssetReader
.本来想试试的,但是发现相关问题后决定不浪费时间here.
那么有没有办法获取我现在在屏幕上看到的内容的快照?我简直不敢相信。
AVPlayerItemVideoOutput
在 m3u8 上对我来说很好用。也许是因为我不咨询hasNewPixelBufferForItemTime
而直接调用copyPixelBufferForItemTime
?此代码生成 CVPixelBuffer
而不是 UIImage
,但有答案 describe how to do that。
此答案主要摘自 here
#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
是拍摄视频的最佳方法,此方法 return 异步 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)
我花了一整天的时间查看了很多 SO 答案、Apple 参考资料、文档等,但没有成功。
我想要一个简单的东西:我正在使用 AVPlayer 播放视频,我想暂停它并获取当前帧 UIImage
。就是这样。
我的视频是网上的m3u8文件,在AVPlayerLayer
中正常播放没有任何问题。
我尝试了什么:
AVAssetImageGenerator
. 它不起作用,方法copyCGImageAtTime:actualTime: error:
returns 空图像参考。根据答案AVAssetImageGenerator
不适用于流媒体视频。- 拍摄播放器视图的快照。 我先在
renderInContext:
上尝试了AVPlayerLayer
,但后来我意识到它不会渲染这种 "special"层。然后我发现了 iOS 7 中引入的一种新方法 -drawViewHierarchyInRect:afterScreenUpdates:
应该也可以渲染特殊层,但运气不好,仍然得到了带有空白黑色区域的 UI 快照显示视频。 AVPlayerItemVideoOutput
. 我已经为我的AVPlayerItem
添加了视频输出,但是每当我调用hasNewPixelBufferForItemTime:
它时 returnsNO
。我想问题又出在流式视频和 I am not alone 上。AVAssetReader
.本来想试试的,但是发现相关问题后决定不浪费时间here.
那么有没有办法获取我现在在屏幕上看到的内容的快照?我简直不敢相信。
AVPlayerItemVideoOutput
在 m3u8 上对我来说很好用。也许是因为我不咨询hasNewPixelBufferForItemTime
而直接调用copyPixelBufferForItemTime
?此代码生成 CVPixelBuffer
而不是 UIImage
,但有答案 describe how to do that。
此答案主要摘自 here
#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
是拍摄视频的最佳方法,此方法 return 异步 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)