当前位置: 移动技术网 > 移动技术>移动开发>IOS > iOS 录像功能的简单实现

iOS 录像功能的简单实现

2018年09月20日  | 移动技术网移动技术  | 我要评论

话不多说,上 demo 这里用的是 svproresshud, 由于 ios10 的权限缘故,需要在 plist 里添加字段,否则会崩溃,具体请看上一篇

//
//  viewcontroller.m
//  录制视频
//
//  created by amydom on 16/8/25.
//  copyright © 2016年 amydom. all rights reserved.
//

#import "viewcontroller.h"
#import 
#import 
#import 

#import 
#import 
#import 
#import 

#import "svprogresshud.h"

static nsstring *const assetcollectionname = @"录制视频";
@interface viewcontroller ()
@property (weak, nonatomic) uiimageview *centerframeimageview;
@property (weak, nonatomic)  uilabel *videodurationlabel;
@property (nonatomic, assign) bool shouldasync;


@end

@implementation viewcontroller

- (void)viewdidload {
    [super viewdidload];
    [self createbtn];
#pragma mark - 视频相关
    /*
     一.保存图片到【camera roll】(相机胶卷)
     1.使用函数uiimagewritetosavedphotosalbum
     2.使用assetslibrary.framework(ios9开始, 已经过期)
     3.使用photos.framework(ios8开始可以使用, 从ios9开始完全取代assetslibrary.framework)
     
     二.创建新的【自定义album】(相簿\相册)
     1.使用assetslibrary.framework(ios9开始, 已经过期)
     2.使用photos.framework(ios8开始可以使用, 从ios9开始完全取代assetslibrary.framework)
     
     三.将【camera roll】(相机胶卷)的图片 添加到 【自定义album】(相簿\相册)中
     1.使用assetslibrary.framework(ios9开始, 已经过期)
     2.使用photos.framework(ios8开始可以使用, 从ios9开始完全取代assetslibrary.framework)
     
     四.photos.framework须知
     1.phasset : 一个phasset对象就代表一张图片或者一段视频
     2.phassetcollection : 一个phassetcollection对象就代表一本相册
     
     五.phassetchangerequest的基本认识
     1.可以对相册图片进行【增\删\改】的操作
     
     六.phphotolibrary的基本认识
     1.对相册的任何修改都必须放在以下其中一个方法的block中
     [[phphotolibrary sharedphotolibrary] performchangesandwait:error:];
     [[phphotolibrary sharedphotolibrary] performchanges:completionhandler:];
     */

    
}

- (void)createbtn{
    
    // 录制视频
    uibutton *recordvideo = [[uibutton alloc]initwithframe:cgrectmake(100, 100, 100, 100)];
    [recordvideo settitle:@"开始录制" forstate:uicontrolstatenormal];
    recordvideo.backgroundcolor = [uicolor lightgraycolor];
    [recordvideo addtarget:self action:@selector(videofromcamera) forcontrolevents:uicontroleventtouchupinside];
    [self.view addsubview:recordvideo];
    
    // 从选择视频
    uibutton *selectlocalvideo = [[uibutton alloc]initwithframe:cgrectmake(100, 250, 100, 100)];
    [selectlocalvideo settitle:@"选择视频" forstate:uicontrolstatenormal];
    selectlocalvideo.backgroundcolor = [uicolor lightgraycolor];
    [selectlocalvideo addtarget:self action:@selector(videofromphotos) forcontrolevents:uicontroleventtouchupinside];
    [self.view addsubview:selectlocalvideo];
}

// 录制视频
- (void)videofromcamera{
        [self getvideowithsourcetype:uiimagepickercontrollersourcetypecamera shouldasync:yes];
    
}

