需求背景:

博主目前所在的公司是一家做视频通讯的公司,所以对音频,视频这一块对编码方式都有一定的要求,由于之前一直没有接触JNI这一块,突然让我去做音频的转码还是有一定的苦难的。一开始对于JNI编程我是拒绝的,一直遵循着能用java源码,就绝不用Jni那一块。但是,显示总是残酷的,网上的资料,Demo很少,或者都是年代久远,还不能运行的。所以我抱着试一试的心态去接触JNI,也还蛮有收获的,好了废话了这么多,也该进入主题了。

Demo主要功能:

AndroidStudio项目,在安卓平台下,调用G.711 C++编解码方法进行音频源的编码和解码。Demo在文章末尾。

G.711 C++源码

http://download.csdn.net/download/orient1860/7691041 待会我demo里面也有

Android 源码

package com.chezi008.hellojni.encode;import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;import com.chezi008.hellojni.R;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;/*** 描述:G.711 编码与解码* 作者:chezi008 on 2017/3/10 16:33* 邮箱:chezi008@163.com*/
@SuppressLint("SdCardPath")
public class G711DecoderActivity extends Activity {// 音频获取源private int audioSource = MediaRecorder.AudioSource.MIC;// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025private static int sampleRateInHz = 8000;// 设置音频的录制的声道CHANNEL_I N_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道private static int channelConfig = AudioFormat.CHANNEL_IN_STEREO;// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;// 缓冲区字节大小private int bufferSizeInBytes = 0;private Button Start;private Button Stop;private Button convert;private AudioRecord audioRecord;private boolean isRecord = false;// 设置正在录制的状态//AudioName裸音频数据文件,编码后的文件private static final String AudioName = "/sdcard/end.g711";//解码后的文件private static final String AudioDecodeName = "/sdcard/endDecode.g711";//NewAudioName可播放的音频文件private static final String NewAudioName = "/sdcard/new.wav";private G711Decoder codec;public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);getWindow().setFormat(PixelFormat.TRANSLUCENT);// 让界面横屏requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉界面标题getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);// 重新设置界面大小setContentView(R.layout.activity_g711);init();}private void init() {Start = (Button) this.findViewById(R.id.start);Stop = (Button) this.findViewById(R.id.stop);convert = (Button) findViewById(R.id.convert);Start.setOnClickListener(new TestAudioListener());Stop.setOnClickListener(new TestAudioListener());convert.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {
//                new PlayTask().execute();new Thread(new AudioConvert()).start();}});codec = new G711Decoder();creatAudioRecord();}private void creatAudioRecord() {// 获得缓冲区字节大小bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig, audioFormat);// 创建AudioRecord对象audioRecord = new AudioRecord(audioSource, sampleRateInHz,channelConfig, audioFormat, bufferSizeInBytes);}class TestAudioListener implements OnClickListener {@Overridepublic void onClick(View v) {if (v == Start) {startRecord();}if (v == Stop) {stopRecord();}}}private void startRecord() {audioRecord.startRecording();// 让录制状态为trueisRecord = true;// 开启音频文件写入线程new Thread(new AudioRecordThread()).start();}private void stopRecord() {close();}private void close() {if (audioRecord != null) {System.out.println("stopRecord");isRecord = false;//停止文件写入audioRecord.stop();audioRecord.release();//释放资源audioRecord = null;}}class AudioRecordThread implements Runnable {@Overridepublic void run() {writeDateTOFile();//往文件中写入裸数据
//            copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件}}class AudioConvert implements Runnable {@Overridepublic void run() {decodeAudio(AudioName, AudioDecodeName);copyWaveFile(AudioDecodeName, NewAudioName);//给裸数据加上头文件}}/*** 解码音频文件* @param inFilename* @param outFilename*/private void decodeAudio(String inFilename, String outFilename) {FileInputStream in = null;FileOutputStream out = null;byte[] data = new byte[1024];byte[] outData = new byte[2048];//这里特别注意outData是data的两倍,之前不知道,一直卡在这里,是的解码进行不下去try {in = new FileInputStream(inFilename);out = new FileOutputStream(outFilename);int length = 0;while ((length = in.read(data)) != -1) {codec.VoiceDecode(data, outData, length);out.write(outData);}in.close();out.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 这里将数据写入文件,但是并不能播放,因为AudioRecord获得的音频是原始的裸音频,* 如果需要播放就必须加入一些格式或者编码的头信息。但是这样的好处就是你可以对音频的 裸数据进行处理,比如你要做一个爱说话的TOM* 猫在这里就进行音频的处理,然后重新封装 所以说这样得到的音频比较容易做一些音频的处理。*/private void writeDateTOFile() {// new一个byte数组用来存一些字节数据,大小为缓冲区大小byte[] audiodata = new byte[bufferSizeInBytes];FileOutputStream fos = null;int readsize = 0;try {File file = new File(AudioName);if (file.exists()) {file.delete();}fos = new FileOutputStream(file);// 建立一个可存取字节的文件} catch (Exception e) {e.printStackTrace();}while (isRecord == true) {readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);byte[] alawData = new byte[readsize];if (AudioRecord.ERROR_INVALID_OPERATION != readsize) {try {int len = codec.VoiceEncode(audiodata, alawData, readsize); //调用C代码进行编码fos.write(alawData, 0, len); //保存到本地} catch (IOException e) {e.printStackTrace();}}}try {fos.close();// 关闭写入流} catch (IOException e) {e.printStackTrace();}}/*** 解码后增加音频文件的头部,是的文件可播放* @param inFilename* @param outFilename*/private void copyWaveFile(String inFilename, String outFilename) {FileInputStream in = null;FileOutputStream out = null;long totalAudioLen = 0;long totalDataLen = totalAudioLen + 36;long longSampleRate = sampleRateInHz;int channels = 2;long byteRate = 16 * sampleRateInHz * channels / 8;byte[] data = new byte[bufferSizeInBytes];try {in = new FileInputStream(inFilename);out = new FileOutputStream(outFilename);totalAudioLen = in.getChannel().size();totalDataLen = totalAudioLen + 36;WriteWaveFileHeader(out, totalAudioLen, totalDataLen,longSampleRate, channels, byteRate);while (in.read(data) != -1) {out.write(data);}in.close();out.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。* 为我为啥插入这44个字节,这个还真没深入研究,不过你随便打开一个wav* 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有* 自己特有的头文件。*/private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,long totalDataLen, long longSampleRate, int channels, long byteRate)throws IOException {byte[] header = new byte[44];header[0] = 'R'; // RIFF/WAVE headerheader[1] = 'I';header[2] = 'F';header[3] = 'F';header[4] = (byte) (totalDataLen & 0xff);header[5] = (byte) ((totalDataLen >> 8) & 0xff);header[6] = (byte) ((totalDataLen >> 16) & 0xff);header[7] = (byte) ((totalDataLen >> 24) & 0xff);header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';header[12] = 'f'; // 'fmt ' chunkheader[13] = 'm';header[14] = 't';header[15] = ' ';header[16] = 16; // 4 bytes: size of 'fmt ' chunkheader[17] = 0;header[18] = 0;header[19] = 0;header[20] = 1; // format = 1header[21] = 0;header[22] = (byte) channels;header[23] = 0;header[24] = (byte) (longSampleRate & 0xff);header[25] = (byte) ((longSampleRate >> 8) & 0xff);header[26] = (byte) ((longSampleRate >> 16) & 0xff);header[27] = (byte) ((longSampleRate >> 24) & 0xff);header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);header[32] = (byte) (2 * 16 / 8); // block alignheader[33] = 0;header[34] = 16; // bits per sampleheader[35] = 0;header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);header[43] = (byte) ((totalAudioLen >> 24) & 0xff);out.write(header, 0, 44);}@Overrideprotected void onDestroy() {close();super.onDestroy();}
}

