原文:AVFoundation Programming Guide
基于時(shí)間的視聽數(shù)據(jù),例如電影文件或視頻流在AV Foundation框架中使用AVAsset表示篡殷。AV Foundation用來表示時(shí)間和媒體的幾個(gè)低級(jí)數(shù)據(jù)結(jié)構(gòu)鸠澈,例如樣本緩沖區(qū)來自Core Media框架歇父。
資源表示
AVAsset是AV Foundation框架的核心類。 它提供了基于時(shí)間的視聽數(shù)據(jù)的格式無關(guān)抽象,例如電影文件或視頻流赘方。 主要關(guān)系如圖6-1所示。 在許多情況下弱左,您使用其中一個(gè)子類:使用composition子類創(chuàng)建新資源(請(qǐng)參閱Editing)窄陡,使用AVURLAsset從給定URL創(chuàng)建新的資源實(shí)例(包括MPMedia框架或Asset Library框架的資源 - 請(qǐng)參閱Using Assets)。
Figure 6-1 AVAsset provides an abstraction of time-based audiovisual data
![](https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AVFoundationPG/Art/avassetHierarchy_2x.png)
資源包含在一起呈現(xiàn)或處理的軌道的集合拆火,每個(gè)軌道的媒體類型跳夭,包括(但不限于)音頻涂圆,視頻,文本币叹,隱藏字幕和字幕润歉。 資源對(duì)象提供關(guān)于整個(gè)資源的信息,例如其持續(xù)時(shí)間或標(biāo)題颈抚,以及呈現(xiàn)的提示踩衩,例如其自然大小。 資源還可以具有由AVMetadataItem的實(shí)例表示的元數(shù)據(jù)贩汉。
軌道由AVAssetTrack的實(shí)例表示驱富,如圖6-2所示。 在通常的簡(jiǎn)單情況下雾鬼,一個(gè)軌道表示音頻萌朱,另一個(gè)表示視頻; 在復(fù)雜的組合中,可能存在多個(gè)重疊的音頻和視頻軌道策菜。
Figure 6-2 AVAssetTrack
![](https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AVFoundationPG/Art/avassetAndTracks_2x.png)
軌道具有許多屬性晶疼,例如其類型(視頻或音頻),視覺和/或聽覺特征又憨,元數(shù)據(jù)和時(shí)間軸翠霍。 軌道還具有一系列格式描述。 該數(shù)組包含CMFormatDescription對(duì)象(請(qǐng)參閱CMFormatDescriptionRef)蠢莺,每個(gè)對(duì)象描述該軌道引用的媒體樣本的格式寒匙。 一個(gè)軌道如果包含統(tǒng)一的媒體(例如,全部使用相同設(shè)置編碼)那么這個(gè)數(shù)組的計(jì)數(shù)為1躏将。
軌道本身可以分為段锄弱,由AVAssetTrackSegment的實(shí)例表示。 段是從源到資源軌道時(shí)間線的時(shí)間映射祸憋。
時(shí)間表示
AV Foundation中的時(shí)間由Core Media框架的原始結(jié)構(gòu)表示会宪。
CMTime代表一段時(shí)間
CMTime是一個(gè)C結(jié)構(gòu),它將時(shí)間表示為有理數(shù)蚯窥,分子(一個(gè)int64_t的值)和分母(一個(gè)int32_t時(shí)間標(biāo)度)掸鹅。 在概念上,時(shí)間標(biāo)度指在每一秒中所占有的比例拦赠。 因此巍沙,如果時(shí)間標(biāo)度是4,每個(gè)單位代表四分之一秒; 如果時(shí)間刻度為10荷鼠,則每個(gè)單位表示十分之一秒句携,依此類推。 您經(jīng)常使用600的時(shí)間尺度允乐,因?yàn)檫@是幾種常用的幀速率的倍數(shù):24 fps的電影务甥,30 fps的NTSC(用于北美和日本的電視)和25 fps的PAL(用于電視 歐洲)牡辽。 使用600的時(shí)間刻度,您可以準(zhǔn)確地表示這些系統(tǒng)中的任意數(shù)量的幀敞临。
除了簡(jiǎn)單的時(shí)間值之外,CMTime結(jié)構(gòu)可以表示非數(shù)值值:+無窮大麸澜,-infinity和無限期挺尿。 它也可以指示時(shí)間是否在某一點(diǎn)被舍入,并且它保持一個(gè)epoch(紀(jì)元)數(shù)字炊邦。
使用CMTime
您可以使用CMTimeMake或相關(guān)函數(shù)如CMTimeMakeWithSeconds(允許您使用float值創(chuàng)建一個(gè)時(shí)間并指定首選的時(shí)間標(biāo)度)來創(chuàng)建一個(gè)時(shí)間编矾。 有一些基于時(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");
}
可用函數(shù)的列表可以參閱 CMTime Reference.
CMTime的特殊值
Core Media提供了一些常量:
kCMTimeZero馁害,kCMTimeInvalid窄俏,kCMTimePositiveInfinity和kCMTimeNegativeInfinity。 CMTime結(jié)構(gòu)可以有多種方式碘菜,例如凹蜈,表示無效的時(shí)間。 要測(cè)試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.
}
您不應(yīng)該將任意CMTime結(jié)構(gòu)的值與kCMTimeInvalid進(jìn)行比較悄晃。
將CMTime表示為對(duì)象
如果需要在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(紀(jì)元)
CMTime結(jié)構(gòu)的epoch通常設(shè)置為0,但您可以使用它來區(qū)分不相關(guān)的時(shí)間軸翁脆。 例如眷蚓,可以在循環(huán)時(shí)通過每個(gè)周期遞增epoch,以區(qū)分循環(huán)0中的時(shí)間N和循環(huán)1中的時(shí)間N.
CMTimeRange表示時(shí)間范圍
CMTimeRange是一個(gè)具有開始時(shí)間和持續(xù)時(shí)間的C結(jié)構(gòu)鹃祖,均表示為CMTime結(jié)構(gòu)溪椎。 時(shí)間范圍不包括開始時(shí)間加上持續(xù)時(shí)間的時(shí)間。
您使用CMTimeRangeMake或CMTimeRangeFromTimeToTime創(chuàng)建一個(gè)時(shí)間范圍恬口。 對(duì)CMTime的epochs有一些約束:
- CMTimeRange結(jié)構(gòu)不能跨越不同的epochs校读。
- 表示時(shí)間戳的CMTime結(jié)構(gòu)中的epoch可能不為零,但您只能在起始字段具有相同epoch的范圍上執(zhí)行范圍操作(例如CMTimeRangeGetUnion)祖能。
- 表示持續(xù)時(shí)間的CMTime結(jié)構(gòu)中的epoch應(yīng)始終為0歉秫,并且該值必須為非負(fù)數(shù)。
使用時(shí)間范圍
Core Media提供了一些函數(shù)养铸,可以用于確定時(shí)間范圍是否包含給定時(shí)間或其他時(shí)間范圍雁芙,確定兩個(gè)時(shí)間范圍是否相等轧膘,計(jì)算時(shí)間范圍的聯(lián)合和交集,如CMTimeRangeContainsTime兔甘,CMTimeRangeEqual谎碍,CMTimeRangeContainsTimeRange和CMTimeRangeGetUnion。
鑒于時(shí)間范圍不包括開始時(shí)間加上持續(xù)時(shí)間的時(shí)間洞焙,以下表達(dá)式總是為false:
CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))
其他的一些可用函數(shù), 參閱 CMTimeRange Reference.
CMTimeRange的特殊值
Core Media分別為零長(zhǎng)度范圍和無效范圍提供了常量kCMTimeRangeZero和kCMTimeRangeInvalid蟆淀。 有許多方式可以使CMTimeRange結(jié)構(gòu)無效,為零或不定式(如果其中一個(gè)CMTime結(jié)構(gòu)是不確定的)澡匪。如果您需要測(cè)試CMTimeRange結(jié)構(gòu)是否有效熔任,零或不確定,您應(yīng)該使用 一個(gè)適當(dāng)?shù)暮?: 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進(jìn)行比較疑苔。
將CMTimeRange結(jié)構(gòu)表示為對(duì)象
如果需要在Core Foundation容器中使用CMTimeRange結(jié)構(gòu),則可以分別使用CMTimeRangeCopyAsDictionary和CMTimeRangeMakeFromDictionary將CMTimeRange結(jié)構(gòu)轉(zhuǎn)換為CFDictionary opaque類型(請(qǐng)參閱CFDictionaryRef)甸鸟。 您還可以使用CMTimeRangeCopyDescription函數(shù)獲取CMTime結(jié)構(gòu)的字符串表示形式惦费。
媒體表示
視頻數(shù)據(jù)及其關(guān)聯(lián)的元數(shù)據(jù)在AV Foundation中由Core Media框架中的不透明對(duì)象表示。 Core Media使用CMSampleBuffer表示視頻數(shù)據(jù)(請(qǐng)參閱CMSampleBufferRef)哀墓。 CMSampleBuffer是一個(gè)Core Foundation風(fēng)格的不透明類型; 一個(gè)實(shí)例包含視頻數(shù)據(jù)幀作為核心視頻像素緩沖區(qū)的樣本緩沖區(qū)(參見CVPixelBufferRef)趁餐。 您可以使用CMSampleBufferGetImageBuffer從樣本緩沖區(qū)訪問像素緩沖區(qū):
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);
從像素緩沖區(qū),您可以訪問實(shí)際的視頻數(shù)據(jù)篮绰。 有關(guān)示例后雷,請(qǐng)參閱 Converting CMSampleBuffer to a UIImage Object。
除了視頻數(shù)據(jù)之外吠各,您還可以獲取視頻幀的其他方面:
時(shí)間信息 您可以分別使用CMSampleBufferGetPresentationTimeStamp和CMSampleBufferGetDecodeTimeStamp獲得原始表示時(shí)間和解碼時(shí)間的時(shí)間戳臀突。
格式化信息。 格式信息封裝在CMFormatDescription對(duì)象中(請(qǐng)參閱CMFormatDescriptionRef)贾漏。 從格式描述中候学,您可以分別使用CMVideoFormatDescriptionGetCodecType和CMVideoFormatDescriptionGetDimensions獲取像素類型和視頻尺寸。
元數(shù)據(jù)纵散。 元數(shù)據(jù)作為附件存儲(chǔ)在字典中梳码。 您使用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對(duì)象
以下代碼顯示如何將CMSampleBuffer轉(zhuǎn)換為UIImage對(duì)象。 使用前伍掀,您應(yīng)仔細(xì)考慮您的要求掰茶。 執(zhí)行轉(zhuǎn)換是比較昂貴的操作。 例如蜜笤,從每秒鐘拍攝的視頻數(shù)據(jù)幀中創(chuàng)建靜止圖像是適當(dāng)?shù)摹?您不應(yīng)該使用它作為一種手段來實(shí)時(shí)地處理來自捕獲設(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);
}