寫在前面
喜歡AVFoundation資料的同學(xué)可以關(guān)注我的專題:《AVFoundation》專輯
也可以關(guān)注我的簡書賬號
正文
基于時間的音視頻數(shù)據(jù),例如電影文件或視頻流,由AVAsset在AV Foundation框架中表示。它的結(jié)構(gòu)決定了很多框架的工作原理。 AV Foundation用于表示時間和媒體(如樣本緩沖區(qū))的幾個底層數(shù)據(jù)結(jié)構(gòu)來自Core Media框架。
Assets的表示
AVAsset是AV Foundation框架中的核心類。它提供基于時間的視聽數(shù)據(jù)的不依賴格式的抽象,例如電影文件或視頻流。主要關(guān)系如圖6-1所示。在許多情況下,你使用其子類之一:在創(chuàng)建新asset時使用composition子類(請參閱Editing),并使用AVURLAsset從給定URL的媒體創(chuàng)建新asset實例(包括來自MPMedia框架的asset)或Asset Library framework - 請參閱Using Assets)。

Asset包含旨在一起呈現(xiàn)或處理的track集合,每個track均勻的媒體類型,包括(但不限于)音頻,視頻,文本,隱藏式字幕和字幕。Asset對象提供有關(guān)整個資源的信息,例如其持續(xù)時間或標(biāo)題,以及演示提示,例如其自然大小。asset還可以具有metadata,由AVMetadataItem的實例表示。
軌道由AVAssetTrack的實例表示,如圖6-2所示。在典型的簡單情況下,一個track代表音頻組件,另一個track代表視頻組件;在復(fù)雜的構(gòu)圖中,可能存在多個重疊的音頻和視頻track。

