基于時間的視聽數(shù)據(jù),例如電影文件或視頻流,在AV Foundation框架中被表示AVAsset。其結(jié)構(gòu)決定了大部分的框架作品。AV Foundation用來表示時間和媒體的幾個低級數(shù)據(jù)結(jié)構(gòu),如樣本緩沖區(qū)來自Core Media框架。
Asset表示
AVAsset是AVFoundation框架的核心課程。它提供了基于時間的視聽數(shù)據(jù)的格式無關(guān)抽象,例如電影文件或視頻流。主要關(guān)系如圖6-1所示。在許多情況下,您可以使用其子類之一:創(chuàng)建新資源時使用組合子類(請參閱編輯),并且您可以使用AVURLAsset從給定URL(包括MPMedia框架的Asset或從MPMedia框架的Asset)創(chuàng)建新的Asset實例Asset庫框架 - 請參閱使用Asset)。

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

軌道具有許多屬性,例如其類型(視頻或音頻),視覺和/或聽覺特征(酌情),元數(shù)據(jù)和時間軸(以其父Asset的方式表示)。軌道還具有一系列格式描述。數(shù)組包含CMFormatDescription對象(參見CMFormatDescriptionRef),每個對象描述了軌道引用的媒體樣本的格式。包含統(tǒng)一媒體(例如,使用相同設(shè)置編碼的曲目)的曲目將提供一個計數(shù)為1的數(shù)組。
軌道本身可以分為段,由實例表示AVAssetTrackSegment。段是從源到Asset軌道時間線的時間映射。
時間的表示
AVFoundation的時間由Core Media框架的原始結(jié)構(gòu)表示。
CMTime表示時間長度
CMTime
是一個表示時間為有理數(shù)字的C結(jié)構(gòu),分子(int64_t值)和分母(int32_t
時間刻度)。在概念上,時間刻度指定分子占據(jù)的每個單位的分?jǐn)?shù)。
因此,如果時間刻度是4,每個單位代表四分之一秒; 如果時間尺度為10,則每個單位表示十分之一秒,依此類推。您經(jīng)常使用600的時間刻度,因為這是幾種常用的幀速率的倍數(shù):24 fps的電影,30 fps的NTSC(用于北美和日本的電視)和25 fps的PAL(用于歐洲電視)。使用600的時間刻度,您可以準(zhǔn)確地表示這些系統(tǒng)中的任何數(shù)量的幀。
除了簡單的時間值之外,CMTime結(jié)構(gòu)可以表示非數(shù)值值:+無窮大,-infinity和無限期。它也可以指示時間是否在某點被舍入,并且它保持一個時代數(shù)字。
使用CMTime
您創(chuàng)建一個時間使用CMTimeMake或其中一個相關(guān)的功能,如CMTimeMakeWithSeconds(允許您使用浮點數(shù)創(chuàng)建一個時間并指定首選時間表)。有幾種基于時間的算術(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參考。
CMTime的特殊價值
核心媒體提供了特殊值的常量:kCMTimeZero,kCMTimeInvalid,kCMTimePositiveInfinity,和kCMTimeNegativeInfinity。CMTime例如,結(jié)構(gòu)可以有許多方式表示無效的時間。為了測試是否CMTime是有效的,或者非數(shù)字的值,你應(yīng)該使用合適的微距,例如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。
將CMTime表示為對象
如果需要CMTime在注釋或Core Foundation容器中使用結(jié)構(gòu),則可以分別使用和函數(shù)將CMTime結(jié)構(gòu)轉(zhuǎn)換為CFDictionary不透明類型(參見CFDictionaryRef)。您還可以使用該函數(shù)獲取結(jié)構(gòu)的字符串表示形式。CMTimeCopyAsDictionaryCMTimeMakeFromDictionaryCMTimeCMTimeCopyDescription
Epochs
CMTime結(jié)構(gòu)的時代號通常設(shè)置為0,但您可以使用它來區(qū)分不相關(guān)的時間軸。例如,可以使用呈現(xiàn)循環(huán)在每個循環(huán)中遞增歷元,以區(qū)分N循環(huán)0中的時間N和循環(huán)1中的時間。
CMTimeRange表示時間范圍
CMTimeRange是具有開始時間和持續(xù)時間的C結(jié)構(gòu),都表示為CMTime
結(jié)構(gòu)。時間范圍不包括開始時間加上持續(xù)時間的時間。
您使用CMTimeRangeMake或創(chuàng)建時間范圍CMTimeRangeFromTimeToTime。對CMTime時代的價值有一些限制:
- CMTimeRange 結(jié)構(gòu)不能跨越不同的時代。
- CMTime表示時間戳的結(jié)構(gòu)中的時期可能不為零,但您只能CMTimeRangeGetUnion對起始字段具有相同歷元的范圍執(zhí)行范圍操作(例如)。
- CMTime表示持續(xù)時間的結(jié)構(gòu)中的時期應(yīng)始終為0,該值必須為非負(fù)數(shù)。
使用時間范圍
核心媒體提供了可用于確定時間范圍是否包含給定的時間或其他時間范圍的功能,確定兩個時間段是否相等,并計算工會和時間范圍的交叉點,例如CMTimeRangeContainsTime,CMTimeRangeEqual,CMTimeRangeContainsTimeRange,和CMTimeRangeGetUnion。
鑒于時間范圍不包括開始時間加上持續(xù)時間的時間,以下表達(dá)式總是計算為false:
CMTimeRangeContainsTime(range,CMTimeRangeGetEnd(range))
有關(guān)所有可用函數(shù)的列表,請參閱CMTimeRange參考。
CMTimeRange的特殊值
核心媒體提供常數(shù)零長度范圍和無效范圍,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)表示為對象
如果需要CMTimeRange在注釋或Core Foundation容器中使用結(jié)構(gòu),則可以分別使用和/ 或?qū)⑼该黝愋停ㄒ姡┺D(zhuǎn)換CMTimeRange為CFDictionary不透明類型的結(jié)構(gòu)。您還可以使用該函數(shù)獲取結(jié)構(gòu)的字符串表示形式。CFDictionaryRefCMTimeRangeCopyAsDictionaryCMTimeRangeMakeFromDictionaryCMTimeCMTimeRangeCopyDescription
媒體表示
視頻數(shù)據(jù)及其關(guān)聯(lián)的元數(shù)據(jù)在AV Foundation中由Core Media框架的不透明對象表示。Core Media代表使用CMSampleBuffer(見CMSampleBufferRef)的視頻數(shù)據(jù)。CMSampleBuffer是一種核心基礎(chǔ)風(fēng)格的不透明型; 一個實例包含視頻數(shù)據(jù)幀作為核心視頻像素緩沖區(qū)的樣本緩沖區(qū)(參見CVPixelBufferRef)。您可以使用CMSampleBufferGetImageBuffer以下方式從樣本緩沖區(qū)訪問像素緩沖區(qū)
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);
從像素緩沖區(qū),您可以訪問實際的視頻數(shù)據(jù)。有關(guān)示例,請參閱將CMSampleBuffer轉(zhuǎn)換為UIImage對象。
除了視頻數(shù)據(jù)外,您還可以檢索視頻幀的其他方面:
時序信息。你得到兩個原始演示文稿的時間和使用解碼時間準(zhǔn)確時間戳CMSampleBufferGetPresentationTimeStamp和CMSampleBufferGetDecodeTimeStamp分別。
格式化信息。格式信息封裝在CMFormatDescription對象中(參見CMFormatDescriptionRef)。從格式的描述,您可以使用獲得例如像素型和視頻的尺寸CMVideoFormatDescriptionGetCodecType和CMVideoFormatDescriptionGetDimensions分別。
-
元數(shù)據(jù)。元數(shù)據(jù)作為附件存儲在字典中。你CMGetAttachment用來檢索字典:
CMSampleBufferRef sampleBuffer = <#獲取一個樣本緩沖區(qū)#>; CFDictionaryRef metadataDictionary = CMGetAttachment(sampleBuffer,CFSTR(“MetadataDictionary”,NULL); if(metadataDictionary){ //用元數(shù)據(jù)做某事 }
將CMSampleBuffer轉(zhuǎn)換為UIImage對象
以下代碼顯示了如何將a轉(zhuǎn)換CMSampleBuffer為UIImage對象。使用前請仔細(xì)考慮您的要求。執(zhí)行轉(zhuǎn)換是相對昂貴的操作。例如,從每秒鐘拍攝的視頻數(shù)據(jù)幀中創(chuàng)建靜止圖像是適當(dāng)?shù)摹D粦?yī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);
}