AVFoundation編程指南08-時間和媒體表示

寫在前面

喜歡AVFoundation資料的同學(xué)可以關(guān)注我的專題:《AVFoundation》專輯
也可以關(guān)注我的簡書賬號

正文

基于時間的音視頻數(shù)據(jù),例如電影文件或視頻流,由AVAssetAV Foundation框架中表示。它的結(jié)構(gòu)決定了很多框架的工作原理。 AV Foundation用于表示時間和媒體(如樣本緩沖區(qū))的幾個底層數(shù)據(jù)結(jié)構(gòu)來自Core Media框架。

Assets的表示

AVAssetAV Foundation框架中的核心類。它提供基于時間的視聽數(shù)據(jù)的不依賴格式的抽象,例如電影文件或視頻流。主要關(guān)系如圖6-1所示。在許多情況下,你使用其子類之一:在創(chuàng)建新asset時使用composition子類(請參閱Editing),并使用AVURLAsset從給定URL的媒體創(chuàng)建新asset實例(包括來自MPMedia框架的asset)或Asset Library framework - 請參閱Using Assets)。

圖6-1 AVAsset提供了基于時間的音視頻數(shù)據(jù)的抽象

Asset包含旨在一起呈現(xiàn)或處理的track集合,每個track均勻的媒體類型,包括(但不限于)音頻,視頻,文本,隱藏式字幕和字幕。Asset對象提供有關(guān)整個資源的信息,例如其持續(xù)時間或標(biāo)題,以及演示提示,例如其自然大小。asset還可以具有metadata,由AVMetadataItem的實例表示。

軌道由AVAssetTrack的實例表示,如圖6-2所示。在典型的簡單情況下,一個track代表音頻組件,另一個track代表視頻組件;在復(fù)雜的構(gòu)圖中,可能存在多個重疊的音頻和視頻track。

圖6-2 AVAssetTrack

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,NTSC30 fps(北美和日本用于電視),PAL25 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,kCMTimeInvalidkCMTimePositiveInfinitykCMTimeNegativeInfinity。 CMTime結(jié)構(gòu)有很多種方式可以表示無效的時間。要測試CMTime是有效還是非數(shù)字值,你應(yīng)該使用適當(dāng)?shù)暮?,例?a target="_blank" rel="nofollow">CMTIME_IS_INVALID,CMTIME_IS_POSITIVE_INFINITYCMTIME_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),則可以分別使用CMTimeCopyAsDictionaryCMTimeMakeFromDictionary函數(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ù)時間的時間。

您可以使用CMTimeRangeMakeCMTimeRangeFromTimeToTime創(chuàng)建時間范圍。 CMTime epochsvalue會受到限制:

  • 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)合和交叉,例如CMTimeRangeContainsTimeCMTimeRangeEqual,CMTimeRangeContainsTimeRangeCMTimeRangeGetUnion。

鑒于時間范圍不包括開始時間加上持續(xù)時間的時間,以下表達式始終計算為false

CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))

有關(guān)所有可用功能的列表,請參閱CMTimeRange Reference

CMTimeRange的特殊值

Core Media分別為零長度范圍和無效范圍kCMTimeRangeZerokCMTimeRangeInvalid提供常量。有許多方法,但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_EMPTYCMTIMERANGE_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),則可以分別使用CMTimeRangeCopyAsDictionaryCMTimeRangeMakeFromDictionaryCMTimeRange結(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)。 CMSampleBufferCore 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ù),你還可以檢索視頻幀的許多其他方面:

  • 時間信息。你可以分別使用CMSampleBufferGetPresentationTimeStampCMSampleBufferGetDecodeTimeStamp獲得原始演示時間和解碼時間的準(zhǔn)確時間戳。

  • 格式信息。格式信息封裝在CMFormatDescription對象中(請參閱CMFormatDescriptionRef)。從格式描述中,你可以分別使用CMVideoFormatDescriptionGetCodecTypeCMVideoFormatDescriptionGetDimensions獲取像素類型和視頻尺寸。

  • 元數(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);
}
上一章 目錄 下一章
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容