Search code examples
javaandroidandroid-mediacodecaudiotrackmediaextractor

Audio sample not playing as expected on android11


I am trying to extract and play audio samples from a video file in Android 11. However, I have followed some tutorials from the official documentation, but it still doesn't work. All I get is noise for a shorter duration of the audio. Please check the code I am using to achieve this:


// This is how I get the audio samples
public ArrayList<byte[]> getAudio() {
    int index = 0;
    ArrayList<byte[]> audio = new ArrayList<>();
    ByteBuffer buffer = ByteBuffer.allocateDirect(format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE));

    for (;;) {
        int size = extractor.readSampleData(buffer, 0);
        total_size += size;
        if (size < 0) { // no more data to read
            break;
        }

        for (int i = 0; i < size / 2; i++) {
            byte[] sample = new byte[2];
            for (int j = 0; j < sample.length; j++) {
                sample[j] = buffer.get();
            }
            audio.add(sample);
        }
        extractor.advance();
    }
    return audio;
}

Log.d("AudioTest", "total audio data size in bytes: " + total_size);

// This is how I play the audio
int SIZE = AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, SIZE, AudioTrack.MODE_STREAM);
ArrayList<byte[]> audio = getAudio();
long start = System.currentTimeMillis();

track.play();
byte[] data = new byte[audio.size() * 2];
for (int i = 0, k = 0; i < audio.size(); i++) {
    byte[] sample = audio.get(i);
    for (int j = 0; j < sample.length; j++, k++) {
        data[k] = sample[j];
    }
}
track.write(data, 0, data.length);

long duration = System.currentTimeMillis() - start;
Log.d("AudioTest", "that took: " + (duration / 1000) + "s");


I am facing an issue where my audio plays random noise for only 5 seconds. The getAudio() method retrieves only 487,831 bytes from the 15-second video file. Here are the details of the video file:

Frames:

  • has-sdtp: 1
  • track-id: 1
  • level: 2048
  • mime: video/avc
  • frame-count: 461
  • profile: 8
  • language: und
  • color-standard: 1
  • display-width: 1920
  • track-fourcc: 828601953
  • csd-1: java.nio.HeapByteBuffer[pos=0 lim=9 cap=9]
  • color-transfer: 3
  • durationUs: 15,382,033
  • display-height: 1080
  • width: 1920
  • color-range: 2
  • max-input-size: 3,145,748
  • frame-rate: 30
  • height: 1080
  • csd-0: java.nio.HeapByteBuffer[pos=0 lim=33 cap=33]

Samples:

  • max-bitrate: 262,664
  • isDMCMMExtractor: 1
  • sample-rate: 48,000
  • track-id: 2
  • mime: audio/mp4a-latm
  • profile: 2
  • language: und
  • aac-profile: 2
  • track-fourcc: -1
  • encoder-delay: 0
  • durationUs: 15,402,666
  • channel-count: 2
  • bits-per-sample: 16
  • encoder-padding: 48
  • max-input-size: 524,308
  • csd-0: java.nio.HeapByteBuffer[pos=0 lim=2 cap=2]

Any help would be highly appreciated.

Thank you.


Solution

  • The audio is compressed using the Advanced Audio Coding (AAC) compression. You can see it from this line: mime: audio/mp4a-latm.

    But you are not decoding it. The MediaExtractor reads compressed samples while the AudioTrack expects decoded data (AudioFormat.ENCODING_PCM_16BIT in your case).

    You can use the MediaCodec API to decode the audio buffers that MediaExtractor reads.

    By the way, you will not need to copy the decoded buffers into an array, just pass them to the AudioTrack as is.

    Alternatively, you could detect the correct encoding and specify it when you set the AudioTrack up. See here and here.