1. 程式人生 > >AVFoundation學習筆記(三): 媒體捕捉、讀取及寫入

AVFoundation學習筆記(三): 媒體捕捉、讀取及寫入

媒體捕捉

捕捉會話

媒體捕捉是AVFoundation的重點功能。其核心類是AVCaptureSession。用於連線輸入輸出的資源。捕捉會話有一個會話預設值,用於控制捕捉資料的格式和質量,預設為AVCaptureSessionPresentHigh。

捕捉裝置

AVCaptureDevice為諸如攝像頭或麥克風等物理裝置定義了一個介面。針對物理裝置定義了大量控制方法,包括對焦、白平衡、曝光等。最常用的方法是

-(AVCaptureDevice*)defaultDeviceWithMediaType:

捕捉裝置的輸入

在使用捕捉裝置進行處理前,首先要新增一個輸入裝置,不過一個捕捉裝置不能直接新增到AVCaptureSession,但可以將它封裝到AVCaptureDeviceInput例項中來新增,使用-deviceInputWithDevice:error:方法。

捕捉的輸出

AVCaptureOutput是一個抽象基類,用於為從捕捉會話得到的資料輸入到目的地。應使用這個類的一些派生類如:AVCaptureStillImageOuptut、AVCaptureMovieFileOutput等。

捕捉預覽

AVCaptureVideoPreviewLayer可滿足在捕捉時的實時預覽,類似於AVPlayerLayer的角色,支援重力概念,可控制視訊內容渲染和縮放、拉伸效果。基本類的關係圖如下所示:

視訊捕捉

建立捕捉會話

建立捕捉會話及新增相關裝置的基礎程式碼如下所示:

當我們需要開始或停止捕捉的時候,使用AVCaptureSession的startRunning和stopRunning方法即可。

座標轉換

對於iPhone來說,螢幕的左上角為(0, 0),右下角為(320, 568),但是對於捕捉裝置如攝像頭來說,通常水平方向不支援旋轉,並且左上角為(0, 0),右下角為(1, 1),那我們如何進行座標轉換呢?

在iOS6之後的版本,AVCaptureVideoPreviewLayer定義了兩個轉換方法:

  • captureDevicePointOfInterestForPoint: 獲取螢幕座標系中的CGPoint資料,返回轉換得到的裝置座標系的CGPoint資料
  • pointForCaptureDevicePointOfInterest: 獲取輸入裝置的CGPoint資料,返回轉換得到的螢幕座標系的CGPoint資料

攝像頭切換

我們在使用iPhone自帶像機的時候,點選切換相機按鈕,可以自由切換前置攝像頭和後置攝像頭,其實實現起來也很簡單,程式碼如下所示:

關於程式碼的幾點說明如下:
* 首先呼叫devicesWithMediaType:方法,傳入AVMediaTypeVideo,獲取本機所有的視訊輸入裝置
* 遍歷裝置,找到當前指定位置的裝置
* 在配置會話之前,需要先呼叫beginConfiguration,標誌配置的開始
* 測試裝置是否可以新增到會話,如果可以的話,查詢會話中的視訊輸入裝置,並移除原來的視訊輸入裝置,並將當前的裝置新增到會話中
* 配置結束之後,需要呼叫commitConfiguration,標誌配置的結束,提交配置
* 使用時,只需要傳入AVCaptureDevicePositionFront(前置攝像頭)、AVCaptureDevicePositionBack(後置攝像頭)即可。

調整對焦

開啟iOS系統相機,在螢幕上點選一下,可以看到相機自動對焦到我們點選的位置。實現思路如下:

  1. 獲取使用者在相機上的點選座標,然後根據前述方法,將座標轉換為裝置座標
  2. 設定和調整焦聚焦距,方法如下:

