版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.09.01 |
前言
AVFoundation
框架是ios中很重要的框架,所有與視頻音頻相關(guān)的軟硬件控制都在這個框架里面韧献,接下來這幾篇就主要對這個框架進(jìn)行介紹和講解。感興趣的可以看我上幾篇研叫。
1. AVFoundation框架解析(一)—— 基本概覽
2. AVFoundation框架解析(二)—— 實現(xiàn)視頻預(yù)覽錄制保存到相冊
3. AVFoundation框架解析(三)—— 幾個關(guān)鍵問題之關(guān)于框架的深度概括
4. AVFoundation框架解析(四)—— 幾個關(guān)鍵問題之AVFoundation探索(一)
5. AVFoundation框架解析(五)—— 幾個關(guān)鍵問題之AVFoundation探索(二)
6. AVFoundation框架解析(六)—— 視頻音頻的合成(一)
7. AVFoundation框架解析(七)—— 視頻組合和音頻混合調(diào)試
8. AVFoundation框架解析(八)—— 優(yōu)化用戶的播放體驗
9. AVFoundation框架解析(九)—— AVFoundation的變化(一)
10. AVFoundation框架解析(十)—— AVFoundation的變化(二)
11. AVFoundation框架解析(十一)—— AVFoundation的變化(三)
12. AVFoundation框架解析(十二)—— AVFoundation的變化(四)
13. AVFoundation框架解析(十三)—— 構(gòu)建基本播放應(yīng)用程序
14. AVFoundation框架解析(十四)—— VAssetWriter和AVAssetReader的Timecode支持(一)
AVAssetReader Reading Timecode
AVAssetReader
對象用于獲取資產(chǎn)的媒體數(shù)據(jù)。 讀取存儲在時間碼軌道中的時間碼媒體樣本以與用于使用AVAssetReader
讀取任何其他媒體(如音頻或視頻媒體)相同的方式執(zhí)行璧针。
將AVAssetReader對象與要讀取的資產(chǎn)分配后嚷炉,為時間碼軌道創(chuàng)建一個AVAssetReaderTrackOutput
,然后調(diào)用 - (BOOL)startReading
來準(zhǔn)備讀取器從資產(chǎn)讀取樣本緩沖區(qū)探橱。 然后將- (CMSampleBufferRef)copyNextSampleBuffer
方法發(fā)送到軌道輸出對象以接收時間碼采樣申屹。
包含時間碼示例的返回的CMSampleBufferRef
可以被解釋為應(yīng)用程序所需的绘证,例如,返回的幀號可以轉(zhuǎn)換為CVSMPTETime
表示哗讥。 有關(guān)實用程序功能嚷那,請參閱本文檔的“時間碼實用程序函數(shù)”部分,該功能允許您執(zhí)行kCMTimeCodeFormatType_TimeCode32
時間碼樣本格式類型的轉(zhuǎn)換杆煞。 檢索描述時間碼示例調(diào)用CMSampleBufferGetFormatDescription
的格式詳細(xì)信息的格式說明魏宽。
下面代碼展示了如何為時間碼媒體軌道創(chuàng)建AVAssetReader
對象和AVAssetReaderTrackOutput
對象。
...
// Create asset reader
assetReader = [[AVAssetReader alloc] initWithAsset:localAsset error:&localError];
success = (assetReader != nil);
// Create asset reader output for the first timecode track of the asset
if (success) {
AVAssetTrack *timecodeTrack = nil;
// Grab first timecode track, if the asset has them
NSArray *timecodeTracks = [localAsset tracksWithMediaType:AVMediaTypeTimecode];
if ([timecodeTracks count] > 0)
timecodeTrack = [timecodeTracks objectAtIndex:0];
if (timecodeTrack) {
timecodeOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:timecodeTrack outputSettings:nil];
[assetReader addOutput:timecodeOutput];
} else {
NSLog(@"%@ has no timecode tracks", localAsset);
}
}
...
下面提供了兩種方法决乎。 第一個演示如何從AVAssetReaderTrackOutput
讀取采樣緩沖區(qū)队询,第二個演示如何檢索采樣數(shù)據(jù)并進(jìn)行解釋。 在這種情況下构诚,從本文檔的Timecode Utility
功能部分調(diào)用其中一個時間碼實用程序函數(shù)蚌斩,將采樣數(shù)據(jù)轉(zhuǎn)換為CVSMPTETime
,然后簡單地輸出值范嘱。
// Read a Timecode Sample Buffer and print out the CVSMPTETime.
- (BOOL)startReadingAndPrintingOutputReturningError:(NSError **)outError
{
BOOL success = YES;
NSError *localError = nil;
// Instruct the asset reader to get ready to do work
success = [assetReader startReading];
if (!success) {
localError = [assetReader error];
} else {
CMSampleBufferRef currentSampleBuffer = NULL;
while ((currentSampleBuffer = [timecodeOutput copyNextSampleBuffer])) {
[self outputTimecodeDescriptionForSampleBuffer:currentSampleBuffer];
}
if (currentSampleBuffer) {
CFRelease(currentSampleBuffer);
}
}
if (!success && outError)
*outError = localError;
return success;
}
- (void)outputTimecodeDescriptionForSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
if (blockBuffer && formatDescription) {
size_t length = 0;
size_t totalLength = 0;
char *rawData = NULL;
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, &length, &totalLength, &rawData);
if (status != kCMBlockBufferNoErr) {
NSLog(@"Could not get data from block buffer");
}
else {
CMMediaType type = CMFormatDescriptionGetMediaSubType(formatDescription);
if (type == kCMTimeCodeFormatType_TimeCode32) {
int32_t *frameNumberRead = (int32_t *)rawData;
CVSMPTETime timecode = timecodeForFrameNumber32UsingFormatDescription(*frameNumberRead, formatDescription);
BOOL dropFrame = CMTimeCodeFormatDescriptionGetTimeCodeFlags(formatDescription) & kCMTimeCodeFlag_DropFrame;
char separator = dropFrame ? ',' : '.';
NSLog(@"%@",[NSString stringWithFormat:@"HH:MM:SS%cFF => %02d:%02d:%02d%c%02d (frame number: %d)", separator, timecode.hours, timecode.minutes, timecode.seconds, separator, timecode.frames, (int)Endian32_Swap(*frameNumberRead)]);
}
}
}
}
時間碼實用函數(shù)
下面代碼提供了實用程序函數(shù)送膳,演示了如何將CVSMPTETime
轉(zhuǎn)換為幀編號,并將幀編號轉(zhuǎn)換為kCMTimeCodeFormatType_TimeCode32
時間代碼媒體樣本格式的CVSMPTETime
丑蛤。
enum {
tcNegativeFlag = 0x80 /* negative bit is in minutes */
};
//CVSMPTETime to Frame Number (kCMTimeCodeFormatType_TimeCode32 Media Sample)
int32_t frameNumber32ForTimecodeUsingFormatDescription(CVSMPTETime timecode, CMTimeCodeFormatDescriptionRef formatDescription)
{
int32_t frameNumber = 0;
if (CMTimeCodeFormatDescriptionGetFormatType(formatDescription) == kCMTimeCodeFormatType_TimeCode32) {
int32_t frameQuanta = CMTimeCodeFormatDescriptionGetFrameQuanta(formatDescription);
frameNumber = timecode.frames;
frameNumber += timecode.seconds * frameQuanta;
frameNumber += (timecode.minutes & ~tcNegativeFlag) * frameQuanta * 60;
frameNumber += timecode.hours * frameQuanta * 60 * 60;
int32_t fpm = frameQuanta * 60;
if (CMTimeCodeFormatDescriptionGetTimeCodeFlags(formatDescription) & kCMTimeCodeFlag_DropFrame) {
int32_t fpm10 = fpm * 10;
int32_t num10s = frameNumber / fpm10;
int32_t frameAdjust = -num10s*(9*2);
int32_t numFramesLeft = frameNumber % fpm10;
if (numFramesLeft > 1) {
int32_t num1s = numFramesLeft / fpm;
if (num1s > 0) {
frameAdjust -= (num1s-1)*2;
numFramesLeft = numFramesLeft % fpm;
if (numFramesLeft > 1)
frameAdjust -= 2;
else
frameAdjust -= (numFramesLeft+1);
}
}
frameNumber += frameAdjust;
}
if (timecode.minutes & tcNegativeFlag) {
frameNumber = -frameNumber;
}
}
return EndianS32_NtoB(frameNumber);
}
// Frame Number (kCMTimeCodeFormatType_TimeCode32 Media Sample) to CVSMPTETime
CVSMPTETime timecodeForFrameNumber32UsingFormatDescription(int32_t frameNumber, CMTimeCodeFormatDescriptionRef formatDescription)
{
CVSMPTETime timecode = {0};
if (CMTimeCodeFormatDescriptionGetFormatType(formatDescription) == kCMTimeCodeFormatType_TimeCode32) {
frameNumber = EndianS32_BtoN(frameNumber);
short fps = CMTimeCodeFormatDescriptionGetFrameQuanta(formatDescription);
BOOL neg = FALSE;
if (frameNumber < 0) {
neg = TRUE;
frameNumber = -frameNumber;
}
if (CMTimeCodeFormatDescriptionGetTimeCodeFlags(formatDescription) & kCMTimeCodeFlag_DropFrame) {
int32_t fpm = fps*60 - 2;
int32_t fpm10 = fps*10*60 - 9*2;
int32_t num10s = frameNumber / fpm10;
int32_t frameAdjust = num10s*(9*2);
int32_t numFramesLeft = frameNumber % fpm10;
if (numFramesLeft >= fps*60) {
numFramesLeft -= fps*60;
int32_t num1s = numFramesLeft / fpm;
frameAdjust += (num1s+1)*2;
}
frameNumber += frameAdjust;
}
timecode.frames = frameNumber % fps;
frameNumber /= fps;
timecode.seconds = frameNumber % 60;
frameNumber /= 60;
timecode.minutes = frameNumber % 60;
frameNumber /= 60;
if (CMTimeCodeFormatDescriptionGetTimeCodeFlags(formatDescription) & kCMTimeCodeFlag_24HourMax) {
frameNumber %= 24;
if (neg && !(CMTimeCodeFormatDescriptionGetTimeCodeFlags(formatDescription) & kCMTimeCodeFlag_NegTimesOK)) {
neg = FALSE;
frameNumber = 23 - frameNumber;
}
}
timecode.hours = frameNumber;
if (neg) {
timecode.minutes |= tcNegativeFlag;
}
timecode.flags = kCVSMPTETimeValid;
}
return timecode;
}
關(guān)于TimeCode64格式類型
在構(gòu)建基于AVFoundation
的媒體應(yīng)用程序時叠聋,建議使用kCMTimeCodeFormatType_TimeCode64('tc64')
格式,而使用本文檔中討論的kCMTimeCodeFormatType_TimeCode32
格式應(yīng)被視為具有與舊版基于QuickTime
的媒體應(yīng)用程序具有特定互操作性要求的應(yīng)用程序的解決方案盏阶, 支持'tmcd'時間碼采樣格式晒奕。
kCMTimeCodeFormatType_TimeCode64
格式的媒體采樣存儲為Big-Endian SInt64
。
注意:'tc64'
時間采樣碼數(shù)據(jù)格式名斟。
CMTimeCodeFormatType_TimeCode64 ('tc64') Timecode Sample Data Format.
The timecode media sample data format is a big-endian signed 64-bit integer representing a frame number that is typically converted to and from SMPTE timecodes representing hours, minutes, seconds, and frames, according to information carried in the format description.
Converting to and from the frame number stored as media sample data and a CVSMPTETime structure is performed using simple modular arithmetic with the expected adjustments for drop frame timecode performed using information in the format description such as the frame quanta and the drop frame flag.
The frame number value may be interpreted into a timecode value as follows:
Hours
A 16-bit signed integer that indicates the starting number of hours.
Minutes
A 16-bit signed integer that contains the starting number of minutes.
Seconds
A 16-bit signed integer indicating the starting number of seconds.
Frames
A 16-bit signed integer that specifies the starting number of frames. This field’s value cannot exceed the value of the frame quanta value in the timecode format description.
后記
未完脑慧,待續(xù)~~~