三十五

三十五
音视频
捕捉会话:
AVCaptureSession 是管理捕获活动并协调从输入设备到捕获输出的数据流的对象。AVCaptureSession 用于连接输入和输出的资源,从物理设备如摄像头和麦克风等获取数据流,输出到一个或多个目的地。AVCaptureSession 可以额外配置一个会话预设值(session preset),用于控制捕捉数据的格式和质量,预设值默认值为 AVCaptureSessionPresetHigh。

要执行实时捕获,需要实例化AVCaptureSession对象并添加适当的输入和输出。可以调用startRunning()来启动从输入到输出的数据流,并调用stopRunning()来停止该流。

但是startRunning()方法是一个阻塞调用,可能会花费一些时间,因此应该在串行队列上执行会话设置,以免阻塞主队列(这使UI保持响应)。参见AVCam:构建摄像机应用程序的实现示例。

捕捉设备:
AVCaptureDevice 是为捕获会话提供输入(如音频或视频)并为特定于硬件的捕获特性提供控制的设备。它为物理设备定义统一接口,以及大量控制方法,获取指定类型的默认设备方法如下:self.activeVideoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
一个 AVCaptureDevice 对象表示一个物理捕获设备和与该设备相关联的属性。您可以使用捕获设备来配置底层硬件的属性。捕获设备还向AVCaptureSession对象提供输入数据(如音频或视频)。

捕捉设备的输入:
不能直接将 AVCaptureDevice 加入到 AVCaptureSession 中,需要封装为 AVCaptureDeviceInput。
self.captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:self.activeVideoDevice error:&videoError];
if (self.captureVideoInput) {
if ([self.captureSession canAddInput:self.captureVideoInput]){
[self.captureSession addInput:self.captureVideoInput];
}
} else if (videoError) {
}
捕捉输出 :
AVCaptureOutput 作为抽象基类提供了捕捉会话数据流的输出目的地,同时定义了此抽象类的高级扩展类。
AVCaptureStillImageOutput - 静态照片( 在ios10后被废弃,使用AVCapturePhotoOutput代替)
AVCaptureMovieFileOutput - 视频,
AVCaptureAudioFileOutput - 音频
AVCaptureAudioDataOutput - 音频底层数字样本
AVCaptureVideoDataOutput - 视频底层数字样本

捕捉连接:
AVCaptureConnection :捕获会话中捕获输入和捕获输出对象的特定对之间的连接。AVCaptureConnection 用于确定哪些输入产生视频,哪些输入产生音频,能够禁用特定连接或访问单独的音频轨道。
捕获输入有一个或多个输入端口(avcaptureinpu . port的实例)。捕获输出可以接受来自一个或多个源的数据(例如,AVCaptureMovieFileOutput对象同时接受视频和音频数据)。只有在canAddConnection(:)方法返回true时,才可以使用addConnection(:)方法将AVCaptureConnection实例添加到会话中。当使用addInput(:)或addOutput(:)方法时,会话自动在所有兼容的输入和输出之间形成连接。在添加没有连接的输入或输出时,只需手动添加连接。您还可以使用连接来启用或禁用来自给定输入或到给定输出的数据流。

捕捉预览 :
AVCaptureVideoPreviewLayer 是一个 CALayer 的子类,可以对捕捉视频数据进行实时预览。

视频捕捉:
QuickTime 格式的影片,元数据处于影片文件的开头位置,这样可以帮助视频播放器快速读取头文件来确定文件内容、结构和样本位置,但是录制时需要等所有样本捕捉完成才能创建头数据并将其附在文件结尾处。这样一来,如果录制时发生崩溃或中断就会导致无法创建影片头,从而在磁盘生成一个不可读的文件。
因此AVFoundation 的 AVCaptureMovieFileOutput 类就提供了分段捕捉能力,录制开始时生成最小化的头信息,录制进行中,片段间隔一定周期再次创建头信息,从而逐步完成创建。默认状态下每 10s 写入一个片段,可以通过 movieFragmentInterval 属性来修改。

