当前位置: 首页 > news >正文

iOS使用AVCaptureSession实现音视频采集

AVCaptureSession配置采集行为并协调从输入设备到采集输出的数据流。要执行实时音视频采集,需要实例化采集会话并添加适当的输入和输出。

  • AVCaptureSession:管理输入输出音视频流
  • AVCaptureDevice:相机硬件的接口,用于控制硬件特性,诸如镜头的位置(前后摄像头)、曝光、闪光灯等。
  • AVCaptureInput:配置输入设备,提供来自设备的数据
  • AVCaptureOutput:管理输出的音视频数据流
  • AVCaptureConnection:输入与输出的连接
  • AVCaptureVideoPreviewLayer:显示当前相机正在采集的状况
  • AVAssetWriter:将媒体数据写入到容器文件

初始化AVCaptureSession

- (AVCaptureSession *)captureSession {if (_captureSession == nil){_captureSession = [[AVCaptureSession alloc] init];if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {_captureSession.sessionPreset = AVCaptureSessionPreset1280x720;}}return _captureSession;
}- (dispatch_queue_t)videoQueue {if (!_videoQueue) {_videoQueue = dispatch_queue_create("VideoCapture", DISPATCH_QUEUE_SERIAL);}return _videoQueue;
}

添加视频输入

- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position {AVCaptureDeviceDiscoverySession *deviceDiscoverySession =  [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:position];for (AVCaptureDevice *device in deviceDiscoverySession.devices) {if ([device position] == position) {return device;}}return nil;
}- (void)setupVideoInput {AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];if (!captureDevice){NSLog(@"captureDevice failed");return;}NSError *error = nil;self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];if (error) {NSLog(@"videoInput error:%@", error);return;}if ([self.captureSession canAddInput:self.videoInput]) {[self.captureSession addInput:self.videoInput];}
}

添加音频输入

- (void)setupAudioInput {AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];NSError *error = nil;self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];if (error) {NSLog(@"audioInput error:%@", error);return;}if ([self.captureSession canAddInput:self.audioInput]) {[self.captureSession addInput:self.audioInput];}
}

添加视频输出

- (void)setupVideoOutput {self.videoOutput = [[AVCaptureVideoDataOutput alloc] init];self.videoOutput.alwaysDiscardsLateVideoFrames = YES;[self.videoOutput setSampleBufferDelegate:self queue:self.videoQueue];if ([self.captureSession canAddOutput:self.videoOutput]) {[self.captureSession addOutput:self.videoOutput];}
}

添加音频输出

- (void)setupAudioOutput {self.audioOutput = [[AVCaptureAudioDataOutput alloc] init];[self.audioOutput setSampleBufferDelegate:self queue:self.videoQueue];if ([self.captureSession canAddOutput:self.audioOutput]) {[self.captureSession addOutput:self.audioOutput];}
}

设置视频预览

- (void)setupCaptureVideoPreviewLayer:(UIView *)previewView {_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];CALayer *layer = previewView.layer;_captureVideoPreviewLayer.frame = previewView.bounds;_captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspect;_captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;_captureVideoPreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;[layer insertSublayer:_captureVideoPreviewLayer atIndex:0];
}

开始和结束采集会话

- (void)startSession {if (![self.captureSession isRunning]) {[self.captureSession startRunning];}
}- (void)stopSession{if ([self.captureSession isRunning]) {[self.captureSession stopRunning];}
}

初始化AVAssetWriter,将音视频保存到视频文件

- (void)setUpWriter {if (self.videoURL == nil) {return;}self.assetWriter = [AVAssetWriter assetWriterWithURL:self.videoURL fileType:AVFileTypeMPEG4 error:nil];NSInteger numPixels = kScreenWidth * kScreenHeight;CGFloat bitsPerPixel = 12.0;NSInteger bitsPerSecond = numPixels * bitsPerPixel;NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond),AVVideoExpectedSourceFrameRateKey : @(15),AVVideoMaxKeyFrameIntervalKey : @(15),AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel };self.videoCompressionSettings = @{ AVVideoCodecKey : AVVideoCodecTypeH264,AVVideoWidthKey : @(width * 2),AVVideoHeightKey : @(height * 2),AVVideoScalingModeKey : AVVideoScalingModeResizeAspect,AVVideoCompressionPropertiesKey : compressionProperties };_assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:self.videoCompressionSettings];_assetWriterVideoInput.expectsMediaDataInRealTime = YES;self.audioCompressionSettings = @{ AVEncoderBitRatePerChannelKey : @(28000),AVFormatIDKey : @(kAudioFormatMPEG4AAC),AVNumberOfChannelsKey : @(1),AVSampleRateKey : @(22050) };_assetWriterAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioCompressionSettings];_assetWriterAudioInput.expectsMediaDataInRealTime = YES;if ([_assetWriter canAddInput:_assetWriterVideoInput]){[_assetWriter addInput:_assetWriterVideoInput];}else{NSLog(@"AssetWriter videoInput append Failed");}if ([_assetWriter canAddInput:_assetWriterAudioInput]){[_assetWriter addInput:_assetWriterAudioInput];}else{NSLog(@"AssetWriter audioInput Append Failed");}_canWrite = NO;
}

