AVFoundation视频录制相关(AVCaptureSession+ AVCaptureMovieFileOutput+ AVCaptureVideoPreviewLayer)...

参考的博客不过里面添加了一些我自己的总结,

#import "LittleVideoController.h"
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>

@interface LittleVideoController ()<AVCaptureFileOutputRecordingDelegate>


@property(nonatomic,strong) dispatch_source_t timer;
@property (weak, nonatomic) IBOutlet UIButton * startButton;//开始录制
@property(nonatomic,strong) UIButton * videoButton;//播放
@property(nonatomic,strong) AVCaptureSession *  captureSession;//捕捉会话对象
@property(nonatomic,strong) AVCaptureMovieFileOutput * captureMovieFileOutput;//
@property(nonatomic,strong) AVCaptureVideoPreviewLayer * captureVideoPreviewLayer;

@property(nonatomic,strong) NSString * videoPath;

@end

@implementation LittleVideoController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
        
    [self.startButton addTarget:self action:@selector(startCaptureWithSession) forControlEvents:UIControlEventTouchUpInside];
    
    /*
     下面是一个有趣的写法,上网搜到这样一段话,补充说明之后能理解这种写法:
     这个问题严格上讲和Objective-C没什么太大的关系,这个是GNU C的对C的扩展语法 
     
     在理解一下什么是GNU C ,下面是百度给的定义:
     GNU C 函式库(GNU C Library,又称为glibc)是一种按照LGPL许可协议发布的,公开源代码的,免费的,方便从网络下载的C的编译程序。 GNU C运行期库,是一种C函式库,是程序运行时使用到的一些API集合,它们一般是已预先编译好,以二进制代码形式存 在Linux类系统中,GNU C运行期库,通常作为GNU C编译程序的一个部分发布。 它最初是自由软件基金会为其GNU操作系统所写,但目前最主要的应用是配合Linux内核,成为GNU/Linux操作系统一个重要的组成部分。
     
     继续解释:
     Xcode采用的Clang编译,Clang作为GCC(GCC的初衷是为GNU操作系统专门编写的一款编译器)的替代品,和GCC一样对于GNU C语法完全支持
     
     你可能知道if(condition)后面只能根一条语句,多条语句必须用{}阔起来,这个语法扩展即将一条(多条要用到{})语句外面加一个括号(),
     这样的话你就可以在表达式中应用循环、判断甚至本地变量等。
     表达式()最后一行应该一个能够计算结果的子表达式加上一个分号(;),这个子表达式作为整个结构的返回结果
     
     这个扩展在代码中最常见的用处在于宏定义中
     */

    
    
    
    self.captureSession = ({
        // 分辨率设置
        AVCaptureSession *session = [[AVCaptureSession alloc] init];
        // 先判断这个设备是否支持设置你要设置的分辨率
        if ([session canSetSessionPreset:AVCaptureSessionPresetMedium]) {
                
                /*
                 下面是对你能设置的预设图片的质量和分辨率的说明
                 AVCaptureSessionPresetHigh      High 最高的录制质量,每台设备不同
                 AVCaptureSessionPresetMedium    Medium 基于无线分享的,实际值可能会改变
                 AVCaptureSessionPresetLow       LOW 基于3g分享的
                 AVCaptureSessionPreset640x480   640x480 VGA
                 AVCaptureSessionPreset1280x720  1280x720 720p HD
                 AVCaptureSessionPresetPhoto     Photo 完整的照片分辨率,不支持视频输出
                 */
                [session setSessionPreset:AVCaptureSessionPresetMedium];
        }
        session;
    });
    
    
    // 初始化一个拍摄输出对象
    self.captureMovieFileOutput = ({
        
        //输出一个电影文件
        /*
         a.AVCaptureMovieFileOutput  输出一个电影文件
         b.AVCaptureVideoDataOutput  输出处理视频帧被捕获
         c.AVCaptureAudioDataOutput  输出音频数据被捕获
         d.AVCaptureStillImageOutput 捕获元数据
         */
        
        AVCaptureMovieFileOutput * output = [[AVCaptureMovieFileOutput alloc]init];
        
        /*
         一个ACCaptureConnection可以控制input到output的数据传输。
         */
        AVCaptureConnection * connection = [output connectionWithMediaType:AVMediaTypeVideo];
        
        if ([connection isVideoMirroringSupported]) {

                
            /*
             
             视频防抖 是在 iOS 6 和 iPhone 4S 发布时引入的功能。到了 iPhone 6,增加了更强劲和流畅的防抖模式,被称为影院级的视频防抖动。相关的 API 也有所改动 (目前为止并没有在文档中反映出来,不过可以查看头文件)。防抖并不是在捕获设备上配置的,而是在 AVCaptureConnection 上设置。由于不是所有的设备格式都支持全部的防抖模式,所以在实际应用中应事先确认具体的防抖模式是否支持:
             
             typedef NS_ENUM(NSInteger, AVCaptureVideoStabilizationMode) {
             AVCaptureVideoStabilizationModeOff       = 0,
             AVCaptureVideoStabilizationModeStandard  = 1,
             AVCaptureVideoStabilizationModeCinematic = 2,
             AVCaptureVideoStabilizationModeAuto      = -1,  自动
             } NS_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED;
             */
            connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
            //预览图层和视频方向保持一致
            connection.videoOrientation = [self.captureVideoPreviewLayer connection].videoOrientation;
        }
        
        if ([self.captureSession canAddOutput:output]) {
            
            [self.captureSession addOutput:output];
        }
        
        output;
    });
    
    /*
     用于展示制的画面
     */
    self.captureVideoPreviewLayer = ({
        
        AVCaptureVideoPreviewLayer * preViewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
        preViewLayer.frame = CGRectMake(10, 50, 355, 355);
        
        /*
         AVLayerVideoGravityResizeAspect:保留长宽比,未填充部分会有黑边
         AVLayerVideoGravityResizeAspectFill:保留长宽比,填充所有的区域
         AVLayerVideoGravityResize:拉伸填满所有的空间
        */
        preViewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        [self.view.layer addSublayer:preViewLayer];
        self.view.layer.masksToBounds = YES;
        preViewLayer;
    });
    
    
    //如果这块代码写在前面,则打开captureVideoPreviewLayer就显示摄像头的内容,和微信小程序一样
    if ([self SetSessioninputs:nil]) {
         [self.captureSession startRunning];
    }
    
}

