iOS:音视频开发——视频采集 你值得拥有

前言 在直播和短视频行业日益火热的发展形势下,音视频开发(采集、编解码、传输、播放、美颜)等技术也随之成为开发者们关注的重点,本系列文章就音视频开发过程当中所运用到的技术和原理进行梳理和总结。 认识 AVCapture 系列 AVCapture 系列是 AVFoundation 框架为咱们提供的用于管理输入设备、采集、输出、预览等一系列接口,其工做原理以下:数组

image.png缓存

  1. AVCaptureDevice: 信号采集硬件设备(摄像头、麦克风、屏幕等) AVCaptureDevice 表明硬件设备,而且为 AVCaptureSession 提供 input,要想使用 AVCaptureDevice,应该先将设备支持的 device 枚举出来, 根据摄像头的位置( 前置或者后置摄像头 )获取须要用的那个摄像头, 再使用; 若是想要对 AVCaptureDevice 对象的一些属性进行设置,应该先调用 lockForConfiguration: 方法, 设置结束后,调用 unlockForConfiguration 方法; [self.device lockForConfiguration:&error]; // 设置 *** [self.device unlockForConfiguration];微信

  2. AVCaptureInput: 输入数据管理 AVCaptureInput 继承自 NSObject,是向 AVCaptureSession 提供输入数据的对象的抽象超类; 要将 AVCaptureInput 对象与会话 AVCaptureSession 关联,须要 AVCaptureSession实例调用 -addInput: 方法。 因为 AVCaptureInput 是个抽象类,没法直接使用,因此咱们通常使用它的子类类管理输入数据。咱们经常使用的 AVCaptureInput 的子类有三个:session

image.png框架

AVCaptureDeviceInput:用于从 AVCaptureDevice 对象捕获数据; AVCaptureScreenInput:从 macOS 屏幕上录制的一种捕获输入; AVCaptureMetadataInput:它为 AVCaptureSession 提供 AVMetadataItems。 3. AVCaptureOutput:输出数据管理 AVCaptureOutput 继承自 NSObject,是输出数据管理,该对象将会被添加到会话AVCaptureSession中,用于接收会话AVCaptureSession各种输出数据; AVCaptureOutput提供了一个抽象接口,用于将捕获输出数据(如文件和视频预览)链接到捕获会话AVCaptureSession的实例,捕获输出能够有多个由AVCaptureConnection对象表示的链接,一个链接对应于它从捕获输入(AVCaptureInput的实例)接收的每一个媒体流,捕获输出在首次建立时没有任何链接,当向捕获会话添加输出时,将建立链接,将该会话的输入的媒体数据映射到其输出,调用AVCaptureSession的-addOutput:方法将AVCaptureOutput与AVCaptureSession关联。 AVCaptureOutput 是个抽象类,咱们必须使用它的子类,经常使用的 AVCaptureOutput的子类以下所示:ide

image.pngoop

AVCaptureAudioDataOutput:一种捕获输出,用于记录音频,并在录制音频时提供对音频样本缓冲区的访问; AVCaptureAudioPreviewOutput :一种捕获输出,与一个核心音频输出设备相关联、可用于播放由捕获会话捕获的音频; AVCaptureDepthDataOutput :在兼容的摄像机设备上记录场景深度信息的捕获输出; AVCaptureMetadataOutput :用于处理捕获会话 AVCaptureSession 产生的定时元数据的捕获输出; AVCaptureStillImageOutput:在macOS中捕捉静止照片的捕获输出。该类在 iOS 10.0 中被弃用,而且不支持新的相机捕获功能,例如原始图像输出和实时照片,在 iOS 10.0 或更高版本中,使用 AVCapturePhotoOutput 类代替; AVCapturePhotoOutput :静态照片、动态照片和其余摄影工做流的捕获输出; AVCaptureVideoDataOutput :记录视频并提供对视频帧进行处理的捕获输出; AVCaptureFileOutput:用于捕获输出的抽象超类,可将捕获数据记录到文件中; AVCaptureMovieFileOutput :继承自 AVCaptureFileOutput,将视频和音频记录到 QuickTime 电影文件的捕获输出; AVCaptureAudioFileOutput :继承自 AVCaptureFileOutput,记录音频并将录制的音频保存到文件的捕获输出。 4. AVCaptureSession:用来管理采集数据和输出数据,它负责协调从哪里采集数据,输出到哪里,它是整个Capture的核心,相似于RunLoop,它不断的从输入源获取数据,而后分发给各个输出源 AVCaptureSession 继承自NSObject,是AVFoundation的核心类,用于管理捕获对象AVCaptureInput的视频和音频的输入,协调捕获的输出AVCaptureOutput学习