首先是开启视频拍摄:
AVCaptureConnection *videoConnection = [self.cameraHelper.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) { [videoConnection setVideoOrientation:self.cameraHelper.videoOrientation]; } if ([videoConnection isVideoStabilizationSupported]) { [videoConnection setPreferredVideoStabilizationMode:AVCaptureVideoStabilizationModeAuto]; } [videoConnection setVideoScaleAndCropFactor:1.0]; if (![self.cameraHelper.movieFileOutput isRecording] && videoConnection.isActive && videoConnection.isEnabled) { // 判断视频连接是否可用 self.countTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(refreshTimeLabel) userInfo:nil repeats:YES]; NSString *urlString = [NSTemporaryDirectory() stringByAppendingString:[NSString stringWithFormat:@"%.0f.mov", [[NSDate date] timeIntervalSince1970] * 1000]]; NSURL *url = [NSURL fileURLWithPath:urlString]; [self.cameraHelper.movieFileOutput startRecordingToOutputFileURL:url recordingDelegate:self]; [self.captureButton setTitle:@“结束” forState:UIControlStateNormal]; } else { }

设置 PreferredVideoStabilizationMode 可以支持视频拍摄时的稳定性和拍摄质量,但是这一稳定效果只会在拍摄的视频中感受到,预览视频时无法感知。
将视频文件临时写入到临时文件中,等待拍摄结束时会调用 AVCaptureFileOutputRecordingDelegate 的 (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error 方法。此时可以进行保存视频和生成视频缩略图的操作。

  • (void)saveVideo:(NSURL *)videoURL
    {
    __block NSString *imageIdentifier;
    @weakify(self)
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    // 保存视频
    PHAssetChangeRequest *changeRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:videoURL];
    imageIdentifier = changeRequest.placeholderForCreatedAsset.localIdentifier;
    } completionHandler:^( BOOL success, NSError * _Nullable error ) {
    @strongify(self)
    dispatch_async(dispatch_get_main_queue(), ^{
    @strongify(self)
    [self resetTimeCounter];
    if (!success) {
    // 错误处理
    } else {
    PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[imageIdentifier] options:nil].firstObject;
    if (asset && asset.mediaType == PHAssetMediaTypeVideo) {
    PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
    options.version = PHImageRequestOptionsVersionCurrent;
    options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
    [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable obj, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
    @strongify(self)
    [self resolveAVAsset:obj identifier:asset.localIdentifier];
    }];
    }
    }
    });
    }];
    }

  • (void)resolveAVAsset:(AVAsset *)asset identifier:(NSString *)identifier
    {
    if (!asset) {
    return;
    }
    if (![asset isKindOfClass:[AVURLAsset class]]) {
    return;
    }
    AVURLAsset *urlAsset = (AVURLAsset *)asset;
    NSURL *url = urlAsset.URL;
    NSData *data = [NSData dataWithContentsOfURL:url];

    AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
    generator.appliesPreferredTrackTransform = YES; //捕捉缩略图时考虑视频 orientation 变化,避免错误的缩略图方向
    CMTime snaptime = kCMTimeZero;
    CGImageRef cgImageRef = [generator copyCGImageAtTime:snaptime actualTime:NULL error:nil];
    UIImage *assetImage = [UIImage imageWithCGImage:cgImageRef];
    CGImageRelease(cgImageRef);
    }

流程:
1,判断是否录制状态
//判断是否录制状态- (BOOL)isRecording { return self.movieOutput.isRecording;}

