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 call
selectTrack()
methods - call
readSampleData()
Method to read the current frame - Read the next frame, call
advance()
- 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:
- Starting a child thread
- Build instance
- Start playing
- Loop to read and write file data
AudioTrack
- Stop the playback to release resources
Then, for audio decoding playback, the steps are as follows:
- Starting a child thread
- Build instance
- from
MediaExtractor
The specified track is found in the - Start playing and decoding
- Cycle from
MediaExtractor
And feeds the data into the decoder - After decoding, data is written
AudioTrack
Play in - 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