iOS AVPlayer截取视频首帧缩略图,常规mp4与m3u8
2024-07-18 本文已影响0人
假如兔子失了尾
获取常规格式视频缩略图(适用于mp4、mov等)
/**
* 获取视频的缩略图方法
* @param videoUrl 视频链接 支持本地路径和网络路径
* @return 视频截图
*/
+ (UIImage *)getVideoImageFromPath:(NSURL *)videoUrl{
UIImage *shotImage;
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
gen.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMakeWithSeconds(0.0, 600);
NSError *error = nil;
CMTime actualTime;
CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
shotImage = [[UIImage alloc] initWithCGImage:image];
CGImageRelease(image);
return shotImage;
}
获取m3u8格式视频缩略图
m3u8格式是适用于直播流的格式,加载时是以切片的模式分段加载,无法从视频流中直接获取某一帧图片
这里是使用AVPlayerItemVideoOutput,采用截屏输出的方案来获取首帧图
该方法一定要视频在可播放的状态下才能取到,如果该缩略图时做封面图使用,建议还是和服务端的同事沟通一下参数方案吧
一、声明对象
/// 播放器
@property (nonatomic, strong) AVPlayer *player;
/// 播放器item
@property (nonatomic, strong) AVPlayerItem *playerItem;
/// 播放器layer
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
/// 视频截图输出
@property (nonatomic, strong) AVPlayerItemVideoOutput *output;
二、初始化播放器时,给播放器添加输出对象
//包装到Asset,防止抓包泄露视频地址
AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:@"视频链接"]];
//创建播放源
self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
//创建播放器
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
//设置播放控件
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
//已经创建过了,代表已经添加到playerItem中,这里先移除,防止重复
if (self.output) {
[self.playerItem removeOutput:self.output];
}
NSDictionary *settings = @{(id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)};
self.output = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:settings];
[self.playerItem addOutput:self.output];
播放器销毁时要移除output对象并置为nil
[self.playerItem removeOutput:self.output];
self.output = nil;
三、监听播放器缓存到可以播放的状态
获取首帧缩略图时一定要监听该状态,否则在此之前去截取会失败
//监听缓存足够播放的状态
[self.playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
播放器销毁时移除
[self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
四、去取首帧缩略图
一定要先暂停,不然取到的图片不一定是首帧图,存在着播放中的误差
//播放器状态监听
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {//缓冲到足够播放的状态
[self.player pause];
//这里等待0.5秒时为了等player绘制完成,否则取不到video缩略图
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//取缩略图
UIImage *thumImg = [self getThumbnailImageCompletion];
[self.player play];
});
}
}
核心代码,如果播放器有截屏的功能也可以用该方法,获取当前播放帧的图片
- (UIImage *)getThumbnailImageCompletion{
UIImage *frameImg = nil;
//确保当前时间的视频已经加载完成
CVPixelBufferRef pixelBuffer = [self.output copyPixelBufferForItemTime:self.player.currentTime itemTimeForDisplay:nil];
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
CIContext *temporaryContext = [CIContext contextWithOptions:nil];
CGImageRef videoImage = [temporaryContext createCGImage:ciImage
fromRect:CGRectMake(0, 0,
CVPixelBufferGetWidth(pixelBuffer),
CVPixelBufferGetHeight(pixelBuffer))];
frameImg = [UIImage imageWithCGImage:videoImage];
CGImageRelease(videoImage);
CVBufferRelease(pixelBuffer);
return frameImg;
}