2,开始录制
//开始录制

  • (void)startRecording {

    if (![self isRecording]) {

      //获取当前视频捕捉连接信息,用于捕捉视频数据配置一些核心属性
      AVCaptureConnection * videoConnection = [self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
    
      //判断是否支持设置videoOrientation 属性。
      if([videoConnection isVideoOrientationSupported])
      {
          //支持则修改当前视频的方向
          videoConnection.videoOrientation = [self currentVideoOrientation];
    
      }
    
      //判断是否支持视频稳定 可以显著提高视频的质量。只会在录制视频文件涉及
      if([videoConnection isVideoStabilizationSupported])
      {
          videoConnection.enablesVideoStabilizationWhenAvailable = YES;
      }
    
      AVCaptureDevice *device = [self activeCamera];
    
      //摄像头可以进行平滑对焦模式操作。即减慢摄像头镜头对焦速度。当用户移动拍摄时摄像头会尝试快速自动对焦。
      if (device.isSmoothAutoFocusEnabled) {
          NSError *error;
          if ([device lockForConfiguration:&error]) {
    
              device.smoothAutoFocusEnabled = YES;
              [device unlockForConfiguration];
          }else
          {
              [self.delegate deviceConfigurationFailedWithError:error];
          }
      }
    
      //查找写入捕捉视频的唯一文件系统URL.
      self.outputURL = [self uniqueURL];
    
      //在捕捉输出上调用方法 参数1:录制保存路径  参数2:代理
      [self.movieOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self];
    

    }
    }

  • (CMTime)recordedDuration {
    return self.movieOutput.recordedDuration;
    }

//写入视频唯一文件系统URL

  • (NSURL *)uniqueURL {

    NSFileManager *fileManager = [NSFileManager defaultManager];

    //temporaryDirectoryWithTemplateString 可以将文件写入的目的创建一个唯一命名的目录;
    NSString *dirPath = [fileManager temporaryDirectoryWithTemplateString:@“kamera.XXXXXX”];

    if (dirPath) {
    NSString *filePath = [dirPath stringByAppendingPathComponent:@“kamera_movie.mov”];
    return [NSURL fileURLWithPath:filePath];
    }
    return nil;
    }
    3,停止录制
    //停止录制

  • (void)stopRecording {

    //是否正在录制
    if ([self isRecording]) {
    [self.movieOutput stopRecording];
    }
    }
    4,捕获视频回调函数AVCaptureFileOutputRecordingDelegate
    #pragma mark - AVCaptureFileOutputRecordingDelegate

  • (void)captureOutput:(AVCaptureFileOutput *)captureOutput
    didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
    fromConnections:(NSArray *)connections
    error:(NSError *)error {

    //错误
    if (error) {
    [self.delegate mediaCaptureFailedWithError:error];
    }else
    {
    //写入
    [self writeVideoToAssetsLibrary:[self.outputURL copy]];

    }

    self.outputURL = nil;
    }
    5,将得到的视频数据保存写入视频文件
    //写入捕捉到的视频

  • (void)writeVideoToAssetsLibrary:(NSURL *)videoURL {

    //ALAssetsLibrary 实例 提供写入视频的接口
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc]init];

    //写资源库写入前,检查视频是否可被写入 (写入前尽量养成判断的习惯)
    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {

      //创建block块
      ALAssetsLibraryWriteVideoCompletionBlock completionBlock;
      completionBlock = ^(NSURL *assetURL,NSError *error)
      {
          if (error) {
    
              [self.delegate assetLibraryWriteFailedWithError:error];
          }else
          {
              //用于界面展示视频缩略图
              [self generateThumbnailForVideoAtURL:videoURL];
          }
      };
    
      //执行实际写入资源库的动作
      [library writeVideoAtPathToSavedPhotosAlbum:videoURL completionBlock:completionBlock];
    

    }
    }
    6,获取视频缩略图
    //获取视频左下角缩略图

  • (void)generateThumbnailForVideoAtURL:(NSURL *)videoURL {

    //在videoQueue 上,
    dispatch_async(self.videoQueue, ^{

      //建立新的AVAsset & AVAssetImageGenerator
      AVAsset *asset = [AVAsset assetWithURL:videoURL];
    
      AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
    
      //设置maximumSize 宽为100,高为0 根据视频的宽高比来计算图片的高度
      imageGenerator.maximumSize = CGSizeMake(100.0f, 0.0f);
    
      //捕捉视频缩略图会考虑视频的变化(如视频的方向变化),如果不设置,缩略图的方向可能出错
      imageGenerator.appliesPreferredTrackTransform = YES;
    
      //获取CGImageRef图片 注意需要自己管理它的创建和释放
      CGImageRef imageRef = [imageGenerator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:nil];
    
      //将图片转化为UIImage
      UIImage *image = [UIImage imageWithCGImage:imageRef];
    
      //释放CGImageRef imageRef 防止内存泄漏
      CGImageRelease(imageRef);
    
      //回到主线程
      dispatch_async(dispatch_get_main_queue(), ^{
    
          //发送通知,传递最新的image
          [self postThumbnailNotifification:image];
    
      });
    

    });

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值