image.pngui

  1. AVCaptureConnection:用于 AVCaptureSession 来创建和维护 AVCaptureInput 和 AVCaptureOutput 之间的链接 AVCaptureConnection 是 Session 和 Output 中间的控制节点,每一个 Output 与 Session 创建链接后,都会分配一个默认的 AVCpatureConnection。spa

  2. AVCapturePreviewLayer:预览层,AVCaptureSession 的一个属性,继承自 CALayer,提供摄像头的预览功能,照片以及视频就是经过把 AVCapturePreviewLayer 添加到 UIView 的 layer 上来显示 开始视频采集 一、建立并初始化输入AVCaptureInput: AVCaptureDeviceInput 和输出AVCaptureOutput: AVCaptureVideoDataOutput; 二、建立并初始化 AVCaptureSession,把 AVCaptureInput 和 AVCaptureOutput 添加到 AVCaptureSession 中; 三、调用 AVCaptureSession 的 startRunning 开启采集 初始化输入 经过 AVCaptureDevice 的 devicesWithMediaType: 方法获取摄像头,iPhone 都是有先后摄像头的,这里获取到的是一个设备的数组,要从数组里面拿到咱们想要的前摄像头或后摄像头,而后将 AVCaptureDevice 转化为 AVCaptureDeviceInput,添加到 AVCaptureSession中 /************************** 设置输入设备 *************************/ // --- 获取全部摄像头 --- NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; // --- 获取当前方向摄像头 --- NSArray *captureDeviceArray = [cameras filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", _capturerParam.devicePosition]];

    if (captureDeviceArray.count == 0) {
         return nil;
     }
     
     // ---  转化为输入设备  ---
     AVCaptureDevice *camera = captureDeviceArray.firstObject;
     self.captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:camera
                                                                     error:&error];
    复制代码

设置视频采集参数 @implementation VideoCapturerParam

  • (instancetype)init { self = [super init]; if (self) { _devicePosition = AVCaptureDevicePositionFront; // 摄像头位置,默认为前置摄像头 _sessionPreset = AVCaptureSessionPreset1280x720; // 视频分辨率 默认 AVCaptureSessionPreset1280x720 _frameRate = 15; // 帧 单位为 帧/秒,默认为15帧/秒 _videoOrientation = AVCaptureVideoOrientationPortrait; // 摄像头方向 默认为当前手机屏幕方向

    switch ([UIDevice currentDevice].orientation) {
          case UIDeviceOrientationPortrait:
          case UIDeviceOrientationPortraitUpsideDown:
              _videoOrientation = AVCaptureVideoOrientationPortrait;
              break;
              
          case UIDeviceOrientationLandscapeRight:
              _videoOrientation = AVCaptureVideoOrientationLandscapeRight;
              break;
              
          case UIDeviceOrientationLandscapeLeft:
              _videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
              break;
              
          default:
              break;
      }
    复制代码

    }

    return self; }

初始化输出 初始化视频输出 AVCaptureVideoDataOutput,并设置视频数据格式,设置采集数据回调线程,这里视频输出格式选的是 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,YUV 数据格式 /************************** 设置输出设备 *************************/ // --- 设置视频输出 --- self.captureVideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];

NSDictionary *videoSetting = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange], kCVPixelBufferPixelFormatTypeKey, nil];   // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 表示输出的视频格式为NV12
    [self.captureVideoDataOutput setVideoSettings:videoSetting];
    
    // ---  设置输出串行队列和数据回调  ---
    dispatch_queue_t outputQueue = dispatch_queue_create("VideoCaptureOutputQueue", DISPATCH_QUEUE_SERIAL);
    // ---  设置代理  ---
    [self.captureVideoDataOutput setSampleBufferDelegate:self queue:outputQueue];
    // ---  丢弃延迟的帧  ---
    self.captureVideoDataOutput.alwaysDiscardsLateVideoFrames = YES;
复制代码

初始化 AVCaptureSession 并设置输入输出 一、初始化 AVCaptureSession,把上面的输入和输出加进来,在添加输入和输出到 AVCaptureSession 先查询一下 AVCaptureSession 是否支持添加该输入或输出端口; 二、设置视频分辨率及图像质量(AVCaptureSessionPreset),设置以前一样须要先查询一下 AVCaptureSession 是否支持这个分辨率; 三、若是在已经开启采集的状况下须要修改分辨率或输入输出,须要用 beginConfiguration 和commitConfiguration 把修改的代码包围起来。在调用 beginConfiguration 后,能够配置分辨率、输入输出等,直到调用 commitConfiguration 了才会被应用; 四、AVCaptureSession 管理了采集过程当中的状态,当开始采集、中止采集、出现错误等都会发起通知,咱们能够监听通知来获取 AVCaptureSession 的状态,也能够调用其属性来获取当前 AVCaptureSession 的状态, AVCaptureSession 相关的通知都是在主线程的。

