2015-10-15 38 views
2

我已經設法解碼和播放H264視頻,但是我對MPEG4視頻有困難。CMVideoFormatDescription擴展MPEG4流

它需要什麼CMVideoFormatDescription擴展?嘗試創建VTDecompressionSession時,出現-8971錯誤(codecExtensionNotFoundErr)。

這是我如何創建一個VideoFormatDescription

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

取而代之的是NULL,我想我需要指定一個CFDictionaryRef,但我不知道它應該包含的內容。任何想法?

回答

2

在經歷了很多痛苦和痛苦之後,我終於設法使它工作。

我需要提供一個至少爲kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms鍵值的CFDictionaryRef。這個鍵的值也必須是一個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

哦,它有助於我用快速解碼ios上的mpeg4,但只有第一幀!成功解碼第一幀後,我總是收到錯誤-12911 kVTVideoDecoderMalfunctionErr。你知道我有什麼可能是錯的嗎? – JULIIncognito

+0

我很高興它有幫助。可悲的是,自從這個項目通過以來,我一直在努力,所以我不能真正幫助你。你可以嘗試爲每一幀創建'CFDictionaryRef',看看是否有效。然後,從那裏開始,找出可能出錯的地方。注意:我只需要在第一幀(這是一個關鍵幀)中執行此操作。 –

+0

是的,這是要點:)我已經做到了每一幀,我的壞。現在它工作完美!謝謝 – JULIIncognito