// 从相册中选择视频"
- (void)videofromphotos{
    //uiimagepickercontrollersourcetypesavedphotosalbum - 这个是自定义库,是由用户截图或保存到里面的
        [self getvideowithsourcetype:uiimagepickercontrollersourcetypesavedphotosalbum shouldasync:no];
}
//调用摄像头
- (void)getvideowithsourcetype:(uiimagepickercontrollersourcetype)type shouldasync:(bool)shouldasync{
    //取得授权状态
    avauthorizationstatus authstatus = [avcapturedevice authorizationstatusformediatype:avmediatypevideo];
    //判断当前状态
    if (authstatus == avauthorizationstatusrestricted
        || authstatus == avauthorizationstatusdenied) {
        //拒绝当前 app 访问[phtot]运行
        [svprogresshud showinfowithstatus:@"提醒用户打开访问开关 [设置] - [隐私] - [视频] - [app]"];
        return;
    }
    
    if ([uiimagepickercontroller issourcetypeavailable:type]) {
        uiimagepickercontroller *picker = [[uiimagepickercontroller alloc]init];
        picker.delegate = self;
        //可以编辑
        picker.allowsediting = yes;
        //设置资源获取类型
        picker.sourcetype = type;
        picker.mediatypes = @[(nsstring *)kuttypemovie];
        [self presentviewcontroller:picker animated:yes completion:null];
        self.shouldasync = shouldasync;
        
    }else{
        
        [svprogresshud showinfowithstatus:@"手机不支持摄像"];
        
    }
}
#pragma mark - uiimagepickercontrollerdelegate