track具有許多屬性,例如其類型(視頻或音頻),視覺和/或聽覺特征(視情況而定),元數(shù)據(jù)和時間線(以其父asset表示)。track還有一系列格式描述。該數(shù)組包含CMFormatDescription對象(請參閱CMFormatDescriptionRef),每個對象都描述了軌道引用的媒體樣本的格式。包含統(tǒng)一媒體的track(例如,所有使用相同設(shè)置編碼的track)將提供計數(shù)為1的數(shù)組。
track本身可以被劃分為段,由AVAssetTrackSegment的實例表示。段是從源到asset跟蹤時間線的時間映射。
時間的表示
AV Foundation的時間由Core Media框架中的原始結(jié)構(gòu)表示。
CMTime表示時間長度
CMTime是一種C語言的數(shù)據(jù)結(jié)構(gòu),它將時間表示為有理數(shù),具有分子(int64_t值)和分母(int32_t時間刻度)。從概念上講,時間刻度指定分子中每個單位占秒的分數(shù)。因此,如果時間刻度為4,則每個單位代表四分之一秒;如果時間刻度為10,則每個單位代表十分之一秒,依此類推。你經(jīng)常使用600的時間刻度,因為這是幾種常用幀速率的倍數(shù):電影為24 fps,NTSC為30 fps(北美和日本用于電視),PAL為25 fps(用于電視)歐洲)。使用600的時間刻度,你可以精確地表示這些系統(tǒng)中的任意數(shù)量的幀。
除了簡單的時間值,CMTime結(jié)構(gòu)還可以表示非數(shù)字值:+無窮大,無窮大和無限期。它還可以指示時間是否在某個時刻被舍入,并且它保持一個紀(jì)元數(shù)。
使用CMTime
你可以使用CMTimeMake或其中一個相關(guān)函數(shù)(如CMTimeMakeWithSeconds)創(chuàng)建時間(允許你使用浮點值創(chuàng)建時間并指定首選時間刻度)?;跁r間的算術(shù)和比較時間有幾個函數(shù),如以下示例所示:
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");
}
有關(guān)所有可用功能的列表,請參閱CMTime Reference。
CMTime的特殊值
Core Media提供特殊值的常量:kCMTimeZero,kCMTimeInvalid,kCMTimePositiveInfinity和kCMTimeNegativeInfinity。 CMTime結(jié)構(gòu)有很多種方式可以表示無效的時間。要測試CMTime是有效還是非數(shù)字值,你應(yīng)該使用適當(dāng)?shù)暮?,例?a target="_blank" rel="nofollow">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結(jié)構(gòu)的值與kCMTimeInvalid進行比較。
將CMTime作為一個對象來表示
如果需要在注釋或Core Foundation容器中使用CMTime結(jié)構(gòu),則可以分別使用CMTimeCopyAsDictionary和CMTimeMakeFromDictionary函數(shù)將CMTime結(jié)構(gòu)轉(zhuǎn)換為CFDictionary opaque類型(請參閱CFDictionaryRef)。你還可以使用CMTimeCopyDescription函數(shù)獲取CMTime結(jié)構(gòu)的字符串表示形式。
Epochs
CMTime結(jié)構(gòu)的epoch號通常設(shè)置為0,但你可以使用它來區(qū)分不相關(guān)的時間軸。例如,可以使用呈現(xiàn)循環(huán)遞增每個周期的時期,以區(qū)分循環(huán)0中的時間N和循環(huán)1中的時間N。
CMTimeRange表示時間范圍
CMTimeRange是一個C語言的數(shù)據(jù)結(jié)構(gòu),具有開始時間和持續(xù)時間,均表示為CMTime結(jié)構(gòu)。時間范圍不包括開始時間加上持續(xù)時間的時間。
您可以使用CMTimeRangeMake或CMTimeRangeFromTimeToTime創(chuàng)建時間范圍。 CMTime epochs的value會受到限制:
CMTimeRange結(jié)構(gòu)不能跨越不同的epoch。表示時間戳的
CMTime結(jié)構(gòu)中的timestamp可能是nonzero,但你只能對起始字段具有相同epoch的范圍執(zhí)行range操作(例如CMTimeRangeGetUnion)。表示持續(xù)時間的
CMTime結(jié)構(gòu)中的epoch應(yīng)始終為0,并且該值必須為非負。
使用時間范圍
Core Media提供的功能可用于確定時間范圍是包含給定時間還是其他時間范圍,以確定兩個時間范圍是否相等,以及計算時間范圍的聯(lián)合和交叉,例如CMTimeRangeContainsTime,CMTimeRangeEqual,CMTimeRangeContainsTimeRange和CMTimeRangeGetUnion。
鑒于時間范圍不包括開始時間加上持續(xù)時間的時間,以下表達式始終計算為false:
CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))
有關(guān)所有可用功能的列表,請參閱CMTimeRange Reference
CMTimeRange的特殊值
Core Media分別為零長度范圍和無效范圍kCMTimeRangeZero和kCMTimeRangeInvalid提供常量。有許多方法,但CMTimeRange結(jié)構(gòu)可能無效,或零或不確定(如果其中一個CMTime結(jié)構(gòu)是無限的。如果你需要測試CMTimeRange結(jié)構(gòu)是有效,零還是無限期,你應(yīng)該使用適當(dāng)?shù)暮辏?a target="_blank" rel="nofollow">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.
}
你不應(yīng)該將任意CMTimeRange結(jié)構(gòu)的值與kCMTimeRangeInvalid進行比較。
將CMTimeRange結(jié)構(gòu)表示為對象
如果需要在注釋或Core Foundation容器中使用CMTimeRange結(jié)構(gòu),則可以分別使用CMTimeRangeCopyAsDictionary和CMTimeRangeMakeFromDictionary將CMTimeRange結(jié)構(gòu)轉(zhuǎn)換為CFDictionary opaque類型(請參閱CFDictionaryRef)。你還可以使用CMTimeRangeCopyDescription函數(shù)獲取CMTime結(jié)構(gòu)的字符串表示形式。
Media的表示
視頻數(shù)據(jù)及其相關(guān)元數(shù)據(jù)在AV Foundation中由來自Core Media框架的opaque對象表示。Core Media使用CMSampleBuffer表示視頻數(shù)據(jù)(請參閱CMSampleBufferRef)。 CMSampleBuffer是Core Foundation風(fēng)格的opaque類型;實例包含視頻數(shù)據(jù)幀的樣本緩沖區(qū)作為核心視頻像素緩沖區(qū)(請參閱CVPixelBufferRef)。你可以使用CMSampleBufferGetImageBuffer從示例緩沖區(qū)訪問像素緩沖區(qū):
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);
從像素緩沖區(qū),您可以訪問實際的視頻數(shù)據(jù)。有關(guān)示例,請參閱Converting CMSampleBuffer to a UIImage Object。
除了視頻數(shù)據(jù),你還可以檢索視頻幀的許多其他方面:
時間信息。你可以分別使用CMSampleBufferGetPresentationTimeStamp和CMSampleBufferGetDecodeTimeStamp獲得原始演示時間和解碼時間的準(zhǔn)確時間戳。
格式信息。格式信息封裝在
CMFormatDescription對象中(請參閱CMFormatDescriptionRef)。從格式描述中,你可以分別使用CMVideoFormatDescriptionGetCodecType和CMVideoFormatDescriptionGetDimensions獲取像素類型和視頻尺寸。-
元數(shù)據(jù)。元數(shù)據(jù)作為附件存儲在字典中。你使用CMGetAttachment來檢索字典:
CMSampleBufferRef sampleBuffer = <#Get a sample buffer#>; CFDictionaryRef metadataDictionary = CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary", NULL); if (metadataDictionary) { // Do something with the metadata. }
將CMSampleBuffer轉(zhuǎn)換為UIImage對象
以下代碼顯示如何將CMSampleBuffer轉(zhuǎn)換為UIImage對象。在使用之前,你應(yīng)該仔細考慮您的要求。執(zhí)行轉(zhuǎn)換是相對昂貴的操作。例如,從每秒鐘左右拍攝的視頻數(shù)據(jù)幀創(chuàng)建靜止圖像是合適的。你不能將此作為一種手段來實時操作來自捕獲設(shè)備的每一幀視頻。
// 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);
}
| 上一章 | 目錄 | 下一章 |
|---|