程式碼很簡單,說明如下:

  • 首先檢查裝置是否支援對焦及是否支援自動對焦模式,對於iPhone手機來說,一般只有後置攝像頭支援對焦。
  • 如果支援對焦,需要呼叫lockForConfiguration: 鎖定裝置,進行配置
  • 設定屬性focusPointOfInterest對焦點為轉換後的裝置座標
  • 設定屬性focusMode為AVCaptureFocusModeAutoFocus(自動對焦)
  • 設定結束之後,需要呼叫unlockForConfiguration 解鎖裝置,提交配置。

調整曝光

iOS系統相機,不但可以點選自動對焦,而且還能點選自動調整曝光,調整曝光的程式碼比較繁瑣,如下所示:

相關程式碼說明如下:

  • 首先檢查裝置是否支援興趣點曝光及自動連續曝光
  • 鎖定裝置,配置exposurePointOfInterest和exposureMode屬性為正確的期望值
  • 判斷裝置是否支援鎖定曝光模式,如果支援,使用KVO來監聽adjustingExposure的狀態,通過觀察該屬性值,可以知道系統調整曝光在何時完成,從而使我們在該點上鎖定曝光
  • 將曝光模式修改為AVCaptureExposureLocked,鎖定曝光。
  • 上述程式碼忘記了一點,在監聽完成之後,我們應該移除監聽器。

調整閃光燈和手電筒模式

iOS在控制中心,有一個手電筒的功能,開啟之後,閃光燈會常亮,可用作手電筒,預設相機的閃光燈也有開、關、自動三種模式,調整閃光燈和手電筒的程式碼一樣,可以設定三種模式
* AVCapture(Torch|Flash)ModeOn: 總是開啟
* AVCapture(Torch|Flash)ModeOff: 總是關閉
* AVCapture(Torch|Flash)ModeAuto: 基於環境光自動調整

設定的程式碼,和之前的設定程式碼幾乎一致,如下所示:

拍攝靜態圖片

拍攝靜態圖片的通用程式碼如下所示:

程式碼說明如下:

  • 首先通過呼叫AVCaptureStillImageOutput的connectionWithMediaType:方法來獲取當前AVCaptureConnection物件的指標,傳遞一個AVMediaType型別
  • 當裝置旋轉時,根據旋轉方向,設定視訊方向,注意到當左旋轉和右旋轉的時候,攝像頭的方向和螢幕方向是相反的
  • 如果接收到一個有效的CMSampleBuffer,呼叫AVCaptureStillImageOutput的jpegStillImageNSDataRepresentation類方法,返回一個表示圖片位元組的NSData

視訊錄製

視訊錄製的基礎程式碼如下所示:

程式碼說明如下:

  • 首先通過呼叫AVCaptureMovieFileOutput的connectionWithMediaType:方法來獲取當前AVCaptureConnection物件的指標,傳遞一個AVMediaType型別
  • 檢查AVCaptureMovieFileOuput的isRecording屬性,確定當前狀態,如果不是在錄製狀態,則繼續
  • 設定enablesVideoStabilizationWhenAvaliable,支援視訊穩定可以顯著提高捕捉到的視訊質量
  • 可以開啟AVCaptureDevice的smoothAutoFocusEnabled屬性,允許進行平滑對焦,減慢攝像頭對焦速度,提供更自然的視訊錄製效果
  • 呼叫startRecordingToOutputFileURL:recordingDelegate:方法,開始錄製
  • 可呼叫stopRecording方法來停止視訊錄製

高階捕捉功能

視訊縮放

在iOS 7之前,蘋果通過AVCaptureConnection的videoScaleAndCropFactor屬性對攝像頭縮放進行了有限制的支援。開發者可通過調整連線縮放的值從預設的1.0增加到videoMaxScaleAndCropFactor。但此屬性使用非常麻煩,而且只能用於AVCaptureStillImageOutput中,這就導致了視訊無法應用。iOS 7之後,AVCaptureDevice提供了videoZoomFactor屬性,用於控制捕捉裝置的縮放等級。其最小值為1.0, 最大值是由捕捉裝置的activeFormat值確定,它是AVCaptureDeviceFormat例項,包括屬性videoMaxZoomFactor。

是否支援縮放