前置摄像头采集到的画面是翻转的,若要解决画面翻转问题,须要设置 AVCaptureConnection 的 videoMirrored 为 YES。

/************************** 初始化会话 *************************/ self.captureSession = [[AVCaptureSession alloc] init]; self.captureSession.usesApplicationAudioSession = NO;

// ---  添加输入设备到会话  ---
    if ([self.captureSession canAddInput:self.captureDeviceInput]) {
        [self.captureSession addInput:self.captureDeviceInput];
    }
    else {
        NSLog(@"VideoCapture:: Add captureVideoDataInput Faild!");
        return nil;
    }
    
    // ---  添加输出设备到会话  ---
    if ([self.captureSession canAddOutput:self.captureVideoDataOutput]) {
        [self.captureSession addOutput:self.captureVideoDataOutput];
    }
    else {
        NSLog(@"VideoCapture:: Add captureVideoDataOutput Faild!");
        return nil;
    }
    
    // ---  设置分辨率  ---
    if ([self.captureSession canSetSessionPreset:self.capturerParam.sessionPreset]) {
        self.captureSession.sessionPreset = self.capturerParam.sessionPreset;
    }
    
    /**************************  初始化链接  *************************/
    self.captureConnection = [self.captureVideoDataOutput connectionWithMediaType:AVMediaTypeVideo];
    
    // ---  设置摄像头镜像,不设置的话前置摄像头采集出来的图像是反转的  ---
    if (self.capturerParam.devicePosition == AVCaptureDevicePositionFront && self.captureConnection.supportsVideoMirroring) { // supportsVideoMirroring 视频是否支持镜像
        self.captureConnection.videoMirrored = YES;
    }
    
    self.captureConnection.videoOrientation = self.capturerParam.videoOrientation;
    
    self.videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    self.videoPreviewLayer.connection.videoOrientation = self.capturerParam.videoOrientation;
    self.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
复制代码

采集视频 / 回调 /**

  • 开始采集 */
  • (NSError *)startCpture { if (self.isCapturing) { return [NSError errorWithDomain:@"VideoCapture:: startCapture faild: is capturing" code:1 userInfo:nil]; }

    // --- 摄像头权限判断 --- AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

    if (videoAuthStatus != AVAuthorizationStatusAuthorized) { return [NSError errorWithDomain:@"VideoCapture:: Camera Authorizate faild!" code:1 userInfo:nil]; }

    [self.captureSession startRunning]; self.isCapturing = YES;

    kLOGt(@"开始采集视频");

    return nil; }

/**

  • 中止采集 */
  • (NSError *)stopCapture { if (!self.isCapturing) { return [NSError errorWithDomain:@"VideoCapture:: stop capture faild! is not capturing!" code:1 userInfo:nil]; }

    [self.captureSession stopRunning]; self.isCapturing = NO;

    kLOGt(@"中止采集视频");

    return nil; }

#pragma mark ————— AVCaptureVideoDataOutputSampleBufferDelegate ————— /**

  • 摄像头采集数据回调 @prama output 输出设备 @prama sampleBuffer 帧缓存数据,描述当前帧信息 @prama connection 链接 */
  • (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { if ([self.delagate respondsToSelector:@selector(videoCaptureOutputDataCallback:)]) { [self.delagate videoCaptureOutputDataCallback:sampleBuffer]; } }

调用 / 获取数据 调用很简单,初始化视频采集参数 VideoCapturerParam 和 视频采集器 VideoVapturer , 设置预览图层 videoPreviewLayer , 调用 startCpture 就能够开始采集了,而后实现数据采集回调的代理方法 videoCaptureOutputDataCallback 获取数据 // --- 初始化视频采集参数 --- VideoCapturerParam *param = [[VideoCapturerParam alloc] init];

// ---  初始化视频采集器  ---
self.videoCapture = [[VideoVapturer alloc] initWithCaptureParam:param error:nil];
self.videoCapture.delagate = self;

// ---  开始采集  ---
[self.videoCapture startCpture];


// ---  初始化预览View  ---
self.recordLayer = self.videoCapture.videoPreviewLayer;
self.recordLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
[self.view.layer addSublayer:self.recordLayer];
复制代码

#pragma mark ————— VideoCapturerDelegate ————— 视频采集回调

  • (void)videoCaptureOutputDataCallback:(CMSampleBufferRef)sampleBuffer { NSLog(@"%@ sampleBuffer : %@ ", kLOGt(@"视频采集回调"), sampleBuffer);

小编这呢,给你们推荐一个优秀的iOS交流平台,平台里的伙伴们都是很是优秀的iOS开发人员,咱们专一于技术的分享与技巧的交流,你们能够在平台上讨论技术,交流学习。欢迎你们的加入(想要进入的可加小编微信)。

微信号:17336563535

来源:本文为第三方转载,若有侵权请联系小编删除。

相关文章
相关标签/搜索