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发上来,有问题可以留言或者私信讨论

 
 
 
 
 
 
 
 
 
 
 
posted @ 2018-05-26 00:18  sundaysios  阅读(993)  评论(0)    收藏  举报