MediaCodec AudioTrack 硬解AAC格式音频(二)

news/2024/11/29 23:34:26/

在上一篇文章介绍了 MediaCodec AudioRecord 硬编 AAC 格式音频,这篇介绍如何用 MediaCodec 硬解码 AAC 文件,并使用 AudioTrack 播放,总体相对而言遇到的坑比硬编要少一些。

介绍

MediaCodec
MediaCodec 是 Android 用于音视频编解码的一套偏底层的 API,直接利用硬件加速进行编解码。

MediaCodec 跟上一篇文章一样的流程:

  1. dequeueInputBuffer:从 input 缓存区申请 buffer 编号 Index
  2. getInputBuffer:用编号 Index 取得输入的缓冲区,将需要编码的数据写入 buffer
  3. queueInputBuffer:将 buffer 入 MediaCodec 的队列,MediaCodec 会从 buffer 中取数据处理
  4. dequeueOutputBuffer:从 output 缓冲区申请 buffer 编号
  5. getOutputBuffer:用编号 Index 取得输出的缓冲区,buffer 中就是处理后的数据
  6. releaseOutputBuffer:将该 buffer 放回 output 缓冲区队列

AudioTrack
使用来播放 PCM 音频数据的类
具体可以参考 Android AudioRecord、AudioTrack 录制播放音频

MediaExtractor
视音频分离器,将一些格式的视频分离出视频轨道和音频轨道。
具体可以参考 MediaExtractor、MediaMuxer 分离和合成 mp4

使用 MediaExtractor 原因是什么?
因为从外部读取了 AAC 文件格式的音频,需要知道 ACC 的具体格式以及其他信息,并且后续还需要从文件一帧一帧的读取出来,放置到 MediaCodec 中进行解码,最后通过 AudioTrack 中播放。刚好 MediaExtractor 可以满足需求。

流程

  1. 初始化
// MediaExtractor的初始化
mExtractor = new MediaExtractor();
mExtractor.setDataSource(getSDPath() + "/acc_encode.mp4");
MediaFormat mFormat = null;
int samplerate = 0;
int changelConfig = 0;
int selectTrack = 0;
String mine = MediaFormat.MIMETYPE_AUDIO_AAC;
for (int i = 0; i < mExtractor.getTrackCount(); i++) {mFormat = mExtractor.getTrackFormat(i);mine = mFormat.getString(MediaFormat.KEY_MIME);if (mine.startsWith("audio/")) {selectTrack = i;samplerate = mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);// 采样率changelConfig = mFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); // 声道数break;}
}
mExtractor.selectTrack(selectTrack);// AudioTrack的初始化
int minBufferSize = AudioTrack.getMinBufferSize(samplerate, changelConfig, AudioFormat.ENCODING_PCM_16BIT);
mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, samplerate, changelConfig, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
mPlayer.play();// MediaCodec的初始化:利用mExtractor查询的信息
mDecoder = MediaCodec.createDecoderByType(mine);
mDecoder.configure(mFormat, null, null, 0);
mDecoder.start();

2. 解码并播放

以下是读取一帧数据并处理的详细过程:

int inputIdex = mDecoder.dequeueInputBuffer(10000);
if (inputIdex < 0) {isFinish = true;
}
ByteBuffer inputBuffer = mDecoder.getInputBuffer(inputIdex);
inputBuffer.clear();
int samplesize = mExtractor.readSampleData(inputBuffer, 0); // 读取一帧数据
if (samplesize > 0) {mDecoder.queueInputBuffer(inputIdex, 0, samplesize, 0, 0);mExtractor.advance();//下一帧
} else {isFinish = true;
}
int outputIndex = mDecoder.dequeueOutputBuffer(decodeBufferInfo, 10000);
//申请输入的buffer, 等待时间,0:不等待,-1:一直而等待
ByteBuffer outputBuffer;
byte[] chunkPCM;
while (outputIndex >= 0) { // 可能不能一次性读完outputBuffer = mDecoder.getOutputBuffer(outputIndex);chunkPCM = new byte[decodeBufferInfo.size];outputBuffer.get(chunkPCM);outputBuffer.clear();//不清空下次会得到同样的数据mPlayer.write(chunkPCM, 0, decodeBufferInfo.size);// 将数据写入AudioTrack播放 mDecoder.releaseOutputBuffer(outputIndex, false);// 若不释放,MediaCodec用完所有的Buffer后 将不能向外输出数据outputIndex = mDecoder.dequeueOutputBuffer(decodeBufferInfo, 10000);
}