G711Decoder类我就不贴了,可以去Demo里面找。

Demo地址

http://download.csdn.net/detail/chezi008/9777122

Android G.711音频编解码相关推荐

  1. G.726音频编解码原理介绍

    一.PCM 脉冲编码调制(Pulse-code modulation,PCM)是一种模拟信号的数码化方法.PCM将信号的强度依照同样的间距分成数段,然后用独特的数码记号(通常是二进制)来量化.PCM常 ...

  2. G711(PCM/PCMA/PCMU),G721,G723,G729等 音频编解码

    G711,G721,G723音频编解码,G729音频库,Android G711(PCMA/PCMU).G726.PCM音频转码到AAC,ffmpeg接收g723音频流,Android G726语音编 ...

  3. Android视频编辑器(五)音频编解码、从视频中分离音频、音频混音、音频音量调节等

    前言 这篇博客,主要讲解的是android端的音频处理,在开发Android视频编辑器的时候,有一个非常重要的点就是音频的相关处理.比如如何从视频中分离音频(保存为mp3文件),然后分离出来的音频如何 ...

  4. 音频编解码标准G.711与G.729

    G.711和G.729协议是两对用于语音压缩的编码方案,两者具有一些相似之处,但不同于完全自由使用的G.711,使用G.729是需要付费的,而且,对于使用G.729的情况,CPU占有时间大约为G.71 ...

  5. Android G711A 音频编解码,去除“吱吱”电流声,附上so下载地址

    Android G711A 音频编解码,去除"吱吱"电流声,附上so下载地址. 下载地址:GitHub - Mr-Jiang/G711A: C++ for G711A coding ...

  6. 视音频编解码学习工程:FLV封装格式分析器

    ===================================================== 视音频编解码学习工程系列文章列表: 视音频编解码学习工程:H.264分析器 视音频编解码学习 ...

  7. 详解音频编解码的原理、演进和应用选型等

    本文来自网易云音乐音视频实验室负责人刘华平在LiveVideoStackCon 2017大会上的分享,并由LiveVideoStack根据演讲内容整理而成(本次演讲PPT文稿,请从文末附件下载). 1 ...

  8. 音频编解码基础知识(一)

    音频编解码常用的三种实现方案 1)采用专用的音频芯片对语音信号进行采集和处理,音频编解码算法集成在硬件内部,如 MP3 编解码芯片.语音合成分析芯片等.使用这种方案的优点就是处理速度块,设计周期短:缺 ...

  9. 即时通讯音视频开发(十八):详解音频编解码的原理、演进和应用选型

    1.引言 大家好,我是刘华平,从毕业到现在我一直在从事音视频领域相关工作,也有一些自己的创业项目,曾为早期Google Android SDK多媒体架构的构建作出贡献. 就音频而言,无论是算法多样性, ...