- (void)imagepickercontroller:(uiimagepickercontroller *)picker didfinishpickingmediawithinfo:(nsdictionary *)info{
    //获取媒体 url
    nsurl * videourl = [info objectforkey:uiimagepickercontrollermediaurl];
    //判断版本号
    if ([uidevice currentdevice].systemversion.doublevalue < 9.0) {
        alassetslibrary * library = [[alassetslibrary alloc]init];
        //创建并行队列
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //判断相册时候兼容视频,兼容才能保存到相册
            if ([library videoatpathiscompatiblewithsavedphotosalbum:videourl]) {
                [library writevideoatpathtosavedphotosalbum:videourl completionblock:^(nsurl *asseturl, nserror *error) {
                    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        //回到主线程,写入相册
                        if (error == nil) {
                            avurlasset *videoasset = [[avurlasset alloc]initwithurl:asseturl options:nil];
                            float64 duration = cmtimegetseconds(videoasset.duration);
                            self.videodurationlabel.text = [nsstring stringwithformat:@"视频时长: %.0f秒",duration];
                            if (_shouldasync) {
                                __weak __typeof(self)weakself = self;
                                // get center frame image asyncly
                                [weakself centerframeimagewithvideourl:videourl completion:^(uiimage *image) {
                                    
                                    weakself.centerframeimageview.image = image;
                                    
                                }];
                                
                            } else {
                                
                                //同步获取中间帧图片
                                uiimage * image = [self frameimagefromvideourl:videourl];
                                self.centerframeimageview.image = image;
                            }
                            
                            // begin to compress and export the video to the output path
                            // 开始压缩和导出视频输出路径
                            nsstring * name = [[nsdate date] description];
                            name = [nsstring stringwithformat:@"%@.mp4", name];
                            [self compressvideowithvideourl:videourl savedname:name completion:^(nsstring *savedpath) {
                                
                                if (savedpath) {
                                    
                                     nslog(@"compressed successfully. path: %@", savedpath);
                                } else {
                                    
                                    nslog(@"compressed failed");
                                }
                            }];
                            
                            [svprogresshud showinfowithstatus:@"保存视频失败"];
                            
                        } else {
                            
                            [svprogresshud showinfowithstatus:@"保存视频成功"];
                        }
                        
                    });
                    
                    
                }];
                
            }
            
        });
        
    }else{
        //9.0以后
        phphotolibrary * library = [phphotolibrary sharedphotolibrary];
        dispatch_async(dispatch_get_main_queue(), ^{
            
            nserror * error = nil;
            // 用来抓取phasset的字符串标识
            __block nsstring *assetid = nil;
            // 用来抓取phassetcollectin的字符串标识符
            __block nsstring *assetcollectionid = nil;
            
            // 保存视频到【camera roll】(相机胶卷)
            
            [library performchangesandwait:^{
                
                assetid = [phassetchangerequest creationrequestforassetfromvideoatfileurl:videourl].placeholderforcreatedasset.localidentifier;
                
            } error:&error];
            // 获取曾经创建过的自定义视频相册名字
            phassetcollection  * createdassetcollection = nil;
            phfetchresult< phassetcollection *>* assetcollections = [phassetcollection fetchassetcollectionswithtype:phassetcollectiontypealbum subtype:phassetcollectionsubtypealbumregular options:nil];
            for (phassetcollection * assetcollection in assetcollections) {
                if ([assetcollection.localizedtitle isequaltostring: assetcollectionname]) {
                    
                    createdassetcollection = assetcollection;
                    
                    break;
                    
                }
            }
            
            //如果这个自定义框架没有创建过
            if (createdassetcollection == nil) {
                //创建新的[自定义的 album](相簿\相册)
                [library performchangesandwait:^{
                    
                    assetcollectionid = [phassetcollectionchangerequest creationrequestforassetcollectionwithtitle:assetcollectionname].placeholderforcreatedassetcollection.localidentifier;
                    
                    
                } error:&error];
                
                //抓取刚创建完的视频相册对象
                createdassetcollection = [phassetcollection fetchassetcollectionswithlocalidentifiers:@[assetcollectionid] options:nil].firstobject;
            }
            
             // 将【camera roll】(相机胶卷)的视频 添加到 【自定义album】(相簿\相册)中
            [library performchangesandwait:^{
                phassetcollectionchangerequest *request = [phassetcollectionchangerequest changerequestforassetcollection:createdassetcollection];
                
                // 视频
                [request addassets:[phasset fetchassetswithlocalidentifiers:@[assetid] options:nil]];
            } error:&error];
            
            // 提示信息
            if (error) {
                [svprogresshud showerrorwithstatus:@"保存视频失败!"];
            } else {
                [svprogresshud showsuccesswithstatus:@"保存视频成功!"];
            }

        });
        
        
    }
    
    [picker dismissviewcontrolleranimated:yes completion:^{
        // for fixing ios 8.0 problem that frame changed when open camera to record video.
        self.tabbarcontroller.view.frame  = [[uiscreen mainscreen] bounds];
        [self.tabbarcontroller.view layoutifneeded];
    }];
    
}
#pragma mark - 同步获取帧图
// get the video's center frame as video poster image
- (uiimage *)frameimagefromvideourl:(nsurl *)videourl {
    // result
    uiimage *image = nil;
    
    // avassetimagegenerator
    avasset *asset = [avasset assetwithurl:videourl];
    avassetimagegenerator *imagegenerator = [[avassetimagegenerator alloc] initwithasset:asset];
    imagegenerator.appliespreferredtracktransform = yes;
    
    // calculate the midpoint time of video
    float64 duration = cmtimegetseconds([asset duration]);
    // 取某个帧的时间,参数一表示哪个时间(秒),参数二表示每秒多少帧
    // 通常来说,600是一个常用的公共参数,苹果有说明:
    // 24 frames per second (fps) for film, 30 fps for ntsc (used for tv in north america and
    // japan), and 25 fps for pal (used for tv in europe).
    // using a timescale of 600, you can exactly represent any number of frames in these systems
    cmtime midpoint = cmtimemakewithseconds(duration / 2.0, 600);
    
    // get the image from
    nserror *error = nil;
    cmtime actualtime;
    // returns a cfretained cgimageref for an asset at or near the specified time.
    // so we should mannully release it
    cgimageref centerframeimage = [imagegenerator copycgimageattime:midpoint
                                                         actualtime:&actualtime
                                                              error:&error];
    
    if (centerframeimage != null) {
        image = [[uiimage alloc] initwithcgimage:centerframeimage];
        // release the cfretained image
        cgimagerelease(centerframeimage);
    }
    
    return image;
}