在操作之前,首先應該確定捕捉裝置是否支援縮放,如下所示:

縮放實現

縮放實現的程式碼和前述程式碼基本一致,如下所示:

上述程式碼不需要多解釋,至於裝置zoomFactor為指數形式,按照書上的說法,是這樣可以提供線性增長的感覺。

設定videoZoomFactor屬性會立即調整縮放級別,如果希望在一個時間段內將縮放級別從一個值逐漸調整為另一個值,寫法類似以下程式碼:

其他rate,表示每秒增加縮放因子一倍。速率值通常介於1.0 至 3.0之間。

人臉檢測

使用iOS系統相機,當檢視中有人臉進入時會自動建立相應的焦點,一個黃色的矩形框會顯示在檢測到的人臉位置,並自動對焦,AVFoundation也提供了類似的功能。支援10個人臉的實時檢測。

蘋果首次提供人臉檢測功能是在CoreImage框架中的CIDetector和CIFaceFeature兩個物件,而AVFoundation中,則由AVCaptureMetadataOutput提供,它輸出元資料,當使用人臉檢測時,輸入的具體物件是AVMetadataFaceObject。

AVMetadataFaceObject

AVMetadataFaceObject物件定義了多個用於描述檢測到人臉的屬性,其中最重要的一個屬性是人臉的邊界,它是一個裝置座標。另外還有用於檢測人臉傾斜角度和偏轉角度的屬性rollAngle/yawAngle。

新增輸出

新增AVCaptureMetadataOutput輸出的程式碼如下所示:

新增時設定metadataObjectTypes為AVMetadataObjectTypeFace,並設定代理,新增到AVCaptureSession當中即可。

AVCaptureMetadataOutputObjectsDelegate

實現AVCaptureMetadataOutputObjectsDelegate當中的-captureOutput:didOutputMetadataObjects:fromConnection:方法,如下所示:

機器可讀程式碼識別

AVFoundation另一個強大的功能就是識別機器可讀程式碼,例如二維碼等,此方面可以參考:使用AVCaptureSession掃描二維碼

視訊處理

之前,使用AVCaptureMovieFileOutput,可以將攝像頭的捕捉寫入到檔案,但無法同視訊資料進行互動,如果要實現此功能,就要使用AVCaptureVideoDataOutput。它可以直接訪問攝像頭感測器捕捉到的視訊幀。這樣,我們可以完全控制視訊資料的格式、時間和元資料。使用AVCaptureVideoDataOutput與前述AVCaptureMetadataOutput基本類似,不同的是AVCaptureVideoDataOutput需要通過AVCaptureVideoDataOutputSampleBufferDelegate協議輸出,其中的方法如下:

  • -captureOutput:didOutputSampleBuffer:fromConnection: 每當有一個新的視訊幀寫入時,該方法就會被呼叫。
  • -captureOutput:didDropSampleBuffer:fromConnection: 每當一個遲到的視訊幀被丟棄時,該方法就會被呼叫。

CMSampleBuffer

CMSampleBuffer是一個由CoreMedia框架提供的物件,用於在媒體管道中傳輸數字樣本,CMSampleBuffer的角色是將基礎的樣本資料進行封閉並提供格式和時間資訊,還會加上所有在轉換和處理資料時用到的元資料。

樣本資料灰度處理

樣本資料灰度處理的示例程式如下所示:

相關說明如下:

  1. 首先使用CMSampleBufferGetImageBuffer函式從CMSampleBufferRef中獲取最基本的CVPixelBuffer。CVPixelBuffer在主記憶體中儲存畫素資料,提供了操作的機會。
  2. 在與CVPixelBuffer資料互動之前,必須呼叫CVPixelBufferLockBaseAddress獲取一個相應的記憶體塊的鎖
  3. 使用CVPixelBufferGetWidth和CVPixelBufferGetHeight來確定畫素的寬和高
  4. 使用CVPixelBufferGetBaseAddress得到畫素buffer的基址指標
  5. 處理完畢之後,需呼叫CVPixelBufferUnlockBaseAddress釋放鎖

