Кодировщик AAC Android MediaCodec

Я использую класс MediaCodec, предоставляемый Android SDK, начиная с уровня API 16, с кодировщиком OMX.SEC.aac.enc для кодирования звука в файл. Я получаю аудиовход от класса AudioRecord. Мой экземпляр класса AudioRecord настроен следующим образом:

bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_DEFAULT, bufferSize);

Я могу воспроизвести необработанные данные из экземпляра AudioRecord, поэтому проблема не связана с этим.

Я записываю выходные данные экземпляра AudioRecord в экземпляр ByteBuffer и передаю его доступному входному буферу из кодировщика. Выходные данные кодировщика записываются в файл на SD-карте.

Это параметры конфигурации для моего экземпляра MediaCodec:

codec = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

VLC сообщает мне, что в моем файле aac нет потоков. Команда FFMPEG -i @filename@ выдает следующую ошибку: При обработке ввода обнаружены недопустимые данные. Ни один из протестированных мной медиаплееров не может воспроизводить мой файл.

Почему я не могу воспроизвести свой файл? Я не получаю OpenMAX ошибок в LogCat и приложение не вылетает при кодировании. Я написал видеокодер, который работает по тому же принципу, и он работает.

Это код для чтения данных из экземпляра AudioRecord в буфер:

    new Thread() {
        public void run() {
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize);
            int read = 0;
            while (isRecording) {
                read = recorder.read(byteBuffer, bufferSize);
                if(AudioRecord.ERROR_INVALID_OPERATION != read){
                    encoder.add(byteBuffer);
                }
            }
            recorder.stop();
        }
    }.start();

Функция add из моего кодировщика копирует содержимое одного буфера в другой:

public void add(ByteBuffer input) {
    if (!isRunning)
        return; 

    if (tmpInputBuffer == null)
        tmpInputBuffer = ByteBuffer.allocate(input.capacity());

    if (!tmpBufferClear)
        Log.e("audio encoder", "deadline missed"); //TODO lower bit rate

    synchronized (tmpInputBuffer) {
        tmpInputBuffer.clear();
        tmpInputBuffer.put(input);
        tmpInputBuffer.notifyAll();
        Log.d("audio encoder", "pushed data into tmpInputBuffer");
    }
}

Следующий код используется для заполнения входного буфера кодировщика:

new Thread() {
    public void run() {
        while (isRunning) {
            if (tmpInputBuffer == null)
                continue;
            synchronized (tmpInputBuffer) {
                if (tmpBufferClear) {
                    try {
                        Log.d("audio encoder", "falling asleep");
                        tmpInputBuffer.wait(); //wait when no input is available
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                ByteBuffer[] inputBuffers = codec.getInputBuffers();
                int inputBufferIndex;
                do
                    inputBufferIndex = codec.dequeueInputBuffer(-1);
                while (inputBufferIndex < 0);
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                Log.d("input buffer size", String.valueOf(inputBuffer.capacity()));
                Log.d("tmp input buffer size", String.valueOf(tmpInputBuffer.capacity()));
                inputBuffer.put(tmpInputBuffer.array());
                tmpInputBuffer.clear();
                codec.queueInputBuffer(inputBufferIndex, 0, tmpInputBuffer.capacity(), 0, 0);
                tmpBufferClear = true;
                Log.d("audio encoder", "added to input buffer");
            }
        }
    }
}.start();

Я записываю вывод кодировщика в локальный файл следующим образом:

    new Thread() {
        public void run() {
            while (isRunning) {
                ByteBuffer[] outputBuffers = codec.getOutputBuffers();
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, -1);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                    byte[] outData = new byte[bufferInfo.size];
                    outputBuffer.get(outData);

                    try {
                        fileWriter.write(outData, 0, outData.length);
                    } 
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                    codec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
                    Log.d("audio encoder", "removed from output buffer");
                }
            }
            codec.stop();

            try {
                fileWriter.close();
            } 
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }.start();
                tmpBufferClear = true;
                Log.d("audio encoder", "added to input buffer");
            }
        }
    }
}.start();

person Community    schedule 07.03.2013    source источник
comment
опубликуйте полный метод, где вы это делаете. с start () stop () .. спасибо.   -  person Sergey Benner    schedule 07.03.2013
comment
Также обратите внимание на эту ветку - code.google.com / p / spydroid-ipcamera / issues / detail? id = 43 у них, похоже, есть рабочий фрагмент кода для записи AAC.   -  person Sergey Benner    schedule 07.03.2013
comment
@Sergey Benner, они не используют класс MediaCodec. Класс MediaRecorder используется в Spydroid. Этот класс будет моим вторым выбором, если класс MediaCodec не работает.   -  person    schedule 08.03.2013
comment
могли бы вы когда-нибудь решить эту проблему? Я столкнулся с аналогичной проблемой при кодировании существующего файла wav. Процесс кодирования завершился без ошибок, созданный файл имеет приемлемый размер, но ни один плеер не может его воспроизвести.   -  person muetzenflo    schedule 24.06.2016
comment
Я тоже столкнулся с этой проблемой, файл aac KEY_BIT_RATE не 64kps, а 128kps. Может это причина того, что файл aac не воспроизводится. Я пока не могу это исправить.   -  person I'm SuperMan    schedule 05.07.2016
comment
Я знаю, что это слишком поздно, но в случае, если в будущем кто-то еще столкнется с этой проблемой, медиа-кодек вернет данные без заголовка и подписи файла, когда вы сохраняете возвращенные данные с помощью медиа-кодека в файл, вам все равно нужно записать данные заголовка файла, и это зависит от расширения файла, которое вы хотите использовать, так как @ Mimmo-Grottoli упомянул, используя Mediamauxer, напишите его для вас, но если у вас есть некоторые ограничения на использование Mediamauxer, вы должны сами написать файл заголовка, и это будет очень сложно, зависит от выбранного носителя формат.   -  person Saman    schedule 06.12.2017


Ответы (1)


Думаю, вы пропустили класс MediaMuxer. Он вам нужен, если вы хотите записать, например, что-то полученное из MediaCodec в файл.

person Mimmo Grottoli    schedule 23.06.2015
comment
OP запрашивал API 16, MediaMuxer был представлен с API 18 - person muetzenflo; 24.06.2016