#pragma mark - 异步获取帧图
//异步获取帧图,可以一次回去多帧图片
- (void)centerframeimagewithvideourl:(nsurl *)videourl completion:(void (^)(uiimage *image))completion{
    
    // avassetimagegenerator
    avasset * asset = [avasset assetwithurl:videourl];
    avassetimagegenerator *imagegenerator = [[avassetimagegenerator alloc]initwithasset:asset];
    imagegenerator.appliespreferredtracktransform = yes;
    // calculate the midpoint time of video
    float64 duration = cmtimegetseconds([asset duration]);
    
    // 取某个帧的时间,参数一表示哪个时间(秒),参数二表示每秒多少帧
    // 通常来说,600是一个常用的公共参数,苹果有说明:
    // 24 frames per second (fps) for film, 30 fps for ntsc (used for tv in north america and
    // japan), and 25 fps for pal (used for tv in europe).
    // using a timescale of 600, you can exactly represent any number of frames in these systems
    
    cmtime midpoint = cmtimemakewithseconds(duration / 2.0, 600);
    //异步获取多帧图
    nsvalue * midtime = [nsvalue valuewithcmtime:midpoint];
    [imagegenerator generatecgimagesasynchronouslyfortimes:@[midtime] completionhandler:^(cmtime requestedtime, cgimageref  _nullable image, cmtime actualtime, avassetimagegeneratorresult result, nserror * _nullable error) {
        
        if (result == avassetimagegeneratorsucceeded && image != null) {
            uiimage * centerframeimage = [[uiimage alloc]initwithcgimage:image];
            dispatch_async(dispatch_get_main_queue(), ^{
                
                if (completion) {
                    
                    completion(centerframeimage);
                    
                }
                
            });
            
        } else {
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                if (completion) {
                    completion(nil);
                }
            });
            
            
        }
        
    }];
  
}

#pragma mark - 压缩导出视频
- (void)compressvideowithvideourl:(nsurl *)videourl
                        savedname:(nsstring *)savedname
                       completion:(void (^)(nsstring *savedpath))completion{
    
    // accessing video by url
    avurlasset *videoasset = [[avurlasset alloc] initwithurl:videourl options:nil];
    // find compatible presets by video asset.
    nsarray *presets = [avassetexportsession exportpresetscompatiblewithasset:videoasset];
    // begin to compress video
    // now we just compress to low resolution if it supports
    // if you need to upload to the server, but server does't support to upload by streaming,
    // you can compress the resolution to lower. or you can support more higher resolution.
    //开始压缩视频
    //现在我们压缩到低分辨率是否支持
    //如果需要上传服务器,但由流媒体服务器不支持上传,
    //可以压缩分辨率降低。或者你可以支持更多的高分辨率。
    if ([presets containsobject:avassetexportpreset640x480]) {
        avassetexportsession * session = [[avassetexportsession alloc]initwithasset:videoasset presetname:avassetexportpreset640x480];
        //nshomedirectory()  得到的是应用程序目录的路径,在该目录下有三个文件夹:documents、library、temp以及一个.app包!该目录下就是应用程序的沙盒,应用程序只能访问该目录下的文件夹!!!
        nsstring * doc = [nshomedirectory() stringbyappendingpathcomponent:@"documents"];
        
        nsstring * folder = [doc stringbyappendingpathcomponent:@"录制视频"];
        bool isdir = no;
        bool isexist = [[nsfilemanager defaultmanager] fileexistsatpath:folder isdirectory:&isdir];
        if (!isexist || (isexist && !isdir)) {
            
            nserror * error = nil;
            
            [[nsfilemanager defaultmanager]createdirectoryatpath:folder withintermediatedirectories:yes attributes:nil error:&error];
            if (error == nil) {
                [svprogresshud showinfowithstatus:@"目录创建成功"];
            } else {
                [svprogresshud showinfowithstatus:@"目录创建失败"];
                
            }
        }
        
        nsstring * outputpath = [folder stringbyappendingpathcomponent:savedname];
        session.outputurl = [nsurl fileurlwithpath:outputpath];
        // optimize for network use.
        session.shouldoptimizefornetworkuse = true;
        
        nsarray * supportedtypearray = session.supportedfiletypes;
        if ([supportedtypearray containsobject:avfiletypempeg4]) {
            session.outputfiletype = avfiletypempeg4;
            
        } else if (supportedtypearray.count == 0){
            
            [svprogresshud showinfowithstatus:@"no supported file types"];
            return;
            
        } else {
            
            session.outputfiletype = [supportedtypearray objectatindex:0];
            
        }
        
        // begin to export video to the output path asynchronously.
        //开始出口异步视频输出路径。
        [session exportasynchronouslywithcompletionhandler:^{
            
            if ([session status] == avassetexportsessionstatuscompleted) {
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (completion) {
                        
                        completion([session.outputurl path
                                    ]);
                        
                    }
                    
                });
                
            } else {
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (completion) {
                        completion(nil);
                    }
                    
                });
                
            }
            
        }];
        
    }
}