格式描述

除了原始媒體樣本本身之外,CMSampleBuffer還提供了CMFormatDescription物件的形式來訪問樣本的格式資訊,下面請看使用CMFormatDescription來獲取媒體型別的程式碼:

時間資訊

CMSampleBufferRef還定義了關於媒體樣本的時間資訊,可分別使用CMSampleBufferGetPresentationTimeStamp和CMSampleBufferGetDecodeTimeStamp函式來提取時間資訊,以得到原始的表示時間和解碼時間戳。

示例

《AVFoundation開發祕藉》中有這樣一個3D相機的視例,效果如下圖所示:

結合OpenGLES實現,由於本人不懂OpenGL程式碼,所以只能照搬書上的程式碼:


和前述一樣,在captureOutput:didOutputSampleBuffer:fromConnection: 中拿到CMSampleBufferRef,並從中取得相關資料,然後貼圖到建立的立方體中。

資源的讀取與寫入

AVCaptureVideoDataOutput雖然使我們能夠與視訊底層樣本資料做互動,但卻無法像AVCaptureMovieFileOutput一樣,能將視訊寫入到檔案。但如果我們需要這麼做,又該怎麼辦呢?答案就是使用AVAssetReader和AVAssetWriter。

AVAssetReader

AVAssetReader用於從AVAsset中讀取媒體樣本資料,通常會配置一個或多個AVAssetReaderOutput例項,並通過copyNextSampleBuffer方法來訪問音訊樣本和視訊幀。

AVAssetWriter

AVAssetWriter用於對媒體資源資料進行編碼並寫入到容器檔案。它由一個或多個AVAssetWriterInput物件配置。

將媒體寫入磁碟有兩種方式,一種是按順序寫入,需要將所有的樣本全部捕捉好,然後再寫入,這會導致資料的低效率排列,另一種更好的方法是使用交錯模式,AVAssetWriterInput提供一個readyForMoreMediaData來指示在保持所需要的交錯情況下是否還可以附加更多資料。

AVAssetWriter可用於實時操作和離線操作兩種情況:

  • 實時:當處理實時資源時,比如從AVCaptureVideoDataOutput寫入捕捉的樣本時,AVAssetWriterInput應令expectsMediaDataInRealTime為YES來確保readyForMoreMediaData的值被正確計算。
  • 離線:當從離線資源讀取資源時,如從AVAssetReader讀取樣本,在附加樣本資料時仍需要觀察readyForMoreMediaData屬性,但可用requestMediaDataWhenREadyOnQueue:usingBlock:來控制資料的提供。

讀寫示例

下面通過基礎例項學習在一個離線場景中如何使用AVAssetReader和AVAssetWriter。

讀取

上述程式碼中,建立AVAssetReader,傳遞讀取的AVAsset例項,建立一個AVAssetReaderTrackOutput,從資源的視訊軌道中讀取樣本,將壓縮為BGRA格式,新增輸出到讀取器,並呼叫startReading開始讀取。

離線寫入

示例中建立了一個新的AVAssetWriter物件,並傳遞一個新檔案寫入輸出目的URL和檔案型別,建立一個新的AVAssetWriterInput,它帶有相應的媒體型別和輸出設定,建立一個720p H.264格式的視訊,新增輸入到寫入器,並呼叫startWriting準備開始寫入,如果可以寫入,呼叫startSessionAtSourceTime啟動會話,然後再呼叫requestMediaDataWhenReadyOnQueue:usingBlock: 從輸出入拿取資料並寫入。寫入完成之後,呼叫markAsFinished標記寫入完成,最後呼叫finishWritingWithCompletionHandler:處理寫入完成之後的流程。

實時寫入

