iOS获取视频缩略图
首先需要导入AVFoundation库。
UIImagePickerController
调用摄像头拍照,摄像的时候需要用到UIImagePickerController。
使用方法:
//实例化一个对象
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;//设置照片源类型
imagePicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];//设置媒体种类
imagePicker.allowsEditing = NO; //允许编辑
imagePicker.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto;//闪光灯模式
imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;//摄像头前置还是后置
imagePicker.delegate = self;//设置代理
//通过模态视图推出页面
[self presentViewController:imagePicker animated:YES completion:nil];
sourceType的三种类型分别是:
UIImagePickerControllerSourceTypeCamera照相机
UIImagePickerControllerSourceTypePhotoLibrary照片库 (用户不可删除)
UIImagePickerControllerSourceTypeSavedPhotosAlbum通过摄像头保存的照片(用户可删除)
如果需要对拍摄的照片或视频进行保存,需要给设置代理,然后遵守两个协议<UIImagePickerControllerDelegate,UINavigationControllerDelegate> ,实现下面的代理方法:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
通常情况下,在didFinish方法里面对照片或视频进行保存处理;在didCancel方法里dismissViewController。
获取视频缩略图
这里我用的filePath是本地视频的存储路径;网络视频URL同理。
/**
* 获取视频的缩略图方法
*
* @param filePath 视频的本地路径
*
* @return 视频截图
*/
- (UIImage *)getScreenShotImageFromVideoPath:(NSString *)filePath{
UIImage *shotImage;
//视频路径URL
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL 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;
}
作者:Goyakod_張较瘦
链接:https://www.jianshu.com/p/6e572b198143
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
中间截图
很多视频的第一个关键帧都是纯黑或纯白画面(如一些在YouTube下载的视频),那么我们在获取缩略图的时候,可以获取视频时长中间的关键帧,当然还是有一定几率会获取到纯黑纯白的画面(如时长中间的画面恰好就是纯黑),这个如果没有对画面进行检测,基本上是不可避免的,不算是bug。
public static Bitmap getVideoMiddleThumbnail(String path, int width, int height) {
float maxSize = Math.max(width, height);
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Bitmap bitmap = null;
try {
retriever.setDataSource(path);
// 获取时长,单位:毫秒
String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
// 取视频时长中点的最近一个关键帧
long frameTime = Long.parseLong(time) * 1000 / 2;
// 参数单位为微秒,1毫秒 = 1000微秒
bitmap = retriever.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
// Assume this is a corrupt video file.
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
// Ignore failures while cleaning up.
}
}
if (bitmap == null) return null;
int tempWidth = bitmap.getWidth();
int tempHeight = bitmap.getHeight();
int max = Math.max(tempWidth, tempHeight);
if (max > maxSize) {
float scale = maxSize / max;
int w = Math.round(scale * tempWidth);
int h = Math.round(scale * tempHeight);
bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
}
return bitmap;
//return ThumbnailUtils.extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
}
————————————————
版权声明:本文为CSDN博主「ithouse」的原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ithouse/article/details/88680633
ios设置视频封面,获取视频任意帧,并进行三级缓存
背景:
项目中有很多视频展示,点击播放的功能,视频的展示封面需要获取视频第一帧画面显示。又因为在列表中用的多,滑动时每次去获取比较耗时。
思路:
创建UIImageView的分类,参考SDWebImage实现,获取到视频第一帧画面时,以视频链接哈希串作为key,image作为值,利用SDImageCache进行图片的三级缓存(磁盘、内存和网络层)。 流程:
- 给UIImageView设置图片;
- 先去缓存查找,有则返回;
- 再去磁盘查找,有则返回;
- 都没找到,截取视频某一帧画面转为image,缓存至磁盘。
实现:
#import "UIButton+VideoImage.h"
#import <SDWebImage/SDImageCache.h>
@implementation UIButton (VideoImage)
- (void)videoImageWithvideoURL:(NSURL *)videoURL atTime:(NSTimeInterval)time {
//先从缓存中查找是否有图片
SDImageCache *cache = [SDImageCache sharedImageCache];
UIImage *memoryImage = [cache imageFromMemoryCacheForKey:videoURL.absoluteString];
if (memoryImage) {
[self setBackgroundImage:memoryImage forState:UIControlStateNormal];
return;
}else{
//再从磁盘中查找是否有图片
UIImage *diskImage = [cache imageFromDiskCacheForKey:videoURL.absoluteString];
if (diskImage) {
[self setBackgroundImage:diskImage forState:UIControlStateNormal];
return;
}
}
if (!time) {
time = 1;
}
//如果都不存在,开启异步线程截取对应时间点的画面,转成图片缓存至磁盘
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
NSParameterAssert(asset);
AVAssetImageGenerator *assetImageGenerator =[[AVAssetImageGenerator alloc] initWithAsset:asset];
assetImageGenerator.appliesPreferredTrackTransform = YES;
assetImageGenerator.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels;
CGImageRef thumbnailImageRef = NULL;
CFTimeInterval thumbnailImageTime = time;
NSError *thumbnailImageGenerationError = nil;
thumbnailImageRef = [assetImageGenerator copyCGImageAtTime:CMTimeMake(thumbnailImageTime, 60)actualTime:NULL error:&thumbnailImageGenerationError];
if(!thumbnailImageRef)
NSLog(@"thumbnailImageGenerationError %@",thumbnailImageGenerationError);
UIImage*thumbnailImage = thumbnailImageRef ? [[UIImage alloc]initWithCGImage: thumbnailImageRef] : nil;
dispatch_async(dispatch_get_main_queue(), ^{
SDImageCache * cache = [SDImageCache sharedImageCache];
[cache storeImage:thumbnailImage forKey:videoURL.absoluteString toDisk:YES completion:nil];
[self setBackgroundImage:thumbnailImage forState:UIControlStateNormal];
});
});
}
@end
iOS获取视频任意帧,出现帧图偏差
应用场景====在项目中需要根据视频内容,由用户自行获取视频封面,为此,开发出一个简单的类似相机视频预览的功能
底部横条是根据视频时间等分的时间节点获取对应的视频封面(这里取的是十等分)其实就是设置十个图片,然后按时间节点获取视频截图放进去(这里就不作代码介绍了)
那个选中帧放大且可移动效果用的是uislider,通过将滑动的点设为获取到的视频帧图而实现当前效果
重点在于下面这个视频帧获取的问题:
-(UIImage*)thumbnailImageWithAtTime:(NSTimeInterval)inputTime
{
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_videoUrl options:nil];
//获取视频缩略图
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
CMTime requestTime;
requestTime =CMTimeMakeWithSeconds(inputTime, 600); // CMTime是表示电影时间信息的结构体,第一个参数表示是视频当前时间,第二个参数表示每秒帧数
NSError* error =nil;
CMTime actualTime;
CGImageRefimageRef = [imageGeneratorcopyCGImageAtTime:requestTime actualTime:&actualTimeerror:&error];
UIImage *thumbImg = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
return thumbImg;
}
然而当你输入你想要获取的时间节点时,就会出现一个问题,获取的图片差强人意的解决办法
为什么呢?这里要先看下CMTime的内容
CMTime是表示视频时间信息的结构体。
它存在两个构造函数:CMTimeMake 、CMTimeMakeWithSeconds
CMTimeMake(a,b) a当前第几帧, b每秒钟多少帧(例如):
CMTime showTime =CMTimeMake(900, 300);
CMTimeShow(showTime);
输出: {900/300 = 3.000}
代表处于900帧,一秒钟300帧,当前时间为3秒
CMTimeMakeWithSeconds(a,b) a当前时间,b每秒钟多少帧.
CMTime showTime =CMTimeMakeWithSeconds(3, 300);
CMTimeShow(showTime);
输出: {900/300 = 3.000}
代表当前时间为3s,一秒钟300帧,处于900帧
按目前的分析来看,根据这个方法来做是可以获取到帧图的,为什么会出现偏差呢?
将requestTime与actualTime OUTPUT出来,
CMTimeShow(requestTime),CMTimeShow(actualTime)会发现两者之间偏差很大
后来查了一下资料,获取到了相关信息:
当我们获取某个时间点的某一帧时,首先会在范围内寻找,有索引内的关键帧(比如整数秒帧)或者缓存,就会直接返回,进而优化性能。
文档中定义相关范围的的API是 requestedTimeToleranceAfter 和 requestedTimeToleranceBefore
所以,我们处理获取任意帧的问题就出在这,只要补上:
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
就可以了
iOS获取视频截图&缩略图
写在开篇
周六需要测试各类视频源和各种方法获取缩略图的时间,于是写个小demo,简单查了下几个相关帖子,不知道是不是我查询的关键词有问题,结果太杂,有用的呢排版感觉又不够友好,自己整理完就觉得还是发出来一下吧,毕竟多一份参考也是好的。话不多说。上代码。
获取视频截图&缩略图
#define k_THUMBNAIL_IMG_WIDTH 100//缩略图及cell大小
#define k_FPS 1//一秒想取多少帧
//这本来是个异步调用,但写成这种方便大家看和复制来直接测试
- (UIImage*)getVideoThumbnailWithUrl:(NSURL*)videoUrl second:(CGFloat)second
{
if (!videoUrl)
{
NSLog(@"WARNING:videoUrl为空");
return nil;
}
AVURLAsset *urlSet = [AVURLAsset assetWithURL:videoUrl];
AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlSet];
imageGenerator.appliesPreferredTrackTransform = YES;
imageGenerator.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels;
/*
如果不需要获取缩略图,就设置为NO,如果需要获取缩略图,则maximumSize为获取的最大尺寸。
以BBC为例,getThumbnail = NO时,打印宽高数据为:1920*1072。
getThumbnail = YES时,maximumSize为100*100。打印宽高数据为:100*55.
注:不乘[UIScreen mainScreen].scale,会发现缩略图在100*100很虚。
*/
BOOL getThumbnail = YES;
if (getThumbnail)
{
CGFloat width = [UIScreen mainScreen].scale * k_THUMBNAIL_IMG_WIDTH;
imageGenerator.maximumSize = CGSizeMake(width, width);
}
NSError *error = nil;
CMTime time = CMTimeMake(second,k_FPS);
CMTime actucalTime;
CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:&actucalTime error:&error];
if (error) {
NSLog(@"ERROR:获取视频图片失败,%@",error.domain);
}
CMTimeShow(actucalTime);
UIImage *image = [UIImage imageWithCGImage:cgImage];
NSLog(@"imageWidth=%f,imageHeight=%f",image.size.width,image.size.height);
CGImageRelease(cgImage);
return image;
}
需要说明的都大概写了注释,写了个demo,点击按钮后,会将对应片源每秒取k_FPS帧图片,顺序展示在collectionview上。 稍后会把demo发上来,有问题可以留言或者私信讨论