简介
MediaCodec:负责媒体文件的编码和解码工作,内部方法均为native方法。
正文
音频播放主要步骤如下:
- 将资源加载到MediaExtractor
- 获取音频所在轨道
- 设置MediaExtractor选中视频所在轨道
- 创建音频解码的MediaCodec
- 循环开始
- 将extractor中资源以一个单位填充到decorder的输入缓存区中
- decorder之后,将解码的音频填充到输出缓冲区中
- 将缓冲区中的PCM数据加载到到AudioTrack中
- 循环结束,直至音频资源的末尾
- 释放资源
音频解码创建一个解码线程
private static ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
1. 初始化,获取音频所在轨道,设置MediaExtractor选中视频所在轨道,创建音频解码的MediaCodec
private void initDecorder() throws IOException {
mMediaExtractor = new MediaExtractor();
mMediaExtractor.setDataSource(mMusicPath);
int numTracks = mMediaExtractor.getTrackCount();
for (int i = 0; i < numTracks; i++) {
MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(i);
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio")) {
mMediaExtractor.selectTrack(i);
mDecoder = MediaCodec.createDecoderByType(mime);
if (mime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
// AAC ADTS头部处理
ByteBuffer csd = mediaFormat.getByteBuffer("csd-0");
for (int k = 0; k < csd.capacity(); ++k) {
Log.e(TAG, "csd : " + csd.array()[k]);
}
}
mSampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
channel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
mediaFormat = makeADTSData(MediaCodecInfo.CodecProfileLevel.AACObjectLC, mSampleRate, channel);
}
mDecoder.configure(mediaFormat, null, null, 0);
break;
}
}
2. 音频解码decoder
public void decode() {
mInputByteBuffers = mDecoder.getInputBuffers();
mOutputByteBuffers = mDecoder.getOutputBuffers();
mBufferInfo = new MediaCodec.BufferInfo();
while (!eosReceived) {
int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US);
if (inIndex >= 0) {
ByteBuffer buffer = mInputByteBuffers[inIndex];
int sampleSize = mMediaExtractor.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(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, mMediaExtractor.getSampleTime(), 0);
mMediaExtractor.advance();
}
int outIndex = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
mOutputByteBuffers = mDecoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
MediaFormat format = mDecoder.getOutputFormat();
Log.d(TAG, "New format " + format);
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d(TAG, "dequeueOutputBuffer timed out!");
break;
default:
ByteBuffer outBuffer = mOutputByteBuffers[outIndex];
Log.v(TAG, "We can't use this buffer but render it due to the API limit, " + outBuffer);
final byte[] chunk = new byte[mBufferInfo.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
MediaFormat mFormat = mDecoder.getOutputFormat();
mOnCapturePCMListener.capturePCM(chunk, mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mFormat.getInteger
(MediaFormat.KEY_CHANNEL_COUNT));
mDecoder.releaseOutputBuffer(outIndex, false);
break;
}
// All decoded frames have been rendered, we can stop playing now
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
}
mDecoder.stop();
mDecoder.release();
mDecoder = null;
mMediaExtractor.release();
mMediaExtractor = null;
eosReceived = true;
mExecutorService.shutdown();
mExecutorService = null;
}
总结
了解mediacodec和MediaExtractor的api和原理,按音频播放的步骤进行解码播放。
学习一步一个脚印,谢谢今天的自己。
网友评论