因此想要完整播放,只需要全部读完文件并一帧帧处理就可以了。

完整源码

public class AacTrackActivity extends AppCompatActivity {public static String[] MICROPHONE = {Manifest.permission.RECORD_AUDIO};public static String[] STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};private MyAudioTrack myAudioTrack;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_aac_track);myAudioTrack = new MyAudioTrack();findViewById(R.id.acc_playing).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {checkRecordPermission();new Thread(new Runnable() {@Overridepublic void run() {myAudioTrack.start();}}).start();}});}private class MyAudioTrack {private AudioTrack mPlayer;private MediaCodec mDecoder;private MediaExtractor mExtractor;public void start() {try {mExtractor = new MediaExtractor();mExtractor.setDataSource(getSDPath() + "/acc_encode.mp4");MediaFormat mFormat = null;int samplerate = 0;int changelConfig = 0;int selectTrack = 0;String mine = MediaFormat.MIMETYPE_AUDIO_AAC;for (int i = 0; i < mExtractor.getTrackCount(); i++) {mFormat = mExtractor.getTrackFormat(i);mine = mFormat.getString(MediaFormat.KEY_MIME);if (mine.startsWith("audio/")) {selectTrack = i;samplerate = mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);changelConfig = mFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);break;}}mExtractor.selectTrack(selectTrack);int minBufferSize = AudioTrack.getMinBufferSize(samplerate, changelConfig, AudioFormat.ENCODING_PCM_16BIT);mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, samplerate, changelConfig, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);mPlayer.play();mDecoder = MediaCodec.createDecoderByType(mine);mDecoder.configure(mFormat, null, null, 0);mDecoder.start();decodeAndPlay();} catch (IOException e) {e.printStackTrace();runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(AacTrackActivity.this, "文件不存在",Toast.LENGTH_SHORT).show();}});}}private void decodeAndPlay() {boolean isFinish = false;MediaCodec.BufferInfo decodeBufferInfo = new MediaCodec.BufferInfo();while (!isFinish) {int inputIdex = mDecoder.dequeueInputBuffer(10000);if (inputIdex < 0) {isFinish = true;}ByteBuffer inputBuffer = mDecoder.getInputBuffer(inputIdex);inputBuffer.clear();int samplesize = mExtractor.readSampleData(inputBuffer, 0);if (samplesize > 0) {mDecoder.queueInputBuffer(inputIdex, 0, samplesize, 0, 0);mExtractor.advance();} else {isFinish = true;}int outputIndex = mDecoder.dequeueOutputBuffer(decodeBufferInfo, 10000);ByteBuffer outputBuffer;byte[] chunkPCM;while (outputIndex >= 0) {outputBuffer = mDecoder.getOutputBuffer(outputIndex);chunkPCM = new byte[decodeBufferInfo.size];outputBuffer.get(chunkPCM);outputBuffer.clear();mPlayer.write(chunkPCM, 0, decodeBufferInfo.size);mDecoder.releaseOutputBuffer(outputIndex, false);outputIndex = mDecoder.dequeueOutputBuffer(decodeBufferInfo, 10000);}}release();}private void release() {if (mDecoder != null) {mDecoder.stop();mDecoder.release();mDecoder = null;}if (mExtractor != null) {mExtractor.release();mExtractor = null;}if (mPlayer != null) {mPlayer.stop();mPlayer.release();mPlayer = null;}runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(AacTrackActivity.this, "播放完成", Toast.LENGTH_SHORT).show();}});}}public String getSDPath() {// 判断是否挂载if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {return Environment.getExternalStorageDirectory().getAbsolutePath();}return Environment.getRootDirectory().getAbsolutePath();}private void checkRecordPermission() {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, MICROPHONE, 1);return;}if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, STORAGE, 1);return;}}
}