實時寫入的基礎程式碼如上所示,說明如下:

  1. 這個方法處理音訊和視訊兩類樣本,通過CMFormatDescription來確定樣本型別
  2. 如果處理的是第一個樣本,則呼叫AVAssetWriter的startWriting和startSessionAtSourceTime啟動一個新的寫入會話
  3. 如果視訊輸入的readyForMoreMediaData屬性為YES,則將畫素buffer連同CMTime附加到AVAssetWriterPixelBufferAdaptor。
  4. 如果處理的是音訊資料,則詢問音訊AVAssetWriterInput是否準備接收更多資料,如果可以,將資料新增到輸入。
  5. 寫入完成之後,呼叫finishWritingWithCompletionHandler:將資料寫入磁碟並處理後續流程。
  6. videoSettings 和 audioSettings 可以使用AVCaptureVideoDataOutput和AVCaptureAudioDataOutput的方法。
- (NSDictionary *)recommendedVideoSettingsForAssetWriterWithOutputFileType:(NSString *)outputFileType;

- (NSDictionary *)recommendedAudioSettingsForAssetWriterWithOutputFileType:(NSString *)outputFileType;

相關推薦

AVFoundation學習筆記(): 媒體捕捉讀取寫入

媒體捕捉 捕捉會話 媒體捕捉是AVFoundation的重點功能。其核心類是AVCaptureSession。用於連線輸入輸出的資源。捕捉會話有一個會話預設值,用於控制捕捉資料的格式和質量,預設為AVCaptureSessionPresentH

VUE學習筆記()-子路由多路由巢狀路由動態路由都是什麼鬼?

最近學習到VUE路由這塊,發現這塊知識點有點多,好容易混亂,我的學習習慣就是先要建立框架,然後再去挨個學習搞懂,所以先來把概念搞搞清楚再說。 首先,我們要知道VUE路由建立的是單頁面路由。 子路由其實和單路由意思是一樣的,單路由應該很好理解,因為我們都知道路由是可以一層一層巢狀的,你可以

https學習筆記----OpenSSL生成root CA簽發證書

open spa centos7 cisc div encrypt 自簽證書 oca led 在https學習筆記二,已經弄清了數字證書的概念,組成和在https連接過程中,客戶端是如何驗證服務器端的證書的。這一章,主要介紹下如何使用openssl庫來創建

【智慧合約學習筆記】geth安裝啟動常用命令,附在Ubuntu中安裝Chrome的方法

1、安裝geth: (1)在Ubuntu上安裝: 開啟終端按順序執行以下四條命令 sudo apt-get install software-properties-common sudo add-apt-repository -y ppa:ethereum/ethereu

Android Studio 學習筆記():簡單控制元件例項

本文針對常用控制元件(Textview、Button、EditText、RadioButton、CheckBox、ImageView)進行簡單說明 控制元件、元件、外掛概念區分 說到控制元件,就不得不區分一些概念。 控制元件(Control):程式設計中用到的部件 元件(Component):軟體的組成部分

Mongodb學習筆記使用asp.net在Mongodb中儲存和讀取圖片檔案

今天練習瞭如何使用c# driver儲存和讀取圖片。 廢話不多說,直接上程式碼。 一、儲存圖片(檔案應該也一樣): private void SaveImgBJSON(string id, byte[] byteImg) {

Pandas學習筆記()——讀取 CSVTXT檔案

pandas是資料分析專用庫。從外部讀寫檔案也屬於資料處理的一部分。pandas提供了多種I/O API函式。支援多種型別資料的讀取。常用的函式如下:讀取函式寫入函式read_csv   to_csvread_excelto_excelread_hdfto_hdfread_s

Odoo10學習筆記:模型(結構化的應用數據)視圖(用戶界面設計)

其他 描述 用戶界面 列表 支持 字段 界面設計 允許 學習 一:模型 1:創建模型 模型屬性:模型類可以使用一些屬性來控制它們的一些行為: _name :創建odoo模型的內部標識符,必含項。 _description :當用戶界面顯示模型時,一個方便用戶的模型記錄標題。

csdn學習筆記:meta元表元方法 __index, __newindexrawsetrawget