AVCaptureVideoDataOutputSampleBufferDelegate和AVCaptureAudioDataOutputSampleBufferDelegate音视频处理

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate|AVCaptureAudioDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {@autoreleasepool{if (connection == [self.videoOutput connectionWithMediaType:AVMediaTypeVideo]) {@synchronized(self){[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];}}if (connection == [self.audioOutput connectionWithMediaType:AVMediaTypeAudio]) {@synchronized(self) {[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio];}}}
}- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer ofMediaType:(NSString *)mediaType {if (sampleBuffer == NULL){NSLog(@"empty sampleBuffer");return;}@autoreleasepool{if (!self.canWrite && mediaType == AVMediaTypeVideo){[self.assetWriter startWriting];[self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];self.canWrite = YES;}if (mediaType == AVMediaTypeVideo){if (self.assetWriterVideoInput.readyForMoreMediaData){BOOL success = [self.assetWriterVideoInput appendSampleBuffer:sampleBuffer];if (!success){NSLog(@"assetWriterVideoInput appendSampleBuffer fail");@synchronized (self){[self stopVideoRecorder];}}}}if (mediaType == AVMediaTypeAudio){if (self.assetWriterAudioInput.readyForMoreMediaData){BOOL success = [self.assetWriterAudioInput appendSampleBuffer:sampleBuffer];if (!success){NSLog(@"assetWriterAudioInput appendSampleBuffer fail");@synchronized (self){[self stopVideoRecorder];}}}}}
}

停止视频录制

- (void)stopVideoRecorder {__weak __typeof(self)weakSelf = self;if(_assetWriter && _assetWriter.status == AVAssetWriterStatusWriting) {[_assetWriter finishWritingWithCompletionHandler:^{weakSelf.canWrite = NO;weakSelf.assetWriter = nil;weakSelf.assetWriterAudioInput = nil;weakSelf.assetWriterVideoInput = nil;}];}
}

http://www.lryc.cn/news/213539.html

相关文章:

  • springboot和flask整合nacos,使用openfeign实现服务调用,使用gateway实现网关的搭建(附带jwt续约的实现)
  • 深入浅出排序算法之基数排序
  • CSS选择器、CSS属性相关
  • 设计模式(21)中介者模式
  • JVM虚拟机:通过一个例子解释JVM中栈结构的使用
  • 会自动写代码的AI大模型来了!阿里云推出智能编码助手通义灵码
  • 如何公网远程访问本地WebSocket服务端
  • python 练习 在列表元素中合适的位置插入 输入值
  • 企业级JAVA、数据库等编程规范之命名风格 —— 超详细准确无误
  • 有什么可以自动保存微信收到的图片和视频的方法么
  • 面试算法46:二叉树的右侧视图
  • vite配置terser,压缩代码及丢弃console
  • R语言使用surveyCV包对NHANES数据(复杂调查加权数据)进行10折交叉验证
  • WOS与CNKI数据库的citespace分析教程及常见问题解决
  • NEFU数字图像处理(三)图像分割
  • UEditorPlus v3.6.0 图标补全,精简代码,快捷操作重构,问题修复
  • C++ Set
  • 基于知识库的chatbot或者FAQ
  • ZOC8 for Mac:超越期待的终端仿真器
  • 织梦dedecms后台档案列表显示空白或显示不了文章的解决方法
  • 10本值得阅读的量化交易书籍
  • c++通过对象的地址初始化指针,需要对指针进行释放么(企业链表衍生)
  • CentOS安装MySQL
  • AI:45-基于深度学习的声纹识别
  • Spring-cloud-openfeign拦截器RequestInterceptor接口
  • 自动化测试开发 —— 如何封装自动化测试框架?
  • Leetcode—2.两数相加【中等】
  • 拷贝音频、视频、word等二进制文件的实现方法,不掉帧
  • dmfldr-快速装载-载入(DM8:达梦数据库)
  • Postman测试金蝶云星空Webapi【协同开发云】