2013-11-07 5 views
12

Я пытаюсь кодировать аудио aac с помощью Android AudioRecord и MediaCodec. Я создал класс энкодера, очень похожий на (Encoding H.264 from camera with Android MediaCodec). С помощью этого класса я создал экземпляр AudioRecord и скажу ему, чтобы он считывал свои байтовые [] данные в AudioEncoder (audioEncoder.offerEncoder (Data)).Кодирование аудио AAC с использованием AudioRecord и MediaCodec на Android

while(isRecording) 
{ 
    audioRecord.read(Data, 0, Data.length); 
    audioEncoder.offerEncoder(Data); 
} 

Вот мои настройки для моего AudioRecord

int audioSource = MediaRecorder.AudioSource.MIC; 
    int sampleRateInHz = 44100; 
    int channelConfig = AudioFormat.CHANNEL_IN_MONO; 
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT; 
    int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); 

Я успешно собрал несколько байт [] массив данных и написал его в локальный файл. К сожалению, файл не воспроизводится. Я сделал еще несколько поисков в Интернете и нашел связанный пост (How to generate the AAC ADTS elementary stream with Android MediaCodec). Таким образом, другие, у кого есть аналогичная проблема, говорят, что главная проблема заключается в том, что «Кодер MediaCodec генерирует необработанный поток AAC. Необработанный поток AAC необходимо преобразовать в воспроизводимый формат, такой как поток ADTS». Поэтому я попытался добавить заголовок ADTS. Тем не менее, после добавления заголовка ADTS (я прокомментировал код ниже), мой AudioEncoder даже не записывал выходной аудиофайл. Есть ли что-нибудь, что мне не хватает? Правильно ли настроена моя установка?

Любые предложения, комментарии и мнения приветствуются и очень ценятся. Спасибо, парни!

import android.media.MediaCodec; 
import android.media.MediaCodecInfo; 
import android.media.MediaFormat; 
import android.os.Environment; 
import android.util.Log; 

import java.io.BufferedOutputStream; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.nio.ByteBuffer; 

public class AudioEncoder { 

    private MediaCodec mediaCodec; 
    private BufferedOutputStream outputStream; 
    private String mediaType = "audio/mp4a-latm"; 

    public AudioEncoder() { 
     File f = new File(Environment.getExternalStorageDirectory(), "Download/audio_encoded.aac"); 
     touch(f); 
     try { 
      outputStream = new BufferedOutputStream(new FileOutputStream(f)); 
      Log.e("AudioEncoder", "outputStream initialized"); 
     } catch (Exception e){ 
      e.printStackTrace(); 
     } 

     mediaCodec = MediaCodec.createEncoderByType(mediaType); 
     final int kSampleRates[] = { 8000, 11025, 22050, 44100, 48000 }; 
     final int kBitRates[] = { 64000, 128000 }; 
     MediaFormat mediaFormat = MediaFormat.createAudioFormat(mediaType,kSampleRates[3],1); 
     mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 

     mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[1]); 
     mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
     mediaCodec.start(); 
    } 

    public void close() { 
     try { 
      mediaCodec.stop(); 
      mediaCodec.release(); 
      outputStream.flush(); 
      outputStream.close(); 
     } catch (Exception e){ 
      e.printStackTrace(); 
     } 
    } 

    // called AudioRecord's read 
    public synchronized void offerEncoder(byte[] input) { 
     Log.e("AudioEncoder", input.length + " is coming"); 

     try { 
      ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 
      ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); 
      int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); 
      if (inputBufferIndex >= 0) { 
       ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
       inputBuffer.clear(); 

       inputBuffer.put(input); 


       mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0); 
      } 

      MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 
      int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0); 

////trying to add a ADTS 
//   while (outputBufferIndex >= 0) { 
//    int outBitsSize = bufferInfo.size; 
//    int outPacketSize = outBitsSize + 7; // 7 is ADTS size 
//    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; 
// 
//    outputBuffer.position(bufferInfo.offset); 
//    outputBuffer.limit(bufferInfo.offset + outBitsSize); 
// 
//    byte[] outData = new byte[outPacketSize]; 
//    addADTStoPacket(outData, outPacketSize); 
// 
//    outputBuffer.get(outData, 7, outBitsSize); 
//    outputBuffer.position(bufferInfo.offset); 
// 
////    byte[] outData = new byte[bufferInfo.size]; 
//    outputStream.write(outData, 0, outData.length); 
//    Log.e("AudioEncoder", outData.length + " bytes written"); 
// 
//    mediaCodec.releaseOutputBuffer(outputBufferIndex, false); 
//    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); 
// 
//   } 


//Without ADTS header 
      while (outputBufferIndex >= 0) { 
       ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; 
       byte[] outData = new byte[bufferInfo.size]; 
       outputBuffer.get(outData); 
       outputStream.write(outData, 0, outData.length); 
       Log.e("AudioEncoder", outData.length + " bytes written"); 

       mediaCodec.releaseOutputBuffer(outputBufferIndex, false); 
       outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); 

      } 
     } catch (Throwable t) { 
      t.printStackTrace(); 
     } 

    } 

    /** 
    * Add ADTS header at the beginning of each and every AAC packet. 
    * This is needed as MediaCodec encoder generates a packet of raw 
    * AAC data. 
    * 
    * Note the packetLen must count in the ADTS header itself. 
    **/ 
    private void addADTStoPacket(byte[] packet, int packetLen) { 
     int profile = 2; //AAC LC 
     //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD; 
     int freqIdx = 4; //44.1KHz 
     int chanCfg = 2; //CPE 

     // fill in ADTS data 
     packet[0] = (byte)0xFF; 
     packet[1] = (byte)0xF9; 
     packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2)); 
     packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11)); 
     packet[4] = (byte)((packetLen&0x7FF) >> 3); 
     packet[5] = (byte)(((packetLen&7)<<5) + 0x1F); 
     packet[6] = (byte)0xFC; 
    } 

    public void touch(File f) 
    { 
     try { 
      if(!f.exists()) 
       f.createNewFile(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
+0

Наконец, я смог воспроизвести аудио, используя технику ADTS. к сожалению, нет абсолютно никаких звуков ... – xiaowoo

+0

Вы когда-нибудь это выясняли? По-видимому, нет рабочих примеров кодирования аудио с MediaCodec, если API> = 18. –

ответ

7

Вы можете использовать андроид MediaMuxer упаковать исходные потоки, созданные MediaCodec в файл .mp4. Бонус: пакеты AAC, содержащиеся в .mp4, не требуют заголовка ADTS.

У меня есть working example of this technique on Github.

+2

спасибо за ваш ответ, однако я нацелился на api 16 (android 4.1). MediaMuxer доступен только для android v 4.3 или api 18. – xiaowoo

4

Проверьте метод «testEncoder» here на предмет правильного использования MediaCodec в качестве кодировщика.

после этого в коде,

ваш вход (аудио-рекордер) сконфигурирован для одного звукового канала, в то время как ваш выход (ADTS заголовок пакета) устанавливается для двух каналов (chanCfg = 2).

Также, если вы измените свой выбор источника (в настоящее время 44.1khz), вам также необходимо изменить флаг freqIdx в заголовке пакета ADTS. проверьте это значение link.

И флаг профиля заголовка ADTS установлен на «AAC LC», вы также можете найти его под MediaCodecInfo.CodecProfileLevel. у вас установлен профиль = 2, то есть MediaCodecInfo.CodecProfileLevel.AACObjectLC

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