重要:在表和元表的__index 和 __newindex 都沒有需要操作的key時,賦值table操作會呼叫__newindex, 取值操作會呼叫__index    元表設定setmetatable t1 = {}; t2 = {}; print("t1=",

csdn學習筆記:meta元表元方法 __index, __newindexrawsetrawget

重要:在表和元表的__index 和 __newindex 都沒有需要操作的key時,賦值table操作會呼叫__newindex, 取值操作會呼叫__index  元表設定setmetatable t1 = {}; t2 = {}; print("t1=",t1);

03html基礎學習筆記---CSS中種選擇器

CSS中三種選擇器:標籤選擇器、類選擇器、ID選擇器 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <

【Redis學習筆記】慢查詢pipeline釋出訂閱BitmapHyperLogLogGEO

目錄 慢查詢 pipeline 釋出訂閱 Bitmap HyperLogLog GEO 慢查詢 生命週期 傳送命令 --> 排隊 --> 執行命令 --> 返回結

Scala 學習筆記() Scala的陣列對映元組集合關鍵字Lazy

文章目錄 一、陣列 1、定長陣列和可變陣列 2、遍歷陣列 3、陣列轉換 4、常用陣列的屬性 二、對映(Map) 1、構建對映的方式

深入理解Java虛擬機器學習筆記——虛擬機器類載入機制

1、概述 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成能夠被虛擬機器直接使用的資料型別,這就是虛擬機器的類載入機制。在Java中,類的載入、校驗、解析和初始化都是在執行期間完成的。 2、類載入的時機 類從被載入都虛擬機器記

jvm學習筆記()類檔案結構java方法數65535上限的原因

瞭解class類檔案結構,對於學習smali也是有必要的!一、class類檔案的結構(8位位元組,一個位元組佔8位,以位元組為基礎單位的二進位制流)儲存結構:class檔案是一組以位元組(8位)為基礎單位的二進位制流,各資料嚴格按照順序緊湊排列在class檔案中,中間沒有任何

深入理解JVM學習筆記(JVM 記憶體分配----逃逸分析與棧上分配)

一、概念 我們之前提到過,JVM堆已經不是物件記憶體分配的唯一選擇。 棧上分配就是java虛擬機器提供的一種優化技術,基本思想是對於那些執行緒私有的物件(指的是不可能被其他執行緒訪問的物件),可以將它們打散分配在棧上,而不是分配在堆上。分配在棧上的好處是可以在函式呼叫結束

Python 建立讀取寫入檔案以及yield關鍵字- 千月的python linux 系統管理指南學習筆記(14)

無論是日誌檔案還是配置檔案都是我們日常運維中常見的型別,學習處理檔案的關鍵是學會如何處理文字資料。Python 包含一個稱為 file 的內建型別,可以用來處理檔案。 建立檔案物件 為了讀取一個現有的檔案,我們需要建立一個新的檔案物件,以用來對檔案進行互動。 open

python學習筆記()字典

映射類型 strong 賦值 python學習 兩個 4.3 所有 tde 及其   字典是一種映射類型的數據類型。辣麽什麽是映射呢?如果看過《數據結構與算法》這一本書的小夥伴應該有印象(我也只是大學學習過,嘻嘻)。   映射:就是將兩個集合一 一對應起來,通過集合a的值

Linux學習筆記():系統執行級與執行級的切換

查看 用戶操作 回車 water hat ntsysv tde 文件表 config 1.Linux系統與其它的操作系統不同,它設有執行級別。該執行級指定操作系統所處的狀態。Linux系統在不論什麽時候都執行於某個執行級上,且在不同的執行級上執行的程序和服務都不同,所要

【Unity 3D】學習筆記十:遊戲元素——遊戲地形

nbsp 3d遊戲 strong 直觀 分辨率 == 摩擦力 fill 世界 遊戲地形 在遊戲的世界中,必然會有非常多豐富多彩的遊戲元素融合當中。它們種類繁多。作用也不大同樣。一般對於遊戲元素可分為兩種:經經常使用。不經經常使用。經常使用的元素是遊戲中比較重要的元素。一