iOS 視頻采集

前言

AVFoundation框架是iOS中很重要的框架摧找,所有與音視頻相關(guān)的軟硬件控制都在這個(gè)框架里。本文主要介紹iOS媒體捕捉和視頻采集蝙斜。

媒體捕捉流程

媒體捕捉(網(wǎng)侵刪).png

簡單介紹

  • AVCaptureSession:媒體捕獲會(huì)話(包括音頻和視頻)箱蝠,負(fù)責(zé)把捕獲的音視頻數(shù)據(jù)輸出到輸出設(shè)備中煤杀,一個(gè)AVCaptureSession可以有多個(gè)輸入輸出诀紊。
    在視頻或音頻捕捉時(shí)谒出,客戶端可以實(shí)例AVCaptureSession,添加適當(dāng)?shù)腁VCaptureInputs、AVCaptureDeviceInput和輸出。
  • AVCaptureInput和AVCaptureDevice:設(shè)備輸入數(shù)據(jù)管理對(duì)象勺疼,可以根據(jù)AVCaptureDevice創(chuàng)建對(duì)應(yīng)AVCaptureDeviceInput對(duì)象,該對(duì)象將會(huì)被添加到AVCaptureSession中管理杀狡。
  • AVCaptureOutput:設(shè)備輸出數(shù)據(jù)管理對(duì)象。
  • AVCaptureVideoPreviewLayer和AVSampleBufferDisplayLayer贰镣,相機(jī)拍攝預(yù)覽圖層捣卤,是CALayer的子類,前者創(chuàng)建需要AVCaptureSession對(duì)象八孝,后者可以直接創(chuàng)建,添加CMSampleBufferRef進(jìn)行展示鸠项。

相關(guān)代碼展示

- (void)configureCamera{
    /// 參數(shù)設(shè)置
    // 默認(rèn)后置攝像頭
    AVCaptureDevicePosition position = AVCaptureDevicePositionBack;
    // 幀率
    int frameRate = 25;
    // 顯色方案
    OSType videoFormat = kCVPixelFormatType_32BGRA;
    // 分辨率高
    int resolutionHeight = 720;
    
    /// 創(chuàng)建AVCaptureSession對(duì)象
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    /// 設(shè)置分辨率
    session.sessionPreset = AVCaptureSessionPreset1280x720;
    /// 獲取攝像頭
    AVCaptureDevice *captureDevice;
    // 默認(rèn)AVCaptureDevicePositionBack干跛,后置攝像頭
    AVCaptureDeviceDiscoverySession *deviceDiscoverySession =  [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:position];
    NSArray *devices = deviceDiscoverySession.devices;
    for (AVCaptureDevice *device in devices) {
        if (AVCaptureDevicePositionBack == device.position) {
            captureDevice = device;
        }else if (AVCaptureDevicePositionFront == device.position){
            captureDevice = device;
        }
    }
    /// 設(shè)置幀率和分辨率高度
    BOOL isSuccess = NO;
    for(AVCaptureDeviceFormat *vFormat in [captureDevice formats]) {
        CMFormatDescriptionRef description = vFormat.formatDescription;
        float maxRate = ((AVFrameRateRange*) [vFormat.videoSupportedFrameRateRanges objectAtIndex:0]).maxFrameRate;
        if (maxRate >= frameRate && CMFormatDescriptionGetMediaSubType(description) == videoFormat) {
            if ([captureDevice lockForConfiguration:NULL] == YES) {
                // 對(duì)比鏡頭支持的分辨率和當(dāng)前設(shè)置的分辨率
                CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(description);
                if (dims.height == resolutionHeight && dims.width == [self.class getResolutionWidthByHeight:resolutionHeight]) {
                    [session beginConfiguration];
                    if ([captureDevice lockForConfiguration:NULL]){
                        captureDevice.activeFormat = vFormat;
                        [captureDevice setActiveVideoMinFrameDuration:CMTimeMake(1, frameRate)];
                        [captureDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, frameRate)];
                        [captureDevice unlockForConfiguration];
                    }
                    [session commitConfiguration];
                    isSuccess = YES;
                }
            }else {
                NSLog(@"%s: 失敗",__func__);
            }
        }
    }
    
    NSError *error;
    
    //添加輸入
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (error != noErr) {
        NSLog(@"配置設(shè)備輸入失敗:%@",error.localizedDescription);
        return;
    }
    [session addInput:input];
    
    //添加輸出
    AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
    if ([session canAddOutput:videoDataOutput]) {
        [session addOutput:videoDataOutput];
    }
    videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:videoFormat]
                                                                forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    //當(dāng)此屬性的值為YES時(shí),接收方將立即丟棄捕獲的幀祟绊,而處理現(xiàn)有幀的調(diào)度隊(duì)列在captureOutput:didOutputSampleBuffer:fromConnection: delegate方法中被阻塞楼入。當(dāng)此屬性的值為NO時(shí),將允許委托在丟棄新幀之前有更多的時(shí)間處理舊幀牧抽,但應(yīng)用程序的內(nèi)存使用量可能會(huì)顯著增加嘉熊。默認(rèn)值為“YES”。(機(jī)翻)
    videoDataOutput.alwaysDiscardsLateVideoFrames = NO;
    
    //創(chuàng)建一個(gè)隊(duì)列接收數(shù)據(jù)
    dispatch_queue_t videoQueue = dispatch_queue_create("video_receive_queue", NULL);
    [videoDataOutput setSampleBufferDelegate:self queue:videoQueue];
    
    //創(chuàng)建接收對(duì)象
    AVCaptureVideoPreviewLayer *videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    videoPreviewLayer.backgroundColor = [[UIColor blackColor] CGColor];
    CGRect frame = [videoPreviewLayer bounds];
    NSLog(@"previewViewLayer = %@",NSStringFromCGRect(frame));
    
    //設(shè)置尺寸和填充方式
    [videoPreviewLayer setFrame:[UIScreen mainScreen].bounds];
    [videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
    
    if ([[videoPreviewLayer connection] isVideoOrientationSupported]) {
        [videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
    }else{
        NSLog(@"不支持視頻定向");
    }
    
    //需要在哪個(gè)view上展示
    UIView *showView = [[UIView alloc] init];
    [showView.layer insertSublayer:videoPreviewLayer atIndex:0];
    
}
//采集視頻的回調(diào)扬舒,如果需要編碼H264/H265,在這里操作
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
    /*
    //另一種展示方式
    AVSampleBufferDisplayLayer *previewLayer = [AVSampleBufferDisplayLayer layer];
    previewLayer.videoGravity =  AVLayerVideoGravityResizeAspectFill;
    [previewLayer enqueueSampleBuffer:sampleBuffer];
    //需要在哪個(gè)view上展示
    UIView *showView = [[UIView alloc] init];
    [showView.layer insertSublayer:previewLayer atIndex:0];
     */
}
///需要考慮橫豎屏情況阐肤,這里暫未考慮
+ (int)getResolutionWidthByHeight:(int)height {
    switch (height) {
        case 2160:
            return 3840;
        case 1080:
            return 1920;
        case 720:
            return 1280;
        case 480:
            return 640;
        default:
            return -1;
    }
}

