Декодирование аудиофайла AAC в файл RAW PCM

У меня есть аудиофайл в формате AAC, который я пытаюсь преобразовать в файл PCM в необработанном формате, чтобы смешать его с другим аудиофайлом и воспроизвести позже с помощью AudioTrack.

После некоторых исследований я наткнулся на эту библиотеку, которая правильно декодирует мой файл AAC. Однако он только передает декодированные байты непосредственно в AudioTrack. При попытке записать декодированные байты в выходной поток полученный файл содержит только шум.

это код, который я использую для декодирования файла AAC -

public void AACDecoderAndPlay() {
    ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
    ByteBuffer[] outputBuffers = mDecoder.getOutputBuffers();

    BufferInfo info = new BufferInfo();

    // create an audiotrack object
    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, JamboxAudioTrack.FREQUENCY,
            JamboxAudioTrack.CHANNEL_CONFIGURATION, JamboxAudioTrack.AUDIO_ENCODING,
            JamboxAudioTrack.BUFFER_SIZE, AudioTrack.MODE_STREAM);

    audioTrack.play();


    long bytesWritten = 0;
    while (!eosReceived) {
        int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US);
        if (inIndex >= 0) {
            ByteBuffer buffer = inputBuffers[inIndex];
            int sampleSize = mExtractor.readSampleData(buffer, 0);
            if (sampleSize < 0) {
                // We shouldn't stop the playback at this point, just pass the EOS
                // flag to mDecoder, we will get it again from the
                // dequeueOutputBuffer
                Log.d(LOG_TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);

            } else {
                mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
                mExtractor.advance();
            }

            int outIndex = mDecoder.dequeueOutputBuffer(info, TIMEOUT_US);
            switch (outIndex) {
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    Log.d(LOG_TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
                    outputBuffers = mDecoder.getOutputBuffers();
                    break;

                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    MediaFormat format = mDecoder.getOutputFormat();
                        Log.d(LOG_TAG, "New format " + format);
//                        audioTrack.setPlaybackRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));

                        break;

                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    Log.d(LOG_TAG, "dequeueOutputBuffer timed out!");
                    break;

                default:
                    ByteBuffer outBuffer = outputBuffers[outIndex];
                    Log.v(LOG_TAG, "We can't use this buffer but render it due to the API limit, " + outBuffer);

                    final byte[] chunk = new byte[info.size];
                    outBuffer.get(chunk); // Read the buffer all at once
                    outBuffer.clear(); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN

                    audioTrack.write(chunk, info.offset, info.offset + info.size); // AudioTrack write data

                    if (info.offset > 0) {
                        Log.v(LOG_TAG, "" + info.offset);
                    }
                    try {
                        mOutputStream.write(chunk, info.offset, info.offset + info.size);
                        bytesWritten += info.offset + info.size;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mDecoder.releaseOutputBuffer(outIndex, false);
                    break;
            }

            // All decoded frames have been rendered, we can stop playing now
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                Log.d(LOG_TAG, "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                break;
            }
        }
    }
    Log.v(LOG_TAG, "Bytes written: " + bytesWritten);

    mDecoder.stop();
    mDecoder.release();
    mDecoder = null;

    mExtractor.release();
        mExtractor = null;

        audioTrack.stop();
        audioTrack.release();
        audioTrack = null;
    }

Для воспроизведения декодированного файла я использую обычный AudioTrack, который читает и воспроизводит из буфера -

public void start() {
    new Thread(new Runnable() {
        public void run() {
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
                InputStream inputStream = new FileInputStream(playingFile);
                BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

                AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, FREQUENCY,
                        CHANNEL_CONFIGURATION, AUDIO_ENCODING, BUFFER_SIZE, AudioTrack.MODE_STREAM);

                short[] buffer = new short[BUFFER_SIZE / 4];
                long startTime = System.currentTimeMillis();

                track.play();

                while (dataInputStream.available() > 0) {
                    int i = 0;
                    while (dataInputStream.available() > 0 && i < buffer.length

) {
                            buffer[i] = dataInputStream.readShort();
                            i++;
                        }
                        track.write(buffer, 0, buffer.length);
                        if (latency < 0) {
                            latency = System.currentTimeMillis() - startTime;
                        }
                    }
//
//            int i = 0;
//            while((i = dataInputStream.read(buffer, 0, BUFFER_SIZE)) > -1){
//                track.write(buffer, 0, i);
//            }
                    track.stop();
                    track.release();
                    dataInputStream.close();
                    inputStream.close();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
    }).start();
}

Что мне не хватает?

1 ответ

Решение

Кажется, ваша проблема в том, что вы записываете свой вывод в виде простых байтов (я не вижу настройки mOutputStream в вашем коде, хотя). Эти простые байты будут в исходном порядке байтов вашей платформы (на практике, little endian), но читайте его как шорты, используя DataInputStream независимым от платформы способом (который указан как big endian).

Самый простой способ решить эту проблему - использовать byte массив вместо short массив при воспроизведении; AudioTrack принимает как байтовые, так и короткие массивы, и когда ему передается байтовый массив, он интерпретирует его правильным (собственным) способом, который совпадает с выводом из MediaCodec. Просто убедитесь, что размер буфера равен четному числу байтов.

Если вам действительно нужно иметь значения как shorts, вам нужно использовать ридер, который читает в режиме с прямым порядком байтов (все текущие ABI Android имеют прямой порядок байтов). Похоже, для этого не существует простого доступного API, но на практике это не так уж сложно. Смотрите, например, readLittleShort метод в Java: замена DataInputStream для порядка байтов для примера того, как это сделать.

Другие вопросы по тегам