#pragma mark 开始录制
-(void)startCaptureWithSession{
    
    // 先删除之前的视频文件
    if ([[NSFileManager defaultManager] fileExistsAtPath:self.videoPath]) {
                
        [[NSFileManager defaultManager] removeItemAtURL:[NSURL fileURLWithPath:self.videoPath] error:NULL];
    }
        
//    NSError * error = nil;
//    if ([self SetSessioninputs:error]) {
//
        // 开始录制
       
        [self startRecordSession];
        
//    }else{
//        
//        [self.captureSession stopRunning];
//        NSLog(@"录制失败:%@",error);
//    }
}


-(BOOL)SetSessioninputs:(NSError *)error{
    
    // capture 捕捉 捕获
    /*
       视频输入类
       AVCaptureDevice 捕获设备类
       AVCaptureDeviceInput 捕获设备输入类
     */
    AVCaptureDevice * captureDevice   = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    AVCaptureDeviceInput * videoInput = [AVCaptureDeviceInput deviceInputWithDevice: captureDevice error: &error];
    
    if (!videoInput) {
        return NO;
    }
    
    // 给捕获会话类添加输入捕获设备
    if ([self.captureSession canAddInput:videoInput]) {
        
        [self.captureSession addInput:videoInput];
    }else{
        return NO;
    }
    
    
    
//      添加音频捕获设备
    /*
     __block AVCaptureDevice *backCamera  = nil;
     NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
     [cameras enumerateObjectsUsingBlock:^(AVCaptureDevice *camera, NSUInteger idx, BOOL * _Nonnull stop) {

     //AVCaptureDevicePositionFront 前摄像头
     //AVCaptureDevicePositionBack:后摄像头
     //AVCaptureDevicePositionUnspecified不指定前值或者后置,用系统当前的
       if(camera.position == AVCaptureDevicePositionBack){
           backCamera = camera;
       }
    }];
     
     
     // 配置曝光模式 设置持续曝光模式
     //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
     NSError *error = nil;
     [backCamera lockForConfiguration:&error];
     //AVCaptureExposureModeLocked 直接用当前值就好,不指定
     //AVCaptureExposureModeAutoExpose 自动调整曝光一次,然后用系统的
     //AVCaptureExposureModeContinuousAutoExposure 需要的时候自行调节
     //AVCaptureExposureModeCustom 自定义,需要自己手动设置
     
    if ([backCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
        [backCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
    }
    [backCamera unlockForConfiguration];
     
     
     //闪光灯设计  AVCaptureFlashMode
     
     //手电筒开关--其实就是相机的闪光灯 AVCaptureTorchMode
     [backCamera lockForConfiguration:&error];
     if([backCamera isTorchModeSupported:AVCaptureTorchModeOn]){
     [backCamera setTorchMode:AVCaptureTorchModeOn];
     }
     [backCamera unlockForConfiguration];
     
     //焦距模式调整AVCaptureFocusMode
     //曝光量调节AVCaptureExposureMode
     //白平衡  AVCaptureWhiteBalanceMode
     //距离调整 AVCaptureAutoFocusRangeRestriction
     
    */

    //如果需要指定的摄像头,
    AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    //

    AVCaptureDeviceInput * audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
    
    if (!audioDevice) {
        
        return NO;
    }
    
    if ([self.captureSession canAddInput:audioInput]) {
        
        [self.captureSession addInput:audioInput];
    }
    return YES;
}