Tips:配置采集之前,記得申請(qǐng)攝像頭權(quán)限讲坎,如果沒有權(quán)限孕惜,需要自己做判斷,這里省略晨炕。
Demo地址整理后奉上衫画。
有其他不明白的,可以留言瓮栗,看到就會(huì)回復(fù)削罩。
如果喜歡瞄勾,請(qǐng)幫忙點(diǎn)贊。支持轉(zhuǎn)載弥激,轉(zhuǎn)載請(qǐng)附原文鏈接进陡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秆撮,隨后出現(xiàn)的幾起案子四濒,更是在濱河造成了極大的恐慌,老刑警劉巖职辨,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盗蟆,死亡現(xiàn)場離奇詭異,居然都是意外死亡舒裤,警方通過查閱死者的電腦和手機(jī)喳资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腾供,“玉大人仆邓,你說我怎么就攤上這事“楸睿” “怎么了节值?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長榜聂。 經(jīng)常有香客問我搞疗,道長,這世上最難降的妖魔是什么须肆? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任匿乃,我火速辦了婚禮,結(jié)果婚禮上豌汇,老公的妹妹穿的比我還像新娘幢炸。我一直安慰自己,他們只是感情好拒贱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布宛徊。 她就那樣靜靜地躺著,像睡著了一般逻澳。 火紅的嫁衣襯著肌膚如雪岩调。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天赡盘,我揣著相機(jī)與錄音号枕,去河邊找鬼。 笑死陨享,一個(gè)胖子當(dāng)著我的面吹牛葱淳,可吹牛的內(nèi)容都是我干的钝腺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼赞厕,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼艳狐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起皿桑,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤毫目,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后诲侮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镀虐,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年沟绪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刮便。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绽慈,死狀恐怖恨旱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坝疼,我是刑警寧澤搜贤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站钝凶,受9級(jí)特大地震影響入客,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腿椎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夭咬。 院中可真熱鬧啃炸,春花似錦、人聲如沸卓舵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掏湾。三九已至裹虫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間融击,已是汗流浹背筑公。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尊浪,地道東北人匣屡。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓封救,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捣作。 傳聞我的和親對(duì)象是個(gè)殘疾皇子誉结,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 概述 音視頻采集包括兩部分:視頻采集音頻采集 在iOS開發(fā)中,是可以同步采集視頻&音頻的券躁,使用方式也非常簡單 相關(guān)...
    CoreCoder閱讀 594評(píng)論 0 0
  • 前言 在直播和短視頻行業(yè)日益火熱的發(fā)展形勢下惩坑,音視頻開發(fā)(采集、編解碼也拜、傳輸以舒、播放、美顏)等技術(shù)也隨之成為開發(fā)者們...
    G_Jayson閱讀 1,328評(píng)論 1 1
  • 前言 在直播和短視頻行業(yè)日益火熱的發(fā)展形勢下搪泳,音視頻開發(fā)(采集稀轨、編解碼、傳輸岸军、播放奋刽、美顏)等技術(shù)也隨之成為開發(fā)者們...
    iOS亮子閱讀 831評(píng)論 0 7
  • 概述 在直播應(yīng)用中,視頻的采集一般都是用AVFoundation框架艰赞,因?yàn)槔盟覀兡芏ㄖ撇杉曨l的參數(shù)佣谐;也能做切...
    iosmedia閱讀 5,697評(píng)論 0 3
  • 概述 音視頻采集是直播架構(gòu)的第一環(huán),是視頻的來源其實(shí)視頻的采集有多個(gè)應(yīng)用場景:比如二維碼開發(fā) 音視頻采集包括兩部分...
    黃曉堅(jiān)閱讀 516評(píng)論 1 0