【问题标题】:CMVideoFormatDescription extensions for MPEG4 streams用于 MPEG4 流的 CMVideoFormatDescription 扩展
【发布时间】:2016-01-13 18:29:53
【问题描述】:

我已经设法解码和播放 H264 视频,但是我在播放 MPEG4 视频时遇到了困难。

它需要哪些 CMVideoFormatDescription 扩展?我在尝试创建 VTDecompressionSession 时收到 -8971 错误(codecExtensionNotFoundErr)。

这就是我创建 VideoFormatDescription 的方式

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

我假设我需要指定一个 CFDictionaryRef,而不是那个 NULL,但是我不知道它应该包含什么。有什么想法吗?

【问题讨论】:

    标签: ios video-streaming mpeg-4


    【解决方案1】:

    历经千辛万苦,我终于成功了。

    我需要为 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 是数据本身。

    【讨论】:

    • 哦,它对我在 ios 上使用 swift 解码 mpeg4 很有帮助,但只是第一帧!成功解码第一帧后,我总是收到错误 -12911 kVTVideoDecoderMalfunctionErr。你知道我有什么问题吗?
    • 我很高兴它有帮助。可悲的是,我早就通过了这个项目,所以我真的帮不了你。您可以尝试为每个帧创建CFDictionaryRef,看看是否可行。然后从那开始,努力解决可能出现的问题。注意:我只需要对第一帧(这是一个关键帧)执行此操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 2017-02-24
    • 1970-01-01
    • 2016-11-13
    • 2017-01-12
    • 1970-01-01
    相关资源
    最近更新 更多