初識AV Foundation-視訊、影象採集
最近上手一個藍芽控制手機視訊錄製的app專案,藍芽這一塊改天再做分享,今天先說說如何呼叫AV Foundation去控制視訊錄製的。AV Foundation 視訊和影象採集主要應用到以上幾個介面類。
AVCaptureDeviceInput 【音視訊輸入口】
作為視訊和音訊的輸入口,而輸入源則是鏡頭(前置和後置)、麥克風(手機自帶麥克風和耳麥),它的初始化如下:
初始化鏡頭輸入
AVCaptureDevice * device =[self getCameraDeviceWithPosition:position];
NSError *initError =nil;
_mVideoInput =[[AVCaptureDeviceInput alloc] initWithDevice:device error:&initError];
if (initError) {
NSLog(@"初始化攝像頭輸入錯誤:%@",initError.localizedDescription);
}
初始化音訊輸入
NSArray *devices =[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
if (!devices) {
NSLog(@"無法獲取音訊裝置");
return;
}
AVCaptureDevice *mdevice =devices.firstObject;
NSError *initError =nil;
_mAudioInput =[[AVCaptureDeviceInput alloc] initWithDevice:mdevice error:&initError];
if (initError) {
NSLog(@"初始化音訊輸入錯誤:%@",initError.localizedDescription);
}
補充說明,獲取前置或後置攝像頭相關裝置的程式碼如下:
-(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
if ([camera position]==position) {
[[camera formats] enumerateObjectsUsingBlock:^(AVCaptureDeviceFormat* obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
return camera;
}
}
return nil;
}
前置攝像頭對應的AVCaptureDevicePosition的值為AVCaptureDevicePositionFront,而後置對應的為AVCaptureDevicePositionBack。
AVCaptureVideoDataOutput【音視訊流輸出口】
該類主要作為音視訊的資料輸出口,可以對取樣得到的音訊流和視訊流進行裁剪,拼接等操作。比如app想達到暫停錄製的話,這個是一個很好地切入點。那麼他的初始化及應用程式碼如下:
初始化視訊輸出
//先初始化輸出佇列
if (!mRecordOutputQueue) {
mRecordOutputQueue =dispatch_queue_create(OutputQueueKey, NULL);
}
_mVideoOutput =[[AVCaptureVideoDataOutput alloc] init];
//設定視訊
_mVideoConnection =[_mVideoOutput connectionWithMediaType:AVMediaTypeVideo];
//設定視訊拍攝方向為正方向
_mVideoConnection.videoOrientation =AVCaptureVideoOrientationPortrait;
//設定取樣輸出回撥代理
[_mVideoOutput setSampleBufferDelegate:self queue:mRecordOutputQueue];
初始化音訊輸出
if (!mRecordOutputQueue) {
mRecordOutputQueue =dispatch_queue_create(OutputQueueKey, NULL);
}
_mAudioOutput =[[AVCaptureAudioDataOutput alloc] init];
_mAudioConnection =[_mAudioOutput connectionWithMediaType:AVMediaTypeAudio];
//設定取樣輸出回撥代理
[_mAudioOutput setSampleBufferDelegate:self queue:mRecordOutputQueue];
而取樣得到的資料將在**- (void)captureOutput:(AVCaptureOutput )captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection )connection代理函式中得以獲取。下面是一段簡單的視訊資料儲存的過程對應的程式碼:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
BOOL isVideo =YES;
if (captureOutput!=_mVideoOutput) {
isVideo =NO;//音訊
}
//初步建立音訊
if (!mVideoEncodeOp&&!isVideo) {
//獲取音訊相關資訊
CMFormatDescriptionRef fmt = CMSampleBufferGetFormatDescription(sampleBuffer);
const AudioStreamBasicDescription *asbd = CMAudioFormatDescriptionGetStreamBasicDescription(fmt);
audio_sample_rate = asbd->mSampleRate;
audio_channel = asbd->mChannelsPerFrame;
//建立檔案路徑
NSString *fileFullPath =[[mFileManger getVideoCachePath] stringByAppendingString:[mFileManger createDefaultVideoFileName:@"mp4"]];
//建立編碼器
mVideoEncodeOp =[[VideoEncodeOperation alloc] initWithPath:fileFullPath audioChannel:audio_channel audioSampleRate:audio_sample_rate videoDimensionWidth:video_dimension_width videoHeight:video_dimension_height];
}
// 計算當前視訊流的時長
CMTime dur = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
if (startTime.value == 0) {
startTime = dur;
}
CMTime sub = CMTimeSubtract(dur, startTime);
currentRecordTime = CMTimeGetSeconds(sub);
// 進行資料編碼
[mVideoEncodeOp encodeFrame:sampleBuffer isVideo:isVideo];
//錄製程式時間回撥
if ([self.delegate respondsToSelector:@selector(recordProgress:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate recordProgress:currentRecordTime];
});
}
CFRelease(sampleBuffer);//釋放記憶體
}
除了使用AVCaptureVideoDataOutput取輸出資料流以外,還可以直接使用AVCaptureMovieFileOutput直接輸出到檔案,但很明顯的是,你無法使用AVCaptureMovieFileOutput對資料流進行諸如裁剪,拼接等操作。根據需求不同可以自主選擇合適的輸出方式。
AVCaptureStillImageOutput【靜態影象輸出】
如果專案需要進行拍攝等操作的話,就要考慮如何接入AVCaptureStillImageOutput來進行操作了。靜態影象捕捉過程可以沿用當前攝像頭的解析度,也可以採用最高質量的解析度,具體過程請參考AVCaptureStillImageOutput的highResolutionStillImageOutputEnabled屬性。以下是初始化和使用的程式碼過程:
初始化
_mStillImageOuput =[[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
_mStillImageOuput.outputSettings =outputSettings;
拍照
for (AVCaptureConnection *connection in _mStillImageOuput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
_mStillImageConnection = connection;
break;
}
}
if (_mStillImageConnection) {
break;
}
}
if (!_mStillImageConnection) {
NSLog(@"無法獲取影象輸出connection");
return;
}
//設定高質量的取樣影象解析度
if (_mStillImageOuput.isHighResolutionStillImageOutputEnabled) {
_mStillImageOuput.highResolutionStillImageOutputEnabled =YES;
}
[_mStillImageOuput captureStillImageAsynchronouslyFromConnection:_mStillImageConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer == NULL) {
return;
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image0 = [UIImage imageWithData:imageData];
}];
AVCaptureConnection【輸入輸出的橋接】
輸入、輸出都已經設定好了,但尚未不清楚他們之間的聯絡,所以我們得給顯式或隱式給他構造一個橋接。程式碼如下:
//視訊輸出與視訊輸入之間的連線[顯式構造]
_mVideoConnection =[_mVideoOutput connectionWithMediaType:AVMediaTypeVideo];
//音訊輸出和音訊輸入之間的連線[顯式構造]
_mAudioConnection =[_mAudioOutput connectionWithMediaType:AVMediaTypeAudio];
//靜態捕捉影象的連線[隱實構造]
for (AVCaptureConnection *connection in _mStillImageOuput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
_mStillImageConnection = connection;
break;
}
}
}
AVCaptureSession【音視訊捕捉總控制】
無論是輸入輸出,都需要受到管理排程,而AVCaptureSession就很好扮演了該角色。你只需要將AVCaptureDeviceInput、AVCaptureVideoDataOutput、AVCaptureAudioDataOutput、AVCaptureStillImageOutput等往裡放,即可將從捕捉到輸出的過程疏通,AVCaptureSession僅僅負責開始捕捉和停止捕捉。執行程式碼如下:
_mRecorderSession =[[AVCaptureSession alloc] init];
if ([_mRecorderSession canAddInput:_mVideoInput]) {
[_mRecorderSession addInput:_mVideoInput];
}
if ([_mRecorderSession canAddInput:_mAudioInput]) {
[_mRecorderSession addInput:_mAudioInput];
}
if ([_mRecorderSession canAddOutput:_mVideoOutput]) {
[_mRecorderSession addOutput:_mVideoOutput];
}
if ([_mRecorderSession canAddOutput:_mAudioOutput]) {
[_mRecorderSession addOutput:_mAudioOutput];
}
if ([_mRecorderSession canAddOutput:_mStillImageOuput]) {
[_mRecorderSession addOutput:_mStillImageOuput];
}
//開始捕捉
[_mRecorderSession startRunning];
//結束捕捉
[_mRecorderSession stopRunning];
到此你就可以進行錄製視訊、拍照等操作了。但還有一個問題,如何顯示實時鏡頭畫面?這個就更簡單了,看下面。
AVCaptureVideoPreviewLayer【實時捕捉畫面】
很顯然這是一個layer,你需要做的是初始化AVCaptureVideoPreviewLayer,然後將其新增到某個view的layer上,即可看到當前的畫面了,程式碼如下:
_mRecoderPreviewLayer =[[AVCaptureVideoPreviewLayer alloc] initWithSession:_mRecorderSession];
//設定比例為鋪滿全屏
_mRecoderPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
CGRect rect =self.view.frame;
_mRecoderPreviewLayer.frame =CGRectMake(0, 0, rect.size.width, rect.size.height);
[self.view.layer insertSublayer:_mRecoderPreviewLayer atIndex:0];
至此,簡單的捕捉過程已經實現。但這都不是重點,重點還在下面:
不知道你常使用iPhone裝置內建相機是否考慮裡面的鏡頭拉近拉遠、慢動作錄製等等功能是怎麼實現的,又或者專業的錄影app FiLMic Pro是如何實現調整錄製的解析度和取樣幀率的?當然上面所介紹的內容是無法解決的,你要是急著去翻文件的話,那先看看下面這篇介紹吧!
New AV Foundation Camera Features for the iPhone 6 and iPhone 6 Plus
或者看看翻譯
iPhone 6和iPhone 6 plus的AV Foundation框架特性
不知道你是否注意到文中常提及的AVCaptureDeviceFormat?這就是今天基本的重點了。。。
首先說說很早之前蘋果在鏡頭使用過程中常用到的一個方法:封裝好幾個合適的Preset,然後讓你去挑。當然這種方法至今還是保留著的,它的好處在於簡便高效。鏡頭本身有很多可調節的相關引數,諸如解析度、取樣幀率、曝光率、白平衡等等。假設每次使用鏡頭都需要你設定各種引數,而你作為普通的開發者,只是希望能夠簡單的呼叫一下鏡頭,錄製和拍照,並不對影象質量有太多額外追求,那麼設定各種鏡頭引數過程對來說是相當崩潰的,那麼蘋果採用一種策略,提供幾種可行的引數設定方案,並將其封裝起來,你只需要使用這些引數方案即可將鏡頭自動設定到相應的引數,這工作量至少成倍減少。那麼它的peset有幾種呢?看看蘋果官網提供的:Video Input Preset。很顯然預設情況下蘋果給AVCaptureSession設定的preset為AVCaptureSessionPresetHigh,而AVCaptureSessionPresetHigh下的iPhone5s後置攝像頭對應的取樣視訊解析度為1080p,既不是最高,也不是最低,而取樣頻率則達到了最高的30FPS,要注意的是不同裝置不同解析度的最高取樣幀率是不同的。而採用preset的程式碼如下:
[_mRecorderSession beginConfiguration];
if (![_mRecorderSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
[_mRecorderSession commitConfiguration];
return NO;
}
_mRecorderSession.sessionPreset =AVCaptureSessionPresetHigh;
[_mRecorderSession commitConfiguration];
那麼現在我們想自己手動調節相應的引數,該如何去設定?此時AVCaptureDeviceFormat就派上用場了。
設定解析度
從New AV Foundation Camera Features for the iPhone 6 and iPhone 6 Plus文中可以看出iPhone6鏡頭解析度能達到4k,也就是3264X2448。那麼如何獲取當前裝置支援的所有的解析度?程式碼如下:
AVCaptureDevice * device =[self getCameraDeviceWithPosition:position];//獲取鏡頭裝置
_mVideoDeviceFormats =[NSArray arrayWithArray:device.formats];//獲取所有裝置格式
那麼如何找到你想要的解析度對應的AVCaptureDeviceFormat?遍歷formats,然後解釋解析度,程式碼如下:
CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
然後比對dims.width或dims.height即可知道那個AVCaptureDeviceFormat是你想要的了,下面下網上一個朋友寫的format資訊列印的函式,看程式碼:
+ (void) dumpCaptureDeviceFormat:(AVCaptureDeviceFormat*)format
{
#if DEBUG
CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
FourCharCode fourCC = CMFormatDescriptionGetMediaSubType(format.formatDescription);
unichar c[4];
c[0] = (fourCC >> 24) & 0xFF;
c[1] = (fourCC >> 16) & 0xFF;
c[2] = (fourCC >> 8) & 0xFF;
c[3] = (fourCC >> 0) & 0xFF;
NSString* fourCCStr = [NSString stringWithCharacters:c length:4];
NSLog(@"解析度(%d x %d) %@", dims.width, dims.height, fourCCStr);
NSLog(@"曝光範圍: %f - %f", CMTimeGetSeconds(format.minExposureDuration), CMTimeGetSeconds(format.maxExposureDuration));
NSLog(@"ISO範圍: %f - %f", format.minISO, format.maxISO);
NSString* ratesStr = @"";
NSArray* supportedFrameRateRanges = [format videoSupportedFrameRateRanges];
int count = 0;
for (AVFrameRateRange* range in supportedFrameRateRanges)
{
if (count > 0)
{
ratesStr = [ratesStr stringByAppendingString:@", "];
}
NSString* rate = [NSString stringWithFormat:@"%f - %f", range.minFrameRate, range.maxFrameRate];
ratesStr = [ratesStr stringByAppendingString:rate];
count++;
}
NSLog(@"取樣幀率範圍: %@", ratesStr);
#endif
}
具體列印出來的資訊如下:
拿到想要的format之後,你可以將該format直接傳給鏡頭device的activeFormat,即可設定當前鏡頭的指定取樣解析度了,程式碼如下:
if ([_mVideoInput.device lockForConfiguration:NULL]) {
[_mVideoInput.device setActiveFormat:format];
[_mVideoInput.device unlockForConfiguration];
}
[_mRecorderSession commitConfiguration];
設定取樣幀率
翻看api文件,沒有發現直接設定鏡頭的取樣頻率,但發現activeVideoMinFrameDuration,activeVideoMaxFrameDuration,然後文件說可以這麼處理來設定取樣的幀率:
-(void)setCaptureVideFrameRate:(NSInteger)rate{
AVCaptureDeviceFormat *format =_mVideoInput.device.activeFormat;
if (!format) {
return ;
}
if ([_mVideoInput.device lockForConfiguration:NULL]) {
//獲取format允許的最大采樣幀率
float maxrate=((AVFrameRateRange*)[format.videoSupportedFrameRateRanges objectAtIndex:0]).maxFrameRate;
//將最小,最大的取樣幀率設為相同的值,即可達到指定取樣幀率的效果
if (rate<=maxrate) {
[_mVideoInput.device setActiveVideoMinFrameDuration:CMTimeMake(10, 10*rate)];
[_mVideoInput.device setActiveVideoMaxFrameDuration:CMTimeMake(10, 10*rate)];
}
[_mVideoInput.device unlockForConfiguration];
}
}
設定鏡頭伸縮
網上有文章說通過設定AVCaptureConnection的videoScaleAndCropFactor來達到鏡頭伸縮效果,但我嘗試過,無法成功,後來還是翻了文件找到了,使用到了AVCaptureDevice的rampToVideoZoomFactor函式來實現,程式碼如下:
-(BOOL)captureZoomIn_Out:(CGFloat)zoomRate velocity:(CGFloat)velocity{
[_mVideoInput.device lockForConfiguration:nil];
AVCaptureDeviceFormat *format =_mVideoInput.device.activeFormat;
//最大的放大因子,最小為1.0
CGFloat maxZoom =format.videoMaxZoomFactor;
CGFloat curZoom =_mVideoInput.device.videoZoomFactor;
curZoom +=(maxZoom-1.0)*zoomRate;
if (curZoom<1.0) {
curZoom =1.0;
}
if (curZoom>maxZoom) {
curZoom =maxZoom;
}
[_mVideoInput.device rampToVideoZoomFactor:curZoom withRate:velocity];
[_mVideoInput.device unlockForConfiguration];
return YES;
}
至此文章基本介紹完畢,希望對你有幫助,程式碼暫時無法網上公開,需要程式碼的朋友可以發郵件到我郵箱1475134837@qq.com.有什麼問題也可留言提問,工作繁忙,繁文絮節就多說了。
相關文章
- Android 音視訊採集那些事Android
- iOS視訊流採集概述(AVCaptureSession)iOSAPTSession
- iOS視訊採集實戰(AVCaptureSession)iOSAPTSession
- 音視訊入門之音訊採集、編碼、播放音訊
- 初識kafka叢集Kafka
- .NET 音訊採集音訊
- 招聘資訊採集
- 抖音商家資訊採集器,抖音小店採集 電話採集
- iOS採集錄製音視訊API選擇推薦iOSAPI
- Android音視訊(一) Camera2 API採集資料AndroidAPI
- 視訊採集:iOS平臺基於AVCaptureDevice的實現iOSAPTdev
- 天貓商品採集軟體,怎麼一鍵批量採集主圖、評論圖以及視訊
- 亞馬遜的主圖視訊/描述視訊有辦法同採集下來嗎?亞馬遜
- python示例 呼叫影象識別服務識別影象Python
- 轉載:iOS音視訊實時採集硬體編碼iOS
- 優惠券採集資訊
- 資料採集知識分享|4大資料採集方式都有什麼?大資料
- 亞馬遜裡的商品視訊是怎麼批量採集的亞馬遜
- 能夠採集小紅書圖片、視訊的軟體,一鍵自動批量採集到電腦上
- 影象識別及處理相關資料集介紹
- 【資訊採集】IBM AIX系統硬體資訊檢視命令(shell指令碼)IBMAI指令碼
- 圖書網站資訊採集網站
- 工商資訊資料採集思路
- 人員基礎資訊採集
- Audio Unit採集音訊實戰音訊
- Python爬蟲初學二(網路資料採集)Python爬蟲
- 深度學習影象視訊壓縮演算法——TNG深度學習演算法
- Prometheus採集Java程式指標資訊PrometheusJava指標
- vs聯合halcon——採集影像(實時採集與單次採集)
- 疫情防控資訊採集難?深度學習手寫體識別來幫忙深度學習
- 利用爬蟲採集音訊資訊完整程式碼示例爬蟲音訊
- 【分散式】 07 系統通訊初識分散式
- 有什麼軟體可以批量採集阿里國際站的商品主圖視訊阿里
- 地圖資料採集,包括百度地圖採集,高德地圖採集,360地圖採集地圖
- 影象識別sift+bow+svm
- 訊息機制篇——初識訊息與訊息佇列佇列
- 出行平臺採集機票價格資訊
- 裝置採集及上報通訊範本