本文轉自:AVAudioFoundation(6):時間和媒體表示 | www.samirchen.comhtml
本文主要內容來自 AVFoundation Programming Guide。ios
基於時間的音視頻數據,例如電影文件或視頻流,在 AVFoundation 框架中用 AVAsset
來表示。AV Foundation 用於表示時間和媒體的幾個底層數據結構,來自 Core Media 框架。數組
AVAsset
是 AVFoundation 框架中的核心類。它對基於時間的音視頻數據的進行了抽象,例如電影文件或視頻流。主要關係以下圖所示。在許多狀況下,咱們須要使用其子類之一。數據結構
Asset 包含了將在一塊兒顯示或處理的多個軌道,每一個軌道均包含(但不限於)音頻、視頻、文本、可隱藏字幕和字幕。 Asset 提供了關於資源的完整信息,好比時長、標題,以及渲染時的提示、視頻源尺寸等等。 資產還能夠包含由 AVMetadataItem
表示的元數據。app
軌道用 AVAssetTrack
的實例表示,以下圖所示。在典型的簡單狀況下,一個軌道表示音頻份量,另外一個表示視頻份量; 在複雜的組合中,音頻和視頻可能有多個重疊的軌道。框架
軌道具備許多屬性,例如其類型(視頻或音頻),視頻和/或音頻特徵,元數據和時間軸。軌道有一個數組用來描述其格式。該數組包含一組 CMFormatDescription
對象,每一個對象描述軌道對應的媒體樣本的格式。包含統一媒體的軌道(好比,都使用相同的設置來編碼)將提供一個計數爲 1 的數組。ide
軌道自己能夠被劃分紅多段,由 AVAssetTrackSegment
的實例表示。段是從源到資產軌道時間軸的時間映射。函數
AVFoundation 中用來表示時間的數據結構主要來自於 Core Media 框架。測試
CMTime
是一個 C 的數據結構,它表示時間爲有理數,包含分子和分母。分子表示時間值,分母表示時間刻度,分子除以分母則表示時間,單位爲秒。好比當時間刻度是 10,則表示每一個單位的時間值表示 1/10 秒。最經常使用的時間刻度是 600,由於在場景的場景中,咱們用 24 fps 的電影,30 fps 的 NTSC,25 fps 的 PAL。使用 600 的時間刻度,能夠準確地表示這些系統中的任何數量的幀。ui
除了簡單的時間值以外,CMTime
結構能夠表示非數值的值:正無窮大,負無窮大和無定義。 它也能夠指示時間是否在某一點被舍入,而且它能保持一個紀元數字。
下面是一些使用 CMTime
示例:
CMTime time1 = CMTimeMake(200, 2); // 200 half-seconds CMTime time2 = CMTimeMake(400, 4); // 400 quarter-seconds // time1 and time2 both represent 100 seconds, but using different timescales. if (CMTimeCompare(time1, time2) == 0) { NSLog(@"time1 and time2 are the same"); } Float64 float64Seconds = 200.0 / 3; CMTime time3 = CMTimeMakeWithSeconds(float64Seconds , 3); // 66.66... third-seconds time3 = CMTimeMultiply(time3, 3); // time3 now represents 200 seconds; next subtract time1 (100 seconds). time3 = CMTimeSubtract(time3, time1); CMTimeShow(time3); if (CMTIME_COMPARE_INLINE(time2, ==, time3)) { NSLog(@"time2 and time3 are the same"); }
Core Media 提供了一些關於 CMTime
的特殊值,好比:kCMTimeZero
,kCMTimeInvalid
,kCMTimePositiveInfinity
,kCMTimeNegativeInfinity
。要檢查一個表示非數值的 CMTime
是不是合法的,能夠用 CMTIME_IS_INVALID
,CMTIME_IS_POSITIVE_INFINITY
,CMTIME_IS_INDEFINITE
這些宏。
CMTime myTime = <#Get a CMTime#>; if (CMTIME_IS_INVALID(myTime)) { // Perhaps treat this as an error; display a suitable alert to the user. }
不要去拿一個 CMTime
和 kCMTimeInvalid
作比較。
若是要使用 Core Foundation 中的一些容器來存儲 CMTime
,咱們須要進行 CMTime
和 CFDictionary
之間的轉換。這時咱們須要用到 CMTimeCopyAsDictionary
和 CMTimeMakeFromDictionary
這些方法。咱們還能用 CMTimeCopyDescription
來輸出描述 CMTime
的字符串。
一般 CMTime
中的 epoch
設置爲 0,不過咱們也能夠設置它爲其餘值來區分不相關的時間軸。好比,咱們能夠在循環遍歷中遞增 epoch
的值,每個 0 到 N 的循序遞增一下 epoch
來區分不一樣的輪迴。
CMTimeRange
表示的時間範圍包含兩個字段:開始時間(start)和時長(duration),這兩個字段都是 CMTime
類型。須要注意的是一個 CMTimeRange
不含開始時間加上時長算出來的那個時間點,即 [start, start + duration)
。
咱們能夠用 CMTimeRangeMake
和 CMTimeRangeFromTimeToTime
來建立 CMTimeRange
。可是這裏有一些限制:
CMTimeRange
不能跨越不一樣的紀元。CMTime
中的 epoch
字段可能不爲 0,可是咱們只能對 start
字段具備相同 epoch
的 CMTimeRange
執行相關操做(好比 CMTimeRangeGetUnion
)。duration
的 CMTime
結構中的 epoch
應始終爲 0,value
必須爲非負數。Core Media 提供了一系列操做 CMTimeRange
的方法,好比 CMTimeRangeContainsTime
、CMTimeRangeEqual
、CMTimeRangeContainsTimeRange
、CMTimeRangeGetUnion
.
下面的代碼返回值永遠爲 false
:
CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))
Core Media 提供了 kCMTimeRangeZero
和 kCMTimeRangeInvalid
表示零長度 range 和錯誤 range。在不少狀況下,CMTimeRange
結構可能無效,或者是零或不定式(若是其中一個 CMTime
字段是不肯定的)。若是要測試 CMTimeRange
結構是否有效、零或不肯定,可使用一個適當的宏:CMTIMERANGE_IS_VALID
,CMTIMERANGE_IS_INVALID
,CMTIMERANGE_IS_EMPTY
或 CMTIMERANGE_IS_EMPTY
。
CMTimeRange myTimeRange = <#Get a CMTimeRange#>; if (CMTIMERANGE_IS_EMPTY(myTimeRange)) { // The time range is zero. }
不要拿任何 CMTimeRange
和 kCMTimeRangeInvalid
作比較。
若是要在 Core Foundation 提供的容器中使用 CMTimeRange
結構,則能夠分別使用 CMTimeRangeCopyAsDictionary
和 CMTimeRangeMakeFromDictionary
將 CMTimeRange
結構轉換爲 CFDictionary
類型。 還可使用 CMTimeRangeCopyDescription
函數獲取 CMTimeRange
結構的字符串表示形式。
視頻數據及其關聯的元數據在 AVFoundation 中由 Core Media 框架中的對象表示。 Core Media 使用 CMSampleBuffer
表示視頻數據。CMSampleBuffer
是一種 Core Foundation 風格的類型。CMSampleBuffer
的一個實例在對應的 Core Video pixel buffer 中包含了視頻幀的數據(參見 CVPixelBufferRef)。 咱們可使用 CMSampleBufferGetImageBuffer
從樣本緩衝區訪問 pixel buffer:
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);
從 pixel buffer 中,咱們能夠訪問實際的視頻數據。
除了視頻數據以外,您還能夠檢索視頻幀的其餘方面的數據:
CMSampleBufferGetPresentationTimeStamp
和 CMSampleBufferGetDecodeTimeStamp
得到原始演示時間和解碼時間的準確時間戳。CMFormatDescription
對象中。從格式描述中,能夠分別使用 CMVideoFormatDescriptionGetCodecType
和 CMVideoFormatDescriptionGetDimensions
獲取像素類型和視頻尺寸。CMGetAttachment
檢索字典。以下面代碼所示:CMSampleBufferRef sampleBuffer = <#Get a sample buffer#>; CFDictionaryRef metadataDictionary = CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary", NULL); if (metadataDictionary) { // Do something with the metadata. }
如下代碼顯示如何將 CMSampleBuffer
轉換爲 UIImage
對象。 使用前,咱們要仔細考慮對應的需求。 執行轉換是比較昂貴的操做。例如,從每隔一秒鐘拍攝的視頻數據幀建立靜止圖像是合適的,但不該該使用它來實時操縱來自錄製設備的每一幀視頻。
// Create a UIImage from sample buffer data. - (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer { // Get a CMSampleBuffer's Core Video image buffer for the media data CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); // Lock the base address of the pixel buffer CVPixelBufferLockBaseAddress(imageBuffer, 0); // Get the number of bytes per row for the pixel buffer void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); // Get the number of bytes per row for the pixel buffer size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); // Get the pixel buffer width and height size_t width = CVPixelBufferGetWidth(imageBuffer); size_t height = CVPixelBufferGetHeight(imageBuffer); // Create a device-dependent RGB color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // Create a bitmap graphics context with the sample buffer data CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); // Create a Quartz image from the pixel data in the bitmap graphics context CGImageRef quartzImage = CGBitmapContextCreateImage(context); // Unlock the pixel buffer CVPixelBufferUnlockBaseAddress(imageBuffer,0); // Free up the context and color space CGContextRelease(context); CGColorSpaceRelease(colorSpace); // Create an image object from the Quartz image UIImage *image = [UIImage imageWithCGImage:quartzImage]; // Release the Quartz image CGImageRelease(quartzImage); return (image); }