Декодирование AAC с помощью MediaCodec API на Android

Я пытаюсь использовать API MediaCodec на Android для декодирования потока AAC. (Это необработанный AAC.) Я попытался использовать MediaFormat.createAudioFormat() для создания объекта формата для передачи в MediaCodec.configure(), но при использовании AAC (аудио/mp4a-latm) я продолжал получать ошибки. (Хотя он работает с MP3 (аудио/mpeg)...)

Наконец, я создал MediaExtractor для файла AAC и посмотрел на создаваемый им объект формата. Я видел, что он включает ключ «csd-0» для ByteBuffer, состоящего из двух байтов, оба со значением 0x12. Если я включу этот ключ и значение в объект формата, который я использовал для настройки кодека AAC, все будет работать.

Кто-нибудь знает, что происходит? В документации указано, что я не должен настраивать этот ключ. У кого-нибудь есть указатель на примеры MediaCodec для декодирования файлов AAC без использования MediaExtractor для создания объекта формата?


person Benjamin Reed    schedule 17.10.2012    source источник
comment
Дальнейшее расследование привело меня к мысли, что csd-0 содержит ESDS (элементарный дескриптор потока). Глядя на MakeAACCodecSpecificData в avc_utils.cpp в исходниках Android, я вижу, что первые 5 бит — это тип объекта (2), следующие 4 — индекс частоты (4 = 48000), следующие 4 — конфигурация канала (2). Эта вики-страница указывает, что оставшиеся биты предназначены для длины кадра, зависит от ядра декодер и флаг расширения. Я использую MediaFormat.createAudioFormat(), который должен настроить важные элементы ESDS.   -  person Benjamin Reed    schedule 19.10.2012


Ответы (3)


Да, 2 байта конфигурации кодека являются начальными, которые вы получаете. И да, это необработанные блоки данных aac. Вы можете увидеть, как я получаю формат ниже при кодировании. Сначала я пытался следовать документации, в которой говорилось, что они в формате latm, и пытался это проанализировать. Затем я нашел некоторые «diff» в документации по Android, в которых говорилось, что вывод действительно был необработанными блоками. Зная это тогда, это был просто вопрос выбора контейнера для моих нужд. В частности, мне нужен был контейнер adts, а не flv или mp4.

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

profile = (configParams[0]>>3)&0x1f;

frequency_index = (this.configParams[0]&0x7) <<1 | (this.configParams[1]>>7) &0x1;

channel_config = (this.configParams[1]>>3) &0xf;

int finallength = encoded_length + 7;       
ENCodedByteArray[0] = (byte) 0xff;
ENCodedByteArray[1] = (byte) 0xf1;
ENCodedByteArray[2] = (byte) ( ((profile - 1) << 6) + (frequency_index << 2) +(channel_config >> 2));
ENCodedByteArray[3] = (byte) (((channel_config & 0x3) << 6) + (finallength >> 11));
ENCodedByteArray[4] = (byte)( (finallength & 0x7ff) >> 3);
ENCodedByteArray[5] = (byte) (((finallength & 7) << 5) + 0x1f) ;
ENCodedByteArray[6] = (byte) 0xfc;

Используя что-то вроде этого:

byte chunkADTS[]=new byte[info.size + 7];
fillInADTSHeader(chunkADTS,info.size);
outputBuffers[bR].get(chunkADTS,7,info.size);
buffer.pushData(chunkADTS);
person Andy--S    schedule 28.06.2013
comment
У меня есть дополнительный вопрос: какое значение configParams указано выше? Это значение config=xxxx из строки a=fmtp:nn в сообщении SDP (для потоков RTSP)? - person David Schwartz; 09.06.2014
comment
Параметры конфигурации представляют собой конкретную конфигурацию звука, и обычно первые два байта выводятся медиакодеком latm. Объект BufferINfo должен иметь установленный флаг Codec_info. - person Andy--S; 12.09.2014

Я использовал следующий код и добавил ES, который удалил заголовок ADTS, он мог бы работать хорошо, но я действительно не знаю, почему следует установить «csd-0», иначе кодек вызовет ошибку

      decoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
      mMediaFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 44100,2);
      byte[] bytes = new byte[]{(byte) 0x12, (byte)0x12};
      ByteBuffer bb = ByteBuffer.wrap(bytes);
      mMediaFormat.setByteBuffer("csd-0", bb);
person huntyy    schedule 12.03.2014

У меня не было проблем с декодированием/воспроизведением контента AAC на нескольких устройствах, которые я тестировал. Мой подход заключался в том, чтобы сначала использовать MediaExtractor для установки источника данных, затем инициализировать MediaFormat и, наконец, выполнять работу в цикле с буферами, отправляемыми в/из MediaCodec. Для поверхности я использовал null, так как это всего лишь аудиоплеер, поэтому отображать нечего.

person radhoo    schedule 01.06.2014