http://www.ppmy.cn/news/805875.html

相关文章

计算机硬件acc作用,累加器A的主要作用是什么_一文解析累加器a和acc的区别

累加器简介 在中央处理器中,累加器(accumulator) 是一种寄存器,用来储存计算产生的中间结果。如果没有像累加器这样的寄存器,那么在每次计算 (加法,乘法,移位等等) 后就必须要把结果写回到内存,也许马上就得读回来。然而存取主存的速度是比从算术逻辑单元到有直接路径的累…

ACC编码

AAC是新一代的音频有损压缩技术&#xff0c;它通过一些附加的编码技术&#xff08;比如PS,SBR等&#xff09;&#xff0c;衍生出了LC-AAC,HE-AAC,HE-AACv2三种主要的编码&#xff0c;LC-AAC就是比较传统的AAC&#xff0c;相对而言&#xff0c;主要用于中高码率(>80Kbps)&…

java解码acc音频文件_(JAVA)将(acc/m4a)音频转换成Mp3格式

实际工作中有一个需求&#xff0c;就是上传到微信的音频(格式为m4a)在PC浏览器无法播放&#xff0c;需要转码成mp3&#xff0c;然后在PC端播放(其实后来发现HTML5的audio可以播放...)&#xff0c;该工具依赖jave-1.0.2.2.jar(http://download.csdn.net/download/tinnkenn/93944…

请问android如何录制acc格式音频

问题描述 我这边要即时通讯。acc跟ios可以互通。但是录制的时候选项没有acc是要转换么 解决方案1 AudioRecord 获取 sample 然后用 ffmpeg 转成 AAC 转载于:https://www.cnblogs.com/yiguobei99/p/4002004.html

FFMPEG编码实现:pcm编码为acc

本程序流程&#xff1a; 1、创建输出码流的上下文AVFormatContext&#xff0c;并初始化 2、打开输出文件&#xff1a;avio_open2() 3、创建新流&#xff1a;avformat_new_stream() //用于保存音频流信息&#xff0c;一个完整的视频文件包含多个流信息&#xff1a;视频流、音频…

java解码acc音频文件_g711a音频文件转aac格式

实现g711a音频文件转换成aac文件的控件 编译成exe可执行文件 传入两个参数,第一个为g711a格式的源文件,第二个为目标文件 readme 控制台应用程序&#xff1a;Convert2ACC 项目概述 应用程序向导已为您创建了此 Convert2ACC 应用程序。 本文件概要介绍组成 Convert2ACC 应用程…

java实现将.acc格式转化为mp3格式

最近接到一个需求&#xff0c;将腾讯云的音频.acc格式的转为mp3格式 这里用到的是jave&#xff0c; jave2&#xff08;Java音频视频编码器&#xff09;库是Java对ffmpeg的包装,它可以很方便的实现视频音频格式的转换&#xff0c;本文简单记录一下将wav格式的音频转换成mp3格式…

蓝牙音频传输格式:ACC,SBC,APTX和LDAC

因为最近接触了一些蓝牙和高品质音乐传输上的知识&#xff0c;先记录一下当前较为常用的几种音频传输格式和支持厂商。下面按照编码的码率由低到高列举。 1. SBC &#xff08;Sub-band coding&#xff0c;子带编码&#xff09; 最早的格式应该是SBC&#xff0c;SBC是A2DP&…