-(void)startRuningWithSession{
        
        __block int time = 0;
        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
        dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
        dispatch_source_set_event_handler(_timer, ^{
                
                time++;
                NSLog(@"录制的时长:%d",time);
                if (time == 10) {
                        
                        NSLog(@"录制的时长限制在10秒以内");
                        [self.captureMovieFileOutput stopRecording];
                        [self.captureSession stopRunning];
                        dispatch_source_cancel(_timer);
                }
        });
        dispatch_resume(_timer);
}


#pragma mark --
#pragma mark -- AVCaptureMovieFileOutput 录制视频
-(void)startRecordSession{

        
    [self.captureMovieFileOutput startRecordingToOutputFileURL:({
        
        NSURL * url  = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"zhangxu.mov"]];
        NSLog(@"视频想要缓存的地址:%@",url);
        if ([[NSFileManager defaultManager]fileExistsAtPath:url.path]) {
            
            [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
        }
        url;
    }) recordingDelegate:self];
}
#pragma mark AVCaptureFileOutputRecordingDelegate 代理方法
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
      fromConnections:(NSArray *)connections{
        
        //Recording started
        NSLog(@"视频录制开始!!");
        [self startRuningWithSession]; // 开始计时
}

- (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections error:(nullable NSError *)error{
    
        NSLog(@"视频录制结束!!");
        NSLog(@"视频缓存地址:%@",outputFileURL);
    
        NSLog(@"视频压缩前大小 %f M",  [self getFileSize:[outputFileURL path]]);
    
    
        BOOL recordedSuccessfully = YES;
        id captureResult = [[error userInfo]objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
        if (captureResult) {
                
                recordedSuccessfully = [captureResult boolValue];
        }
        
        if (recordedSuccessfully) {
                
            [self compressVideoWithFileUrl:outputFileURL];
            
        }
}

//此⽅方法可以获取视频⽂文件的大小。
- (CGFloat) getFileSize:(NSString *)path {
    
    NSData * data = [NSData dataWithContentsOfFile:path];
    float dataSize = (float)data.length/1024/1024;
    NSLog(@"视频压缩qian大小 %f M", dataSize);
    return dataSize;
    
//    NSLog(@"%@",path);
//    NSFileManager *fileManager = [NSFileManager defaultManager];
//    float filesize = -1.0;
//    if ([fileManager fileExistsAtPath:path]) {
//        NSDictionary *fileDic = [fileManager attributesOfItemAtPath:path error:nil];//获取⽂文件的属性
//        unsigned long long size = [[fileDic objectForKey:NSFileSize] longLongValue];
//        filesize = 1.0*size/1024/1024;
//    }else{ NSLog(@"找不不到⽂文件");
//
//
//    }
//    return filesize;
    
}

#pragma mark --
#pragma mark -- 视频压缩方法
-(void)compressVideoWithFileUrl:(NSURL *)fileUrl{
    
    /*
      这里需要注意的一点就是在重复的路径上保存文件是不行的,可以选择在点击开始的时候删除之前的
      也可以这样按照时间命名不同的文件保存
      在后面的AVAssetWriter也要注意这一点
     */
    // 压缩后的视频的方法命名
    NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
    [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
        
    // 压缩后的文件路径
    self.videoPath = [
                      NSString stringWithFormat:@"%@/%@.mov",NSTemporaryDirectory(),[formatter stringFromDate:[NSDate date]]];
    
    
    // 先根据你传入的文件的路径穿件一个AVAsset
    AVAsset * asset = [AVAsset  assetWithURL:fileUrl];
    /*
     
     根据urlAsset创建AVAssetExportSession压缩类
     第二个参数的意义:常用 压缩中等质量  AVAssetExportPresetMediumQuality
     AVF_EXPORT NSString *const AVAssetExportPresetLowQuality        NS_AVAILABLE_IOS(4_0);
     AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality     NS_AVAILABLE_IOS(4_0);
     AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality    NS_AVAILABLE_IOS(4_0);
    
    */
    AVAssetExportSession * exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
    
    // 优化压缩,这个属性能使压缩的质量更好
    exportSession.shouldOptimizeForNetworkUse = YES;
    // 到处的文件的路径
    exportSession.outputURL =  [NSURL fileURLWithPath:self.videoPath];
    // 导出的文件格式
    /*!
      @constant  AVFileTypeMPEG4  mp4格式的   AVFileTypeQuickTimeMovie mov格式的
      @abstract A UTI for the MPEG-4 file format.
      @discussion
      The value of this UTI is @"public.mpeg-4".
      Files are identified with the .mp4 extension.
      可以看看这个outputFileType格式,比如AVFileTypeMPEG4也可以写成public.mpeg-4,其他类似
    */
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    
    NSLog(@"视频压缩后的presetName: %@",exportSession.presetName);
    // 压缩的方法  export 导出  Asynchronously 异步
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        
        /*
         exportSession.status 枚举属性
         typedef NS_ENUM(NSInteger, AVAssetExportSessionStatus) {
         AVAssetExportSessionStatusUnknown,
         AVAssetExportSessionStatusWaiting,
         AVAssetExportSessionStatusExporting,
         AVAssetExportSessionStatusCompleted,
         AVAssetExportSessionStatusFailed,
         AVAssetExportSessionStatusCancelled
         };
         */
        int exportStatus = exportSession.status;
        switch (exportStatus) {
            case AVAssetExportSessionStatusFailed:
                
                NSLog(@"压缩失败");
                break;
            case AVAssetExportSessionStatusCompleted:
            {
                /*
                 压缩后的大小
                 也可以利用exportSession的progress属性,随时监测压缩的进度
                 */
                NSData * data = [NSData dataWithContentsOfFile:self.videoPath];
                float dataSize = (float)data.length/1024/1024;
                NSLog(@"视频压缩后大小 %f M", dataSize);
                
                
                
                
               
            }
                break;
            default:
                break;
        }
    }];
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

 

转载于:https://www.cnblogs.com/hualuoshuijia/p/11549165.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值