美文网首页
使用 AVFoundation 录制视频的两种方式

使用 AVFoundation 录制视频的两种方式

作者: 大成小栈 | 来源:发表于2024-03-21 14:00 被阅读0次

1. 概述

AVCaptureSession 是 AVFoundation 的核心类,用于管理捕获对象AVCaptureInput 的视频和音频的输入,协调捕获的输出AVCaptureOutput。

AVCaptureOutput 的输出有两种方法:

  1. 直接以 movieFileUrl 方式输出,基于 AVCaptureSession + AVCaptureMovieFileOutput;
  2. 以原始数据流 data 的方式输出,基于AVCaptureSession + AVAssetWriter。

两种流程对比,如下:

两种流程对比

2. 对于 AVCaptureSession + AVCaptureMovieFileOutput

1)创建AVCaptureSession

//导入 AVFoundation.framework 
#import <AVFoundation/AVFoundation.h>

//声明属性
@property (nonatomic, strong) AVCaptureSession *captureSession;

//懒加载 AVCapturesession
- (AVCaptureSession *)captureSession {
    if (!_captureSession) {
        _captureSession = [[AVCaptureSession alloc] init];

        //设置分辨率
        if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
            [_captureSession setSessionPreset:AVCaptureSessionPresetHigh];
        }
    }
    return _captureSession;
}
// 注意:AVCaptureSession 的调用是会阻塞线程的,建议单独开辟子线程处理

2)设置音频、视频输入

//声明属性
@property (nonatomic, strong) AVCaptureDeviceInput *videoInput;
@property (nonatomic, strong) AVCaptureDeviceInput *audioInput;

//设置视频,音频输入源
- (void)setCaptureDeviceInput {
    //1\. 视频输入源
    //获取视频输入设备, 默认后置摄像头
    AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;
    self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error];

    if ([self.captureSession canAddInput:self.videoInput]) {
        [self.captureSession addInput:self.videoInput];
    }

    //2\. 音频输入源
    AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
    self.audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];
    if ([self.captureSession canAddInput:self.audioInput]) {
        [self.captureSession addInput:self.audioInput];
    }
}

3)设置文件输出源

//声明属性
@property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

//设置文件输出源
- (void)setDeviceFileOutput {

    //初始化文件输出对象
    self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];

    //捕获会话中特定捕获输入对象和捕获输出对象之间的连接
    AVCaptureConnection *captureConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];

    //设置防抖
    if ([captureConnection isVideoStabilizationSupported]) {
        captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
    }

    //预览图层和视频方向保持一致
    captureConnection.videoOrientation = [self.previewLayer connection].videoOrientation;

    //添加文件输出源
    if ([self.captureSession canAddOutput:self.movieFileOutput]) {
        [self.captureSession addOutput:self.movieFileOutput];
    }
}

4)添加视频预览层

- (void)setVideoPreviewLayer {
    self.previewLayer.frame = [UIScreen mainScreen].bounds;

    [self.superView.layer addSubLayer:self.previewLayer];
}

- (AVCaptureVideoPreviewLayer *)previewLayer {
    if (!_previewLayer) {
        _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
        _previewLayer.masksToBounds = YES;
        _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//填充模式
    }
    return _previewLayer;
}

5)开始采集

//声明属性
@property (nonatomic, strong) dispatch_queue_t sessionQueue;

//开始采集
- (void)startCapture {
    self.sessionQueue = dispatch_queue_create("com.capturesession.queue", DISPATCH_QUEUE_CONCURRENT);

    if (![self.captureSession isRunning]) {
        __weak __typeof(self) weakSelf = self;

        dispatch_async(self.sessionQueue, ^{
            [weakSelf.captureSession startRunning];
        });

    }
}

6)开始录制


//开始录制
- (void)startRecord {

    [self.movieFileOutput startRecordingToOutputFileURL:[self createVideoPath] recordingDelegate:self];
}

当实际的录制开始或停止时,系统会有代理回调。当开始录制之后,这时可能还没有真正写入,真正开始写入会回调下面代理,停止录制也是如此,所以如果你需要对录制视频起始点操作,建议通过系统的回调代理:

//实现协议 <AVCaptureFileOutputRecordingDelegate>中的方法

#pragma mark _ AVCaptureFileOutputRecordingDelegate

//起始点 - 开始录制
- (void)captureOutput:(AVCaptureFileOutput *)output didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections {

}

//结束录制
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
    NSLog(@"视频录制完成. 文件路径:%@",[outputFileURL absoluteString]);
}

7)停止录制


//停止录制
- (void)stopRecord {
    if ([self.movieFileOutput isRecording]) {
        [self.movieFileOutput stopRecording];
    }
}

8)停止采集

//停止采集
- (void)stopCapture {
    if ([self.captureSession isRunning]) {
        __weak __typeof(self) weakSelf = self;
        dispatch_async(self.sessionQueue, ^{
            [weakSelf.captureSession stopRunning];
            weakSelf.captureSession = nil;
        });
    }
}

3. 基于 AVCaptureSession + AVAssetWriter

在iOS开发中,我们经常需要使用摄像头来录制视频。AVCaptureSession是iOS中用于捕获音频和视频的核心类之一,而AVAssetWriter是一个简单易用的类,用于将音频和视频捕捉到的数据写入到一个文件中。本文将介绍如何使用AVCaptureSession和AVAssetWriter来录制视频,并提供相应的代码示例。

