大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
Android音视频编码分为软编和硬编两种。所谓的硬编是用设备GPU去实现编解码,从而减轻CPU的压力,让程序更加的健壮,自然而然你就知道了软编其实就是让CPU编码(其实是在c层通过c/c++进行编码,之所以这样是因为c/c++平台上已经有很多比较好的音视频编解码库。比如著名ffmpeg,搞过音视频的相信对这个库绝对不会陌生)。那么或许你心目中有一个小小的疑问?为什么要编解码了?原因就是让数据更小便于传输。编解码就好比是压缩与解压!本文是把PCM数据硬编成ACC格式数据。如果对音频的采集不熟悉,请查阅Android 音频采集。
读取原始数据:
public class AudioData {
public ByteBuffer buffer; //存储原始音频数据的buffer
public int size; //buffer大小
}
//读取音频数据(原始音频数据)
private int readData(AudioData data){
if(mAudioRecord==null){//检查是否初始化
return -1;
}
if(data==null){
return -1;
}
//开辟大小为640字节的byteBuffer
if(data.buffer==null){
data.buffer=ByteBuffer.allocateDirect(640);
}else{
if(data.buffer.capacity()<640) {
data.buffer=ByteBuffer.allocate(640);
}
}
//把音频读取到data.buffer中,期望读取640个byte,返回值表示实际读取多个byte
data.size=mAudioRecord.read(data.buffer,640);
if(data.size==AudioRecord.ERROR_BAD_VALUE){//AudioRecord对象参数不可用
return -1;
}
return 0;
}
把原始的音频数据读取到ByteBuffer中,ByteBuffer是nio包中引用的,相对传统的io包要快的多,如果对ByteBuffer不熟悉请查阅图解ByteBuffer。 android平台上的音视频硬编码主要就是通过MediaCodec进行实现的。
//创建编码器
@SuppressLint("NewApi")
private int createEncoder(){
//防止重复创建编码器
if(mediaCodec!=null){
return 0;
}
try {
mediaCodec=MediaCodec.createEncoderByType("audio/mp4a-latm");
} catch (Exception e) {
e.printStackTrace();
return -1;
}
// AAC 硬编码器
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT,1); //声道数(这里是数字)
format.setInteger(MediaFormat.KEY_SAMPLE_RATE,mSampleRateInHz); //采样率
format.setInteger(MediaFormat.KEY_BIT_RATE,9600); //码率
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
bufferInfo=new MediaCodec.BufferInfo();//记录编码完成的buffer的信息
mediaCodec.configure(format, null, null,MediaCodec.CONFIGURE_FLAG_ENCODE);// MediaCodec.CONFIGURE_FLAG_ENCODE 标识为编码器
mediaCodec.start();
return 0;
}
//停止编码
@SuppressLint("NewApi")
private int stopEncoder(){
if(mediaCodec==null){
return -1;
}
mediaCodec.stop();
mediaCodec.release();
return 0;
}
或许你在烦恼配置MediaCodec时用到的MediaFormat 我怎么知道其中应该配置哪些属性了? 放心答案就在下图:(也可直接访问官网查,当然需要翻墙 编解码所需要的MediaFormat属性)
//编码
@SuppressLint("NewApi")
private int encode(AudioData result){
if(mediaCodec==null){
return -1;
}
//把数据拷贝到byte数组中
byte[] data=new byte[result.size];
result.buffer.get(data);
result.buffer.flip();
inputBuffers=mediaCodec.getInputBuffers();
outputBuffers=mediaCodec.getOutputBuffers();
// <0一直等待可用的byteBuffer 索引;=0 马上返回索引 ;>0 等待相应的毫秒数返回索引
inputBufferIndex=mediaCodec.dequeueInputBuffer(-1); //一直等待(阻塞)
if(inputBufferIndex>=0){ //拿到可用的buffer索引了
inputBuffer=inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
mediaCodec.queueInputBuffer(inputBufferIndex,0,result.size,0,0); //投放到编码队列里去
}
//获取已经编码成的buffer的索引 0表示马上获取 ,>0表示最多等待多少毫秒获取
outputBufferIndex=mediaCodec.dequeueOutputBuffer(bufferInfo,0);
while(outputBufferIndex>=0){
//------------添加头信息--------------
int outBitsSize = bufferInfo.size;
int outPacketSize = outBitsSize + 7; // 7 is ADTS size
byte[]outData= new byte[outPacketSize];
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
addADTStoPacket(outData,outPacketSize,mSampleRateInHz,1);//添加头
outputBuffer.get(outData,7,outBitsSize);
try {
ou.write(outData);
} catch (IOException e) {
e.printStackTrace();
}
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
}
return 0;
}
/**
* 添加头部信息
* Add ADTS header at the beginning of each and every AAC packet. This is
* needed as MediaCodec encoder generates a packet of raw AAC data.
* Note the packetLen must count in the ADTS header itself.
* packet 数据
* packetLen 数据长度
* sampleInHz 采样率
* chanCfgCounts 通道数
**/
private void addADTStoPacket(byte[] packet, int packetLen,int sampleInHz,int chanCfgCounts) {
int profile = 2; // AAC LC
int freqIdx = 8; // 16KHz 39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
switch (sampleInHz){
case 8000:{
freqIdx = 11;
break;
}
case 16000:{
freqIdx = 8;
break;
}
default:
break;
}
int chanCfg = chanCfgCounts; // CPE
// fill in ADTS data
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF9;
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
整片文章中最重要的就是编码这部分 ,编码AAC文件格式的音频时需要添加头的,要不然是没有办法进行正常播放的。添加头部信息,详细可以查阅该文:ADTS格式解析
最后是录音以及编码的调用封装方法:(全部完整代码,请在文章最后下载AAC音频硬编可播放Demo查阅)
//录音以及编码
private void Recording(){
isStart=true;
File file=null;
int result=startRecord();//开始录音
if(result==0){
file=new File(parent,String.valueOf(SystemClock.elapsedRealtime())+".aac");
final String a=file.getAbsolutePath();
try {
file.createNewFile();
runOnUiThread(new Runnable() {
@Override
public void run() {
path.setText("文件存目路径:"+a);
}
});
} catch (IOException e) {
e.printStackTrace();
Log.e("ZL","创建文件出错");
}
}
if(file!=null){
try {
ou=new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("ZL","创建输出流出错");
}
}
int result1=createEncoder(); //创建编码器
if(result1==0){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"创建编码器成功",Toast.LENGTH_SHORT).show();
}
});
}
AudioData data=new AudioData();
while(isStart)
{
int result2=readData(data);
if(result2==0){
encode(data);
Log.e("ZL","录音成功");
}
}
stopRecord(); //停止录音
stopEncoder(); //停止编码
if(ou!=null){
try {
ou.close();
}
catch (IOException e) {
e.printStackTrace();
Log.e("ZL","关闭输出流出错");
}
}
}
截止至2016/10/10为止,目前android平台支持的音视频硬编码格式(当然大家也可访问这个网址android平台支持的音视频硬编码格式进行查看。ps:要想打开这个网址需要翻墙,中国长城防火墙实在是太厚太高啦,翻墙的方法大家百度下就知道啦。),如下图所示:
转载请申明出处 http://blog.csdn.net/java_android_c/article/details/52775769
备注:
AAC音频硬编可播放Demo 用手机上支持aac格式的播放器就可以播放
注意添加相应的权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/179191.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...