Техническое примечание TN2310

AVFoundation - Временной код поддержка с AVAssetWriter и AVAssetReader

AVFoundation включает поддержку дорожек временного кода. Дорожки временного кода позволяют Вам хранить внешнюю информацию временного кода, такую как временной код SMPTE в файлах фильма в формате QuickTime (.mov). Это техническое примечание обсуждает, как записать и считать 32-разрядный временной код ('tmcd') выборка носителей с AVAssetWriter и AVAssetReader.

Предпосылки
О временных кодах
AVAssetWriter пишущий временной код
AVAssetReader читая временной код
Функции утилиты временного кода
Проверка дорожки временного кода
О типе формата TimeCode64
Ссылки
История версии документа

Предпосылки

Это техническое примечание предполагает, что читатель знаком с AVFoundation и более в частности с AVAssetWriter и AVAssetReader классы. Некоторый фон с Демонстрационным Буфером CoreMedia APIs также принят. Посмотрите раздел References этого документа для получения дополнительной информации.

О временных кодах

Файлы фильма в формате QuickTime (.mov) допускают хранение информации синхронизации, полученной из материала первоисточника фильма, такого как продолжительность кадра, влияющая, как носители интерпретируются и играются.

Файлы фильма в формате QuickTime также позволяют Вам хранить дополнительную информацию синхронизации, которая не будет в частности влиять на воспроизведение носителей. Эта дополнительная информация синхронизации обычно получалась бы из материала первоисточника; например, как временной код SMPTE. В сущности можно думать о временном коде как об обеспечении ссылки между носителями определенная информация синхронизации и информация синхронизации от материала первоисточника.

Информация временного кода файла фильма в формате QuickTime хранится в дорожке временного кода. Дорожки временного кода содержат описание формата, состоящее из следующего:

Информация, хранящаяся в дорожке, находится способом, который независим от любого определенного стандарта временного кода. Формат этой информации достаточно гибок для размещения всех известных стандартов временного кода, включая временной код 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). Это расширение является a CFDictionary содержа следующие два ключа; kCMTimeCodeFormatDescriptionKey_Value a CFString и kCMTimeCodeFormatDescriptionKey_LangCode a CFNumber. Ключ описания мог бы содержать имя видеозаписи, из которой создавался фильм.

Создание описания формата временного кода

Лучший способ понять, как отформатировать и интерпретировать описание формата временного кода, состоит в том, чтобы рассмотреть пример. При создании фильма из источника видеосигнала 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 кадров отбрасывания.

  Время временного кода рисунка 1.
  Время фильма стандарта рисунка 2.

О типе формата 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

Ссылка AVAssetReader API

Ссылка AVAssetWriter API

Ссылка CMSampleBuffer

Спецификация формата файла QuickTime

Стандарты SMPTE

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.