После сильной боли и агонии, я, наконец, смог заставить ее работать.
Мне нужно предоставить 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
- это сами данные.
О, это помогает мне декодировать mpeg4 на ios с быстрым, но только первым кадром! После успешного декодирования первого кадра я всегда получаю ошибку -12911 kVTVideoDecoderMalfunctionErr. Вы знаете, что может быть неправильно для меня? – JULIIncognito
Я рад, что это помогло. К сожалению, я давно уже прошел этот проект, поэтому я не могу вам помочь. Вы можете попробовать создать 'CFDictionaryRef' для каждого фрейма, посмотрите, работает ли это. Затем отработайте свой путь назад, что может быть неправильным. ПРИМЕЧАНИЕ. Мне нужно было сделать это только с первым фреймом (который был ключевым фреймом). –
Да, это главное :) Я сделал это для каждого кадра, моего плохого. Теперь он работает отлично! Спасибо – JULIIncognito