AVFoundation - Временной код поддержка с AVAssetWriter и AVAssetReader
AVFoundation включает поддержку дорожек временного кода. Дорожки временного кода позволяют Вам хранить внешнюю информацию временного кода, такую как временной код SMPTE в файлах фильма в формате QuickTime (.mov). Это техническое примечание обсуждает, как записать и считать 32-разрядный временной код ('tmcd'
) выборка носителей с AVAssetWriter
и AVAssetReader
.
Предпосылки
Это техническое примечание предполагает, что читатель знаком с AVFoundation и более в частности с AVAssetWriter
и AVAssetReader
классы. Некоторый фон с Демонстрационным Буфером CoreMedia APIs также принят. Посмотрите раздел References этого документа для получения дополнительной информации.
О временных кодах
Файлы фильма в формате QuickTime (.mov) допускают хранение информации синхронизации, полученной из материала первоисточника фильма, такого как продолжительность кадра, влияющая, как носители интерпретируются и играются.
Файлы фильма в формате QuickTime также позволяют Вам хранить дополнительную информацию синхронизации, которая не будет в частности влиять на воспроизведение носителей. Эта дополнительная информация синхронизации обычно получалась бы из материала первоисточника; например, как временной код SMPTE. В сущности можно думать о временном коде как об обеспечении ссылки между носителями определенная информация синхронизации и информация синхронизации от материала первоисточника.
Информация временного кода файла фильма в формате QuickTime хранится в дорожке временного кода. Дорожки временного кода содержат описание формата, состоящее из следующего:
Данные о формате временного кода, указывающие характеристики временного кода и как интерпретировать информацию о временном коде.
Числа кадра как способ отобразиться от данного кадра фильма, с точки зрения временных стоимостей AVFoundation, к его соответствующему значению временного кода.
Исходная идентификационная информация, которая дополнительно может идентифицировать исходное имя, например имя исходного барабана, клипа или проекта.
Информация, хранящаяся в дорожке, находится способом, который независим от любого определенного стандарта временного кода. Формат этой информации достаточно гибок для размещения всех известных стандартов временного кода, включая временной код SMPTE, который является фокусом этого технического примечания. Данные о формате временного кода предоставляют пользователям AVFoundation параметры для понимания временного кода и преобразования временных стоимостей во временные стоимости временного кода и наоборот.
Один ключевой атрибут временного кода касается метода, используемого для синхронизации значений временного кода с видеокадрами. Большая часть материала источника видеосигнала зарегистрирована в частотах кадров целого числа. Например, и PAL и видео SECAM содержат точно 25 кадров в секунду. Однако некоторый материал источника видеосигнала не зарегистрирован в частотах кадров целого числа. В частности NTSC окрашивает, видео содержит 29,97 кадров в секунду (хотя оно обычно упоминается как 30 видео кадров в секунду). Однако значения временного кода NTSC соответствуют полным 30 уровням кадров в секунду; это - пережиток от NTSC черно-белое видео. Для таких источников видеосигнала Вам нужен механизм, исправляющий ошибку, которая будет разрабатывать в течение долгого времени между значениями временного кода и фактическими видеокадрами.
Общий метод для поддержания синхронизации между значениями временного кода и видеоданными вызывают dropframe. Вопреки его имени dropframe метод фактически пропускает значения временного кода на предопределенном уровне для хранения временного кода, и видеоданные синхронизировались. Это фактически не отбрасывает видеокадры. В видео цвета NTSC, использующем dropframe метод, значения временного кода пропускают два значения кадра каждую минуту, за исключением мелких значений, которые являются равномерно делимыми десять. Так значения временного кода NTSC, выраженные как HH:MM:SS:FF (часы, минуты, секунды, кадры) пропуск от 00:00:59:29 до 00:01:00:02 (пропускающий 00:01:00:00 и 00:01:00:01). Существует флаг в описании формата временного кода, указывающем, использует ли временной код метод кадра отбрасывания.
AVAssetWriter пишущий временной код
AVAssetWriter
объект используется для записи данных носителей в новый файл указанного аудиовизуального контейнерного типа. Для временного кода это должно быть файлом фильма в формате QuickTime (.mov), который определяется в AVFoundation как AVFileTypeQuickTimeMovie
.
Создание дорожки временного кода и добавление носителей временного кода выполняются таким же образом используемые для создания любого другого типа дорожки для добавления аудио или видео использования носителей AVAssetWriter
. Создайте AVAssetWriter
для AVFileTypeQuickTimeMovie
тип файла. Создайте AVAssetWriterInput
с типом среды временного кода AVMediaTypeTimecode
, записать дорожку временного кода. Сделайте описание формата, описывающее определенный тип среды временного кода используемый (kCMTimeCodeFormatType_TimeCode32
наиболее распространено) путем вызова CMTimeCodeFormatDescriptionCreate
. Это описание формата определяет формат данных временного кода, который Вы будете добавлять к дорожке. Тогда используйте базовый демонстрационный буфер носителей APIs такой как CMSampleBufferCreate
создать демонстрационный буфер носителей и наконец AVAssetWriterInput
-(BOOL)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer
метод для добавления выборки носителей временного кода буферизует к дорожке временного кода.
Создание дорожки временного кода
Для создания дорожки временного кода создают AVAssetWriterInput
с AVMediaTypeTimecode
тип среды.
При работе с дорожками временного кода необходимо также определить отношение между дорожкой временного кода и одним или более видеотреками, с которыми связан временной код. Это сделано при помощи видео AVAssetWriterInput
объекты -(void)addTrackAssociationWithTrackOfInput:(AVAssetWriterInput *)input type:(NSString *)trackAssociationType
метод для установки этой ассоциации дорожки.
Перечисление 1 демонстрирует, как создать AVAssetWriter
и AVAssetWriterInput
объекты и для дорожки видео и для временного кода и как установить ассоциацию дорожки.
Перечисление 1 , создающее a AVAssetWriterInput
для носителей TimeCode.
AVAssetWriter *assetWriter = [[AVAssetWriter alloc] initWithURL:localOutputURL |
fileType:AVFileTypeQuickTimeMovie error:&localError]; |
... |
// Setup video track to write video samples into |
AVAssetWriterInput *videoInput = [AVAssetWriterInput assetWriterInputWithMediaType: |
[videoTrack mediaType] outputSettings:nil]; |
[assetWriter addInput:videoInput]; |
... |
// Setup timecode track to write timecode samples into |
AVAssetWriterInput *timecodeInput = |
[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeTimecode |
outputSettings:nil]; |
// add track association with video track |
[videoInput addTrackAssociationWithTrackOfInput:timecodeInput |
type:AVTrackAssociationTypeTimecode]; |
[assetWriter addInput:timecodeInput]; |
Каждая выборка в дорожке временного кода предоставляет информацию временного кода для промежутка времени фильма. Выборка носителей временного кода включает информацию о продолжительности. В результате Вы обычно добавляете каждую выборку временного кода после создания соответствия, довольного дорожка или дорожки.
@property (nonatomic) CGSize naturalSize
Описание формата временного кода
Описание формата носителя временного кода содержит управляющую информацию, позволяющую AVFoundation интерпретировать выборки. Фактические демонстрационные данные содержат число кадра, идентифицирующее один или несколько кадров видеоконтента, использующих этот временной код. Сохраненный как a Big-Endian
int32_t
при использовании типа формата kCMTimeCodeFormatType_TimeCode32
, это значение идентифицирует первый кадр в группе кадров, использующих эту выборку временного кода. В случае фильма, сделанного из исходного материала, не содержащего редактирований, Вам только была бы нужна одна выборка. Когда исходный материал содержит редактирования, Вам обычно нужна одна выборка для каждого редактирования. Те выборки содержали бы числа кадра кадров, начинающих каждую новую группу кадров.
Создать использование описания формата CMTimeCodeFormatDescriptionCreate
.
/*! |
@function CMTimeCodeFormatDescriptionCreate |
@abstract Creates a format description for a timecode media. |
@discussion The caller owns the returned CMFormatDescription, |
and must release it when done with it. All input parameters |
are copied (the extensions are deep-copied). |
The caller can deallocate them or re-use them after making this call. |
*/ |
CM_EXPORT OSStatus CMTimeCodeFormatDescriptionCreate( |
CFAllocatorRef allocator, /*! @param allocator |
Allocator to be used for creating the |
FormatDescription object */ |
CMTimeCodeFormatType timeCodeFormatType, /*! @param timeCodeFormatType |
One of the CMTimeCodeFormatTypes */ |
CMTime frameDuration, /*! @param frameDuration |
Duration of each frame (eg. 100/2997) */ |
uint32_t frameQuanta, /*! @param frameQuanta |
Frames/sec for timecode (eg. 30) OR |
frames/tick for counter mode */ |
uint32_t tcFlags, /*! @param tcFlags |
kCMTimeCodeFlag_DropFrame, |
kCMTimeCodeFlag_24HourMax, |
kCMTimeCodeFlag_NegTimesOK */ |
CFDictionaryRef extensions, /*! @param extensions |
Keys are always CFStrings. Values are |
always property list objects (ie. CFData). |
May be NULL. */ |
CMTimeCodeFormatDescriptionRef *descOut) /*! @param descOut |
Receives the newly-created CMFormatDescription. */ |
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0); |
Описание формата временного кода определяет формат и содержание выборки носителей временного кода и составлено из следующей информации:
Тип формата временного кода (CMTimeCodeFormatType) - один из типов формата временного кода, например
kCMTimeCodeFormatType_TimeCode32
, который описывает демонстрационный тип как 32-разрядное целое число.Продолжительность кадра (
CMTime
) - продолжительность каждого кадра (например, 100/2997). Это указывает, сколько времени каждый кадр длится, как определено к этому времени масштаб.Кванты кадра (
uint32_t
) - Указывает число кадров, сохраненных в секунду, например 30.Флаги временного кода (
uint32_t
) - Флаги, обеспечивающие некоторые данные о формате временного кода, напримерkCMTimeCodeFlag_DropFrame
указание, что временной код иногда отбрасывает кадры для пребывания в синхронизации. Некоторые временные коды, выполненные в кроме целого числа кадров в секунду. Например, видео NTSC достигает 29,97 кадров в секунду. Для ресинхронизации между уровнем временного кода и 30 скоростями воспроизведения кадров в секунду, временной код отбрасывает кадр в предсказуемое время (почти таким же способом, которым високосные годы сохраняют календарь синхронизируемым). Установите этот флаг в 1, если временной код использует метод кадра отбрасывания. Другие флаги включаютkCMTimeCodeFlag_24HourMax
указать, что временной код оценивает обертку в 24 часа. Установите этот флаг в 1, если значение часа временного кода переносится (т.е. возвращается к 0) в 24 часа иkCMTimeCodeFlag_NegTimesOK
указать, что временной код поддерживает отрицательные временные стоимости. Установите этот флаг в 1, если временной код позволяет отрицательные величины.Расширения (
CFDictionary
) - Дополнительный словарь, предоставляющий источник, называют информацию (kCMTimeCodeFormatDescriptionExtension_SourceReferenceName
). Это расширение является aCFDictionary
содержа следующие два ключа;kCMTimeCodeFormatDescriptionKey_Value
aCFString
иkCMTimeCodeFormatDescriptionKey_LangCode
aCFNumber
. Ключ описания мог бы содержать имя видеозаписи, из которой создавался фильм.
Создание описания формата временного кода
Лучший способ понять, как отформатировать и интерпретировать описание формата временного кода, состоит в том, чтобы рассмотреть пример. При создании фильма из источника видеосигнала NTSC, зарегистрированного в 29,97 кадрах в секунду Вы создали бы описание формата следующим образом:
Перечисление 2 создает описание формата TimeCode.
... |
CMTimeCodeFormatDescriptionRef formatDescription = NULL; |
uint32_t tcFlags = kCMTimeCodeFlag_DropFrame | kCMTimeCodeFlag_24HourMax; |
OSStatus status = CMTimeCodeFormatDescriptionCreate(kCFAllocatorDefault, |
kCMTimeCodeFormatType_TimeCode32, |
CMTimeMake(100, 2997), |
30, |
tcFlags, |
NULL, |
&formatDescription); |
... |
Естественная частота кадров фильма 29,97 кадров в секунду получена путем деления значения масштаба времени к продолжительности кадра (2997 / 100). Поле флагов указывает, что временной код использует метод кадра отбрасывания, чтобы повторно синхронизировать естественную частоту кадров фильма 29,97 кадров в секунду с ее скоростью воспроизведения 30 кадров в секунду.
Выборка носителей временного кода
Выборка носителей, записанная в дорожку, содержит число кадра, идентифицирующее один или несколько видеокадров, использующих этот временной код. При использовании типа формата временного кода kCMTimeCodeFormatType_TimeCode32
это число кадра сохранено как a Big-Endian
int32_t
.
Учитывая описание формата временного кода, можно преобразовать от чисел кадра до временных стоимостей SMPTE и от временных стоимостей SMPTE для структурирования чисел. Простой пример этого - для временной стоимости SMPTE 00:00:12:15 (HH:MM:SS:FF) 30 футов в секунду, кадра неотбрасывания, Вы получили бы число кадра 375 ((12*30) + 15). Посмотрите раздел Timecode Utility Functions этого документа для двух служебных функций, позволяющих Вам выполнять их назад и вперед преобразования для kCMTimeCodeFormatType_TimeCode32
демонстрационный тип формата временного кода.
При работе с временными стоимостями SMPTE Базового Видео CVSMPTETime
структура используется для хранения этих временных стоимостей. CVSMPTETime
структура позволяет Вам интерпретировать информацию времени как временные стоимости (HH:MM:SS:FF) и определяется следующим образом:
struct CVSMPTETime |
{ |
SInt16 subframes; |
SInt16 subframeDivisor; |
UInt32 counter; |
UInt32 type; |
UInt32 flags; |
SInt16 hours; |
SInt16 minutes; |
SInt16 seconds; |
SInt16 frames; |
}; |
typedef struct CVSMPTETime CVSMPTETime; |
Если значения временного кода позволяют отрицательные временные стоимости (поле флагов описания формата имеет kCMTimeCodeFlag_NegTimesOK
отметьте набор), мелкое поле CVSMPTETime
структура указывает, положительна ли временная стоимость или отрицательна. Если tcNegativeFlag (0x80)
бит мелкого поля установлен, временная стоимость отрицательна.
Демонстрационные данные временного кода
CMTimeCodeFormatType_TimeCode32 ('tmcd') Timecode Sample Data Format. |
The timecode media sample data format is a big-endian signed 32-bit integer and may be interpreted into a timecode value as follows: |
Hours |
An 8-bit unsigned integer that indicates the starting number of hours. |
Negative |
A 1-bit value indicating the time’s sign. If bit is set to 1, the timecode record value is negative. |
Minutes |
A 7-bit integer that contains the starting number of minutes. |
Seconds |
An 8-bit unsigned integer indicating the starting number of seconds. |
Frames |
An 8-bit unsigned 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. |
Создание выборки носителей временного кода
Перечисление 3 демонстрирует шаги, требуемые создать выборку носителей временного кода. Метод создает единственную выборку носителей временного кода в течение времени SMPTE 01:30:15:07 (HH:MM:SS:FF), формат кадра отбрасывания на 30 футов в секунду, длящийся всю продолжительность видеотрека.
Перечисление 3 создает выборку носителей временного кода.
// this method creates a single SMPTE timecode media sample for time 01:30:15:07 (HH:MM:SS:FF) |
// 30fps, drop frame format lasting the entire duration of the video track |
- (CMSampleBufferRef)createTimecodeSampleBuffer |
{ |
CMSampleBufferRef sampleBuffer = NULL; |
CMBlockBufferRef dataBuffer = NULL; |
CMTimeCodeFormatDescriptionRef formatDescription = NULL; |
CVSMPTETime timecodeSample = {0}; |
OSStatus status = noErr; |
timecodeSample.hours = 1; // HH |
timecodeSample.minutes = 30; // MM |
timecodeSample.seconds = 15; // SS |
timecodeSample.frames = 7; // FF |
status = CMTimeCodeFormatDescriptionCreate(kCFAllocatorDefault, kCMTimeCodeFormatType_TimeCode32, CMTimeMake(100, 2997), 30, kCMTimeCodeFlag_DropFrame | kCMTimeCodeFlag_24HourMax, NULL, &formatDescription); |
if ((status != noErr) || !formatDescription) { |
NSLog(@"Could not create format description"); |
} |
// use utility function to convert CVSMPTETime time into frame number to write |
int32_t frameNumberData = frameNumber32ForTimecodeUsingFormatDescription(timecodeSample, formatDescription); |
status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, sizeof(int32_t), kCFAllocatorDefault, NULL, 0, sizeof(int32_t), kCMBlockBufferAssureMemoryNowFlag, &dataBuffer); |
if ((status != kCMBlockBufferNoErr) || !dataBuffer) { |
NSLog(@"Could not create block buffer"); |
} |
status = CMBlockBufferReplaceDataBytes(&frameNumberData, dataBuffer, 0, sizeof(int32_t)); |
if (status != kCMBlockBufferNoErr) { |
NSLog(@"Could not write into block buffer"); |
} |
CMSampleTimingInfo timingInfo; |
// duration of each timecode sample is from the current frame to the next frame specified along with a timecode |
// in this case the single sample will last the entire duration of the video content |
timingInfo.duration = [[sourceVideoTrack asset] duration]; |
timingInfo.decodeTimeStamp = kCMTimeInvalid; |
timingInfo.presentationTimeStamp = kCMTimeZero; |
size_t sizes = sizeof(int32_t); |
status = CMSampleBufferCreate(kCFAllocatorDefault, dataBuffer, true, NULL, NULL, formatDescription, 1, 1, &timingInfo, 1, &sizes, &sampleBuffer); |
if ((status != noErr) || !sampleBuffer) { |
NSLog(@"Could not create block buffer"); |
} |
CFRelease(formatDescription); |
CFRelease(dataBuffer); |
return sampleBuffer; |
} |
Добавление выборки носителей временного кода
Добавление выборки носителей временного кода сделано тем же способом как другие данные носителей. AVAssetWriterInput
-(BOOL)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer
метод используется для добавления выборок носителей, упакованных как объекты CMSampleBuffer. Перечисление 4 показывает, что некоторый стандартный код AVFoundation раньше добавлял демонстрационный буфер временного кода, создаваемый в перечислении 3.
Перечисление 4 , добавляющее демонстрационный буфер.
... |
if ([timecodeInput isReadyForMoreMediaData] && !completedOrFailed) { |
CMSampleBufferRef sampleBuffer = NULL; |
sampleBuffer = [timecodeSampleBufferGenerator createTimecodeSampleBuffer]; |
if (sampleBuffer != NULL) { |
BOOL success = [timecodeInput appendSampleBuffer:sampleBuffer]; |
CFRelease(sampleBuffer); |
sampleBuffer = NULL; |
completedOrFailed = !success; |
} else { |
completedOrFailed = YES; |
} |
} |
... |
AVAssetReader читая временной код
AVAssetReader
объект используется для получения данных носителей актива. Чтение выборки (ок) носителей временного кода, сохраненной в дорожке временного кода, выполняется таким же образом используемое для чтения любых других носителей, таких как аудио или видео использование носителей AVAssetReader
.
Как только Вы выделяете AVAssetReader
объект с активом Вы хотите читать из, создать AVAssetReaderTrackOutput
поскольку дорожка временного кода тогда вызывает -(BOOL)startReading
подготавливать читателя к чтению выборки буферизует от актива. Тогда отправьте -(CMSampleBufferRef)copyNextSampleBuffer
метод к выводу дорожки возражает для получения выборки временного кода.
Возвращенный CMSampleBufferRef
содержание выборки временного кода может быть интерпретировано как требуется приложением, например возвращенное число кадра может быть преобразовано в a CVSMPTETime
представление. Посмотрите раздел Timecode Utility Functions этого документа для служебных функций, позволяющих Вам выполнять преобразования для kCMTimeCodeFormatType_TimeCode32
демонстрационный тип формата временного кода. Получать описание формата, описывающее сведения о форматах демонстрационного вызова временного кода CMSampleBufferGetFormatDescription
.
Перечисление 5 демонстрирует, как создать AVAssetReader
объект и a AVAssetReaderTrackOutput
объект для дорожки носителей временного кода.
Перечисление 5 , создающее выходной объект читателя и читателя.
... |
// 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); |
} |
} |
... |
Перечисление 6 представляет две методики. Первое демонстрирует, как считать демонстрационные буферы из AVAssetReaderTrackOutput
и второе демонстрирует, как получить демонстрационные данные и интерпретировать их. В этом случае вызов одной из утилиты временного кода функционирует от раздела Timecode Utility Functions этого документа для преобразования демонстрационных данных в a CVSMPTETime
тогда просто распечатывание значений.
Перечисление 6 Считало Демонстрационный Буфер Временного кода и распечатывает 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)]); |
} |
} |
} |
} |
Функции утилиты временного кода
Перечисление 7 и 8 обеспечивает служебные функции, демонстрирующие, как преобразовать из a CVSMPTETime
к числу кадра и от числа кадра до a CVSMPTETime
для kCMTimeCodeFormatType_TimeCode32
демонстрационный формат носителей временного кода.
enum { |
tcNegativeFlag = 0x80 /* negative bit is in minutes */ |
}; |
Перечисление 7 CVSMPTETime
структурировать число (kCMTimeCodeFormatType_TimeCode32
Выборка носителей)
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); |
} |
Число кадра перечисления 8 (kCMTimeCodeFormatType_TimeCode32
Выборка носителей) к 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; |
} |
Проверка дорожки временного кода
QuickTime Player 7 может использоваться в качестве быстрого способа визуально проверить что Ваш kCMTimeCodeFormatType_TimeCode32
дорожка временного кода была записана правильно.
При принятии единственной выборки носителей временного кода, представляющей 01:30:15:07, (HH:MM:SS:FF) был записан для соответствия 0:00:00 времени фильма для видеоконтента на приблизительно 29,97 кадр/с с помощью информации, предоставленной в перечислении 3, результаты появятся как показано на рисунке 1.
Можно также переключить дисплей временной шкалы, как желаемый как показано на рисунке 2. Дисплей говорит Кадр отбрасывания на рисунке 1, потому что описание формата, которое мы создали для представления типа выборки, которую мы записали в перечислении 3, содержавшем kCMTimeCodeFlag_DropFrame
флаг и поэтому выборка носителей временного кода интерпретируются как 29,97 кадров отбрасывания.
О типе формата TimeCode64
kCMTimeCodeFormatType_TimeCode64
('tc64'
) когда создание AVFoundation базировало мультимедийное приложение, в то время как использование, формат рекомендуется kCMTimeCodeFormatType_TimeCode32
формат, как обсуждено в этом документе нужно рассмотреть как решение для приложений, имеющих определенные требования функциональной совместимости с базируемыми мультимедийными приложениями более старого устаревшего QuickTime, которые могут только поддерживать 'tmcd'
демонстрационный формат временного кода.
A kCMTimeCodeFormatType_TimeCode64
выборка носителей формата сохранена как a Big-Endian
SInt64
.
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. |
Посмотрите пример кода Читателя/Писателя Временного кода для получения дополнительной информации и служебные функции преобразования.
Ссылки
Руководство по программированию AVFoundation
Спецификация формата файла QuickTime
AVFoundation - Читатель/Писатель временного кода (avtimecodereadwrite)
История версии документа
Дата | Примечания |
---|---|
20.01.2014 | Удаленный неправильный свободный () rawData указателя в - (недействительный) outputTimecodeDescriptionForSampleBuffer: (CMSampleBufferRef) sampleBuffer. |
Удаленный неправильный свободный () rawData указателя в - (недействительный) outputTimecodeDescriptionForSampleBuffer: (CMSampleBufferRef) sampleBuffer. | |
18.06.2013 | Передовая статья |
03.06.2013 | Передовая статья |
16.05.2013 | Новый документ, который обсуждает это техническое примечание, как считать и записать выборку носителей kCMTimeCodeFormatType_TimeCode32 'tmcd' с AVAssetReader и AVAssetWriter. |