最新文章

  1. kubernetes相关概念
  2. spark数据查询语句select_sparksql读取hive表中数据
  3. (018)java后台开发之语法输出流flush()方法
  4. float类型为什么不精确等于0_程序中算钱不能用浮点类型是个什么坑?
  5. mysql 配置文件设置最大链接数 max_connections不生效
  6. 46. Permutations 排列数
  7. 关于luci的几个问题一
  8. javafx 使用_使用JavaFX AnimationTimer
  9. MIK C语言面试两题
  10. 杭州师范大学马云雕像被拆,校方回应!
  11. 2020 科大讯飞全球开发者大会节目单来了!
  12. 嵌入式Linux系统编程学习之十七计时器与信号
  13. MSF之IIS6WebDAV执行漏洞复现
  14. 各大网络安全厂商及安全产品
  15. FRM-18108和FRM-10102解决方法
  16. Ubuntu Linux 3D桌面完全教程
  17. Google APAC 2016 University Graduates Test Practice Round APAC test
  18. 石油大学专升本计算机课程-计算机应用基础(1)
  19. 2021-09-17
  20. JS指定打印机进行打印

热门文章

  1. 常见的设计模式(单例模式工厂模式)
  2. 美剧字幕组翻译谈如何提高英语听力口语
  3. SitePoint播客#73:停产和退汤
  4. 【基于STM32F103C8T6的智能风扇控制】
  5. 【经验分享】我的数据挖掘竞赛之路及秋招总结
  6. 电子元器件简介——三极管功放
  7. 射频功放学习之ADS版图优化
  8. 赣榆高中2021高考成绩查询,2019年赣榆高考各校的成绩情况
  9. RTX 3080 Linux和Windows 平台兼容性问题
  10. Linux运维 第二阶段(十六)OS优化(1)