- (void)didreceivememorywarning {
    [super didreceivememorywarning];
    // dispose of any resources that can be recreated.
}
#pragma mark - 相关属性参数
/**
 *  (1)代理
 delegate
 
 (2)几个基本属性设置
 sourcetype  //设置资源获取类型
 allowsediting   //是否允许图片编辑
 
 (3)几个判断类方法
 *是否可以获取该类型资源
+ (bool)issourcetypeavailable:(uiimagepickercontrollersourcetype)sourcetype;

*是否可以获取该类型相机(前置和后置 )
+ (bool)iscameradeviceavailable:(uiimagepickercontrollercameradevice)cameradevice;

*是否可以获取闪光灯
+ (bool)isflashavailableforcameradevice:(uiimagepickercontrollercameradevice)cameradevice;

(4)代理方法(ios4后仅存2个可用)
- (void)imagepickercontroller:(uiimagepickercontroller *)picker didfinishpickingmediawithinfo:(nsdictionary *)info;
- (void)imagepickercontrollerdidcancel:(uiimagepickercontroller *)picker;
* 参数info中的键
nsstring *const  uiimagepickercontrollermediatype ;指定用户选择的媒体类型(文章最后进行扩展)
nsstring *const  uiimagepickercontrolleroriginalimage ;原始图片
nsstring *const  uiimagepickercontrollereditedimage ;修改后的图片
nsstring *const  uiimagepickercontrollercroprect ;裁剪尺寸
nsstring *const  uiimagepickercontrollermediaurl ;媒体的url
nsstring *const  uiimagepickercontrollerreferenceurl ;原件的url
nsstring *const  uiimagepickercontrollermediametadata;当来数据来源是照相机的时候这个值才有效

* uiimagepickercontrollermediatype
uiimagepickercontrollermediatype 包含着kuttypeimage 和kuttypemovie
kuttypeimage 包含:
const cfstringref  kuttypeimage ;抽象的图片类型
const cfstringref  kuttypejpeg ;
const cfstringref  kuttypejpeg2000 ;
const cfstringref  kuttypetiff ;
const cfstringref  kuttypepict ;
const cfstringref  kuttypegif ;
const cfstringref  kuttypepng ;
const cfstringref  kuttypequicktimeimage ;
const cfstringref  kuttypeappleicns
const cfstringref kuttypebmp;
const cfstringref  kuttypeico;

kuttypemovie 包含:
const cfstringref  kuttypeaudiovisualcontent ;抽象的声音视频
const cfstringref  kuttypemovie ;抽象的媒体格式(声音和视频)
const cfstringref  kuttypevideo ;只有视频没有声音
const cfstringref  kuttypeaudio ;只有声音没有视频
const cfstringref  kuttypequicktimemovie ;
const cfstringref  kuttypempeg ;
const cfstringref  kuttypempeg4 ;
const cfstringref  kuttypemp3 ;
const cfstringref  kuttypempeg4audio ;
const cfstringref  kuttypeappleprotectedmpeg4audio;
 */

@end

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网