1)设置AVCaptureSession

首先,创建一个AVCaptureSession实例,并配置其输入和输出

// 初始化AVCaptureSession
let captureSession = AVCaptureSession()

// 设置摄像头设备
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
guard let videoInput = try? AVCaptureDeviceInput(device: videoDevice) else { return }
if captureSession.canAddInput(videoInput) {
    captureSession.addInput(videoInput)
}

// 设置音频设备
guard let audioDevice = AVCaptureDevice.default(for: .audio) else { return }
guard let audioInput = try? AVCaptureDeviceInput(device: audioDevice) else { return }
if captureSession.canAddInput(audioInput) {
    captureSession.addInput(audioInput)
}

// 配置输出
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global())
if captureSession.canAddOutput(videoOutput) {
    captureSession.addOutput(videoOutput)
}

let audioOutput = AVCaptureAudioDataOutput()
audioOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global())
if captureSession.canAddOutput(audioOutput) {
    captureSession.addOutput(audioOutput)
}

在上述代码中,我们首先创建了一个AVCaptureSession实例,然后设置了视频和音频的输入设备,并将其添加到captureSession中。接下来,我们配置了视频和音频的输出,并将其添加到captureSession中。

2)创建AVAssetWriter

接下来,我们需要创建一个AVAssetWriter实例,并设置其输出文件的路径和格式。

// 设置输出文件路径
let outputPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/output.mp4"
let outputURL = URL(fileURLWithPath: outputPath)

// 创建AVAssetWriter实例
guard let assetWriter = try? AVAssetWriter(url: outputURL, fileType: .mp4) else { return }

在上述代码中,我们首先使用NSSearchPathForDirectoriesInDomains方法获取了输出文件的路径,并创建了相应的URL。然后,我们使用AVAssetWriter的init(url:fileType:)方法创建了一个AVAssetWriter实例。

3)配置AVAssetWriter输入

接下来,我们需要配置AVAssetWriter的输入,以便将捕获到的音视频数据写入到输出文件中。

// 配置视频输入
let videoSettings: [String: Any] = [
    AVVideoCodecKey: AVVideoCodecType.h264,
    AVVideoWidthKey: 1280,
    AVVideoHeightKey: 720
]
let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
videoInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(videoInput) {
    assetWriter.add(videoInput)
}

// 配置音频输入
let audioSettings: [String: Any] = [
    AVFormatIDKey: kAudioFormatMPEG4AAC,
    AVNumberOfChannelsKey: 2,
    AVSampleRateKey: 44100,
    AVEncoderBitRateKey: 128000
]
let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
audioInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(audioInput) {
    assetWriter.add(audioInput)
}

在上述代码中,我们首先配置了视频输入的输出设置,指定了视频编码格式、视频宽度和高度。然后,我们创建了一个AVAssetWriterInput实例,并将其添加到assetWriter中。接下来,我们配置了音频输入的输出设置,指定了音频格式、声道数、采样率和比特率,并将其添加到assetWriter中。

4)开始录制和停止录制

最后,我们需要通过AVCaptureSession的startRunning()方法开始录制,并通过AVAssetWriter的finishWriting(completionHandler:)方法停止录制。

// 开始录制
captureSession.startRunning()

// 开始写入
assetWriter.startWriting()

// 停止写入
assetWriter.finishWriting(completionHandler: {
                if self.assetWriter.status.rawValue == 2 {
                    DispatchQueue.main.async {
                        guard let callback = self.finishWriteCallback else {
                            return
                        }
                        callback(self.assetWriter.outputURL)
                    }
                }
            })

4. 以上两种方式间的区别

主要在于输出的数据格式和处理方式,以及适用场景:

  1. 以movieFileUrl方式输出(AVCaptureMovieFileOutput):
  • 输出的是已经编码好的视频文件,直接以文件的形式保存在设备上。
  • 适用于需要简单录制视频并保存为文件的场景,比如拍摄视频、录制直播等。
  • AVCaptureMovieFileOutput负责将捕获到的音视频数据直接编码并保存为文件,对于开发者来说操作简单,不需要处理原始数据流,因此适用于对音视频处理不深入的场景。
  1. 以原始数据流data的方式输出(AVAssetWriter):
  • 输出的是未经编码的原始音视频数据流,需要开发者自行处理数据的编码、封装等操作。
  • 适用于需要对音视频数据进行更加灵活的处理、编辑或传输的场景,比如实时处理、编辑、加水印、实时传输等。
  • 使用AVAssetWriter需要开发者手动实现数据的编码和写入,相比起直接保存文件的方式更加灵活,但也更加复杂,需要开发者对音视频编码、数据封装等有一定的了解和技能。

综上所述,如果你只需要简单地录制视频并保存为文件,可以选择使用AVCaptureMovieFileOutput。而如果需要对音视频数据进行更复杂的处理,比如实时编辑、加工或传输,那么就需要使用AVAssetWriter以原始数据流方式输出,并自行处理数据。

参考链接:
https://www.cnblogs.com/reyzhang/p/16646673.html
https://blog.51cto.com/u_16175437/6672987

相关文章

网友评论

      本文标题:使用 AVFoundation 录制视频的两种方式

      本文链接:https://www.haomeiwen.com/subject/stmftjtx.html