视频图与视频是否在播放没有关系的,这里演示截图只是为了演示,截取当前正在播放的那一个点(当前界面内容)
示例代码:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
@interface ViewController ()
// 截取正在播放的视频后,用来显示截取的图片
@property (weak, nonatomic) IBOutlet UIImageView *showSnipImageView;
// 因为在封装的截图方法内需要使用播放控制器,所以声明一个全局属性
@property (nonatomic,strong) AVPlayerViewController *playerViewController;
@end
@implementation ViewController
// 开始播放
- (IBAction)ClickStartButton:(id)sender {
// 创建播放控制器
self.playerViewController = [[AVPlayerViewController alloc]init];
// 获取本地视频资源路径
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"minion_01.mp4" ofType:nil];
// 设置播放器
self.playerViewController.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]];
// 自定义视图
self.playerViewController.view.frame = CGRectMake(50, 200, 320, 300);
[self.view addSubview:self.playerViewController.view];
// 开始播放
[self.playerViewController.player play];
}
// 截图
- (IBAction)clickSnipButton:(id)sender {
// 上面为了简单,没有通过资源图片的方式加载视频,使用了URL路径的方式
// 获取本地视频资源路径
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"minion_01.mp4" ofType:nil];
// 获取资源文件
AVAsset *imageAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
// 创建资源图片生成器
AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:imageAsset];
// 获取截图的时间 当前播放的时间
NSValue *currentTimeValue = [NSValue valueWithCMTime:self.playerViewController.player.currentTime];
/*
生成图片 (异步处理)
参数1: 时间(需要一个存放NSValue类型的数组)
参数2:完成回调
*/
[generator generateCGImagesAsynchronouslyForTimes:@[currentTimeValue] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
// 回到主线程刷新UI
dispatch_sync(dispatch_get_main_queue(), ^{
self.showSnipImageView.image = [UIImage imageWithCGImage:image];
});
/*
注意点: 刷新UI需要回归主线程,并且在回调block内,刷新UI需要使用内部的image参数
如果异步处理,当线程执行到这行代码会开启新线程,跳过执行后面的代码,而不会等待该代码块执行完毕
等到新线程访问image的时候,有可能image已经被释放了,就会出现野指针访问,所以需要同步
(当回调block代码块执行完毕,image就会被释放,image是一个内部参数,外部并没有进行强引用)
并非所有的情况下主队列同步都会造成死锁,此方法block内部代码默认在子线程执行
刷新UI需要放在主线程,所以使用了主队列同步处理任务,保证了在内部参数image释放前,block代码块结束前完成UI刷新操作
dispatch_async(dispatch_get_main_queue(), ^{
self.showSnipImageView.image = [UIImage imageWithCGImage:image];
});
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.showSnipImageView.image = [UIImage imageWithCGImage:image];
}];
*/
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
@end
注意点:
刷新UI需要回归主线程,并且在回调block内,刷新UI需要使用内部的image参数 如果异步处理,当线程执行到这行代码会开启新线程,跳过执行后面的代码,而不会等待该代码块执行完毕 等到新线程访问image的时候,有可能image已经被释放了,就会出现野指针访问,所以需要同步 (当回调block代码块执行完毕,image就会被释放,image是一个内部参数,外部并没有进行强引用)
并非所有的情况下主队列同步都会造成死锁,此方法block内部代码默认在子线程执行 刷新UI需要放在主线程,所以使用了主队列同步处理任务,保证了在内部参数image释放前,block代码块结束前完成UI刷新操作
效果图: