2015-10-15 3 views
2

Мне удалось декодировать и воспроизводить видео H264, однако у меня тяжелое время с видео в формате MPEG4.CMVideoFormatОпределенные расширения для потоков MPEG4

Какие расширения CMVideoFormatDescription необходимы? При попытке создать VTDecompressionSession я получаю ошибку -8971 (codecExtensionNotFoundErr).

Это, как я создаю VideoFormatDescription

OSStatus success = CMVideoFormatDescriptionCreate(kCFAllocatorDefault, 
                self.mediaCodec, 
                message.frameSize.width, 
                message.frameSize.height, 
                NULL, 
                &mediaDescriptor); 

Вместо этого NULL, я полагаю, мне нужно указать CFDictionaryRef, однако я не знаю, что она должна содержать. Есть идеи?

ответ

2

После сильной боли и агонии, я, наконец, смог заставить ее работать.

Мне нужно предоставить CFDictionaryRef, по крайней мере, для ключа kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms. Значение для этого ключа также должно быть CFDictionaryRef. Для типов H264 это создается внутри CMVideoFormatDescriptionCreateFromH264ParameterSets и выглядит следующим образом:

avcC = <014d401e ffe10016 674d401e 9a660a0f ff350101 01400000 fa000013 88010100 0468ee3c 80> 

Однако для типа MPEG4, вам нужно создать это самостоятельно. Конечный результат должен выглядеть следующим образом:

esds = <00000000 038081e6 00000003 8081e611 00000000 00000000 058081e5 060102> 

Теперь путь, чтобы создать это все еще нечеткое мне, однако это как-то работает. Я был вдохновлен this link. Это код:

- (CMFormatDescriptionRef)createFormatDescriptorFromMPEG4Message:(MessageContainer *)message { 
    CMVideoFormatDescriptionRef mediaDescriptor = NULL; 
    NSData *esdsData = [self newESDSFromData:message.frameData]; 

    CFMutableDictionaryRef esdsDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, 
                     &kCFTypeDictionaryKeyCallBacks, 
                     &kCFTypeDictionaryValueCallBacks); 
    CFDictionarySetValue(esdsDictionary, CFSTR("esds"), (__bridge const void *)(esdsData)); 

    NSDictionary *dictionary = @{(__bridge NSString *)kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms : (__bridge NSDictionary *)esdsDictionary}; 

    OSStatus status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault, 
                self.mediaCodec, 
                message.frameSize.width, 
                message.frameSize.height, 
                (__bridge CFDictionaryRef)dictionary, 
                &mediaDescriptor); 
    if (status) { 
     NSLog(@"CMVideoFormatDesciprionCreate failed with %zd", status); 
    } 

    return mediaDescriptor; 
} 


- (NSData *)newESDSFromData:(NSData *)data { 
    NSInteger dataLength = data.length; 

    int full_size = 3 + 5 + 13 + 5 + dataLength + 3; 

    // ES_DescrTag data + DecoderConfigDescrTag + data + DecSpecificInfoTag + size + SLConfigDescriptor 
    int config_size = 13 + 5 + dataLength; 
    int padding = 12; 

    int8_t *esdsInfo = calloc(full_size + padding, sizeof(int8_t)); 

    //Version 
    esdsInfo[0] = 0; 

    //Flags 
    esdsInfo[1] = 0; 
    esdsInfo[2] = 0; 
    esdsInfo[3] = 0; 

    //ES_DescrTag 
    esdsInfo[4] |= 0x03; 
    [self addMPEG4DescriptionLength:full_size 
          toPointer:esdsInfo + 5]; 

    //esid 
    esdsInfo[8] = 0; 
    esdsInfo[9] = 0; 

    //Stream priority 
    esdsInfo[10] = 0; 

    //DecoderConfigDescrTag 
    esdsInfo[11] = 0x03; 

    [self addMPEG4DescriptionLength:config_size 
          toPointer:esdsInfo + 12]; 

    //Stream Type 
    esdsInfo[15] = 0x11; 

    //Buffer Size 
    esdsInfo[16] = 0; 
    esdsInfo[17] = 0; 

    //Max bitrate 
    esdsInfo[18] = 0; 
    esdsInfo[19] = 0; 
    esdsInfo[20] = 0; 

    //Avg bitrate 
    esdsInfo[21] = 0; 
    esdsInfo[22] = 0; 
    esdsInfo[23] = 0; 

    //< DecSpecificInfoTag 
    esdsInfo[24] |= 0x05; 

    [self addMPEG4DescriptionLength:dataLength 
          toPointer:esdsInfo + 25]; 

    //SLConfigDescrTag 
    esdsInfo[28] = 0x06; 

    //Length 
    esdsInfo[29] = 0x01; 

    esdsInfo[30] = 0x02; 

    NSData *esdsData = [NSData dataWithBytes:esdsInfo length:31 * sizeof(int8_t)]; 

    free(esdsInfo); 
    return esdsData; 
} 

- (void)addMPEG4DescriptionLength:(NSInteger)length 
         toPointer:(int8_t *)ptr { 
    for (int i = 3; i >= 0; i--) { 
     uint8_t b = (length >> (i * 7)) & 0x7F; 
     if (i != 0) { 
      b |= 0x80; 
     } 

     ptr[3 - i] = b; 
    } 
} 

Контейнер сообщений представляет собой простая обертка данных, полученные от сервера:

@interface MessageContainer : NSObject 

@property (nonatomic) CGSize frameSize; 
@property (nonatomic) NSData *frameData; 

@end 

Где frameSize является размером кадра (полученным отдельно от сервера) и frameData - это сами данные.

+0

О, это помогает мне декодировать mpeg4 на ios с быстрым, но только первым кадром! После успешного декодирования первого кадра я всегда получаю ошибку -12911 kVTVideoDecoderMalfunctionErr. Вы знаете, что может быть неправильно для меня? – JULIIncognito

+0

Я рад, что это помогло. К сожалению, я давно уже прошел этот проект, поэтому я не могу вам помочь. Вы можете попробовать создать 'CFDictionaryRef' для каждого фрейма, посмотрите, работает ли это. Затем отработайте свой путь назад, что может быть неправильным. ПРИМЕЧАНИЕ. Мне нужно было сделать это только с первым фреймом (который был ключевым фреймом). –

+0

Да, это главное :) Я сделал это для каждого кадра, моего плохого. Теперь он работает отлично! Спасибо – JULIIncognito

Смежные вопросы