The previous chapter introduced how to encode Pcm into AAC. This chapter introduces how to decode AAC and play it with AudioTrack

A, MediaCodec

As described in the previous chapter, MediaCodec can not only encode, but also decode, with respect to MediaCodec, again, for decoding, let’s review the flowchart on the Google website

Before encoding, we read the data from AudioRecord into the encoder, and then get the output data from the encoder, so that we can finally get the encoded AAC file

For decoding, we can pass the encoded AAC file into the decoder, and then get the output data from the decoder into AudioTrack, so that the loop, can play the full AAC file

For AudioTrack, the previous chapter is also introduced, not clear students can go to see Android audio and video development [three] audio playback

This raises a question, how do we feed the AAC file into the decoder? Is it possible to read the file directly, just like AudioTrack plays the Pcm file, and directly read the file data and pass it to AudioTrack

Of course, it is possible to read the file directly, we only need to set the relevant parameters of audio decoding, such as sampling rate, channel Settings, encoding format and so on

Android, however, offers a more convenient component, The MediaExtractor, which takes encoded audio or video from a file and parses it frame by frame

So, next we will use MediaExtractor to read aAC audio files

First, introduce the use of MediaExtractor steps:

  • Initializing an instance
  • Setting the file path
  • Traversal all tracks, according to the need, find the specified track, and callselectTrack()methods
  • callreadSampleData()Method to read the current frame
  • Read the next frame, calladvance()
  • When the read is complete, the resource is released

Two, audio decoding

From the above description, we have a certain understanding of the steps of audio decoding, let’s review the steps of using AudioTrack:

  1. Starting a child thread
  2. Build instance
  3. Start playing
  4. Loop to read and write file dataAudioTrack
  5. Stop the playback to release resources

Then, for audio decoding playback, the steps are as follows:

  1. Starting a child thread
  2. Build instance
  3. fromMediaExtractorThe specified track is found in the
  4. Start playing and decoding
  5. Cycle fromMediaExtractorAnd feeds the data into the decoder
  6. After decoding, data is writtenAudioTrackPlay in
  7. Stop playing and decoding to release resources

Below, on the above steps, one by one

2.1 Enabling subthreads

The entire process decoded is also executed in the child thread

private static class DecodeThread extends Thread {
    public DecodeThread(a){}}Copy the code

2.2 Set parameters

private static class DecodeThread extends Thread {
    private static final long TIMEOUT_MS = 2000L;
    private MediaExtractor mediaExtractor;
    private MediaCodec mediaCodec;
    private AudioTrack audioTrack;
    private final String path;
    /** * Audio stream format (usually music) */
    private final int streamType;
    /**
     * 采样率
     */
    private final int sampleRateInHz;
    /** * Channel Settings */
    private final int channelConfig;
    /** * Encoding format */
    private final int audioFormat;
    /** * Play mode (generally use stream mode) */
    private final int mod;
    /**
     * 音频缓存大小
     */
    private int bufferSizeInBytes;
    /** * Audio format */
    private MediaFormat format;
    private String mime;
    /** whether to stop decoding */
    private boolean isStopDecode = false;
    /** * constructor (passing in the necessary arguments) */
    public DecodeThread(String path,
                        int streamType,
                        int sampleRateInHz,
                        int channelConfig,
                        int audioFormat,
                        int mod
    ) {
        this.path = path;
        this.streamType = streamType;
        this.sampleRateInHz = sampleRateInHz;
        this.channelConfig = channelConfig;
        this.audioFormat = audioFormat;
        this.mod = mod; }}Copy the code

The parameters are basically the same as when AudioTrack plays Pcm, except that there are two more components, MediaExtractor and MediaCodec

2.3 the initialization

Override Thread’s run method

@Override
public void run(a) {
    super.run();
    initMediaExtractor();
    initMediaCodec();
    initAudioTrack();
    decode();
}
Copy the code

In the run method, a series of initialization operations are performed, and the last method, decode(), actually starts decoding

# initMediaExtractor()

private void initMediaExtractor(a) {
    if (TextUtils.isEmpty(path)) {
        return;
    }
    try {
        mediaExtractor = new MediaExtractor();
        mediaExtractor.setDataSource(path);
        int trackCount = mediaExtractor.getTrackCount();
        for (int i = 0; i < trackCount; i++) {
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if(! TextUtils.isEmpty(mime) && mime.startsWith("audio/")) {
                mediaExtractor.selectTrack(i);
                this.format = format;
                this.mime = mime;
                break; }}}catch (IOException e) {
        Log.e(TAG, "initMediaExtractor: ", e);
        mediaExtractor = null;
        format = null;
        mime = null; }}Copy the code

In this method, we initialize the MediaExtractor and find the track # initMediaCodec corresponding to audio

private void initMediaCodec(a) {
    if (TextUtils.isEmpty(mime) || format == null) {
        return;
    }
    try {
        mediaCodec = MediaCodec.createDecoderByType(mime);
        mediaCodec.configure(format, null.null.0);
    } catch (IOException e) {
        e.printStackTrace();
        mediaCodec = null; }}Copy the code

As you can see, the logic of initializing MediaCodec is relatively simple, because we use the MediaFormat obtained from MediaExtractor to configure MediaCodec. If we read from a file, we need to configure the MediaFormat ourselves

# initAudioTrack()

private void initAudioTrack(a) {
    bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
    audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, mod);
}
Copy the code

2.4 play

Once you’ve entered the decode() method, it starts playing

# decode()

private void decode(a) {
    if (mediaExtractor == null || mediaCodec == null || audioTrack == null) {
        return;
    }
    long startMs = System.currentTimeMillis();
    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    mediaCodec.start();
    audioTrack.play();
    for(; ;) {if (isStopDecode) {
            release();
            break;
        }
        int inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_MS);
        if (inputBufferId >= 0) {
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferId);
            int readSize = -1;
            if(inputBuffer ! =null) {
                readSize = mediaExtractor.readSampleData(inputBuffer, 0);
            }
            if (readSize <= 0) {
                mediaCodec.queueInputBuffer(
                        inputBufferId,
                        0.0.0,
                        MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                isStopDecode = true;
            } else {
                mediaCodec.queueInputBuffer(inputBufferId, 0, readSize, mediaExtractor.getSampleTime(), 0); mediaExtractor.advance(); }}int outputBufferId = mediaCodec.dequeueOutputBuffer(info, TIMEOUT_MS);
        if (outputBufferId >= 0) {
            ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferId);
            if(outputBuffer ! =null && info.size > 0) {
                while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                    try {
                        sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break; }}byte[] data = new byte[info.size];
                outputBuffer.get(data);
                outputBuffer.clear();
                audioTrack.write(data, 0, info.size);
            }
            mediaCodec.releaseOutputBuffer(outputBufferId, false); }}}Copy the code

Decoding process, but also through the infinite loop continuously read data, and the decoding data into the AudioTrack play process, however, need to note is that, after completion of the decoding, data is not immediately introduced into AudioTrack, but to pass the decoding time and compare the current time, delay decoding, if not for this operation, The audio will play quickly

Third, making

PcmDecoder.java

PcmActivity.java