Android多媒体之 wav和amr的互转
来源:互联网 发布:thinkphp商城免费源码 编辑:程序博客网 时间:2024/06/01 18:54
1、wav和amr文件都有头文件,AudioRecord录制出来的文件是raw格式的就不能播放,加上wav头文件就变成wav文件就可以播放。
给raw文件添加wav头文件
/** * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。 * 为我为啥插入这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 header header[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 ' chunk header[13] = 'm';header[14] = 't';header[15] = ' ';header[16] = 16; // 4 bytes: size of 'fmt ' chunk header[17] = 0;header[18] = 0;header[19] = 0;header[20] = 1; // format = 1 header[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 align header[33] = 0;header[34] = 16; // bits per sample header[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);}amr头文件
final private static byte[] header = new byte[]{0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A};
2、wav或raw转amr
2.1 通过Android系统自带的AmrInputStream类,因为它被隐藏了,只有通过反射来操作。
public class AmrInputStream extends InputStream{ static { System.loadLibrary("media"); } private final static String TAG = "AmrInputStream"; // frame is 20 msec at 8.000 khz private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; // pcm input stream private InputStream mInputStream; // native handle private int mGae; // result amr stream private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2]; private int mBufIn = 0; private int mBufOut = 0; // helper for bytewise read() private byte[] mOneByte = new byte[1]; /** * Create a new AmrInputStream, which converts 16 bit PCM to AMR * @param inputStream InputStream containing 16 bit PCM. */ public AmrInputStream(InputStream inputStream) { mInputStream = inputStream; mGae = GsmAmrEncoderNew(); GsmAmrEncoderInitialize(mGae); } @Override public int read() throws IOException { int rtn = read(mOneByte, 0, 1); return rtn == 1 ? (0xff & mOneByte[0]) : -1; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int offset, int length) throws IOException { if (mGae == 0) throw new IllegalStateException("not open"); // local buffer of amr encoded audio empty if (mBufOut >= mBufIn) { // reset the buffer mBufOut = 0; mBufIn = 0; // fetch a 20 msec frame of pcm for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) { int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i); if (n == -1) return -1; i += n; } // encode it mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0); } // return encoded audio to user if (length > mBufIn - mBufOut) length = mBufIn - mBufOut; System.arraycopy(mBuf, mBufOut, b, offset, length); mBufOut += length; return length; } @Override public void close() throws IOException { try { if (mInputStream != null) mInputStream.close(); } finally { mInputStream = null; try { if (mGae != 0) GsmAmrEncoderCleanup(mGae); } finally { try { if (mGae != 0) GsmAmrEncoderDelete(mGae); } finally { mGae = 0; } } } } @Override protected void finalize() throws Throwable { if (mGae != 0) { close(); throw new IllegalStateException("someone forgot to close AmrInputStream"); } } // // AudioRecord JNI interface // public static native int GsmAmrEncoderNew(); public static native void GsmAmrEncoderInitialize(int gae); public static native int GsmAmrEncoderEncode(int gae, byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException; public static native void GsmAmrEncoderCleanup(int gae); public static native void GsmAmrEncoderDelete(int gae);}
JNI位于源代码下的frameworks\base\media\jni目录,对应的libmedia_jni.so文件文件在system/lib,打开File Exploer就可以看到。
下面是通过反射进行文件转换
/** * 通过反射调用android系统自身AmrInputStream类进行转换 * @param inPath 源文件 * @param outPath 目标文件 */ public void systemWav2Amr(String inPath,String outPath){try {FileInputStream fileInputStream = new FileInputStream(inPath);FileOutputStream fileoutputStream = new FileOutputStream(outPath);// 获得ClassClass<?> cls = Class.forName("android.media.AmrInputStream");// 通过Class获得所对应对象的方法Method[] methods = cls.getMethods();// 输出每个方法名fileoutputStream.write(header);Constructor<?> con = cls.getConstructor(InputStream.class);Object obj = con.newInstance(fileInputStream);for (Method method : methods) {Class<?>[] parameterTypes = method.getParameterTypes();if ("read".equals(method.getName())&& parameterTypes.length == 3) {byte[] buf = new byte[1024];int len = 0;while ((len = (int) method.invoke(obj, buf, 0, 1024)) > 0) {fileoutputStream.write(buf, 0, len);}break;}}for (Method method : methods) {if ("close".equals(method.getName())) {method.invoke(obj);break;}}fileoutputStream.close();} catch (Exception e) {e.printStackTrace();} }
2.2 通过开源库opencore进行转换,下面是jni部分
public class AmrEncoder { public enum Mode { MR475,/* 4.75 kbps */ MR515, /* 5.15 kbps */ MR59, /* 5.90 kbps */ MR67, /* 6.70 kbps */ MR74, /* 7.40 kbps */ MR795, /* 7.95 kbps */ MR102, /* 10.2 kbps */ MR122, /* 12.2 kbps */ MRDTX, /* DTX */ N_MODES /* Not Used */ } public static native void init(int dtx); public static native int encode(int mode, short[] in, byte[] out); public static native void reset(); public static native void exit(); static { System.loadLibrary("amr-codec"); }}
转换操作,里面也有解码的部分
private void wav2amr(final String inpath,final String outpath){ //Random random = new Random();//File file = new File(root + "/RawAudio.raw");// file.getAbsolutePath(),root + "/test" + random.nextInt(120) + ".amr" new Thread(new Runnable() {@Overridepublic void run() {convertAMR(inpath,outpath);}}).start(); } /** * 将wav或raw文件转换成amr * @param inpath 源文件 * @param outpath 目标文件 */ private void convertAMR(String inpath,String outpath){ try { AmrEncoder.init(0); File inFile = new File(inpath); List<short[]> armsList = new ArrayList<short[]>();FileInputStream inputStream = new FileInputStream(inFile);FileOutputStream outStream = new FileOutputStream(outpath);//写入Amr头文件outStream.write(header);int byteSize = 320; byte[] buff = new byte[byteSize];int rc = 0;while ((rc = inputStream.read(buff, 0, byteSize)) > 0) {short[] shortTemp = new short[160];//将byte[]转换成short[]ByteBuffer.wrap(buff).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortTemp); //将short[]转换成byte[] //ByteBuffer.wrap(buff).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortTemp); armsList.add(shortTemp);} for (int i = 0; i < armsList.size(); i++) {int size = armsList.get(i).length;byte[] encodedData = new byte[size*2];int len = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), armsList.get(i), encodedData);if (len>0) {byte[] tempBuf = new byte[len];System.arraycopy(encodedData, 0, tempBuf, 0, len);outStream.write(tempBuf, 0, len);}}AmrEncoder.reset();AmrEncoder.exit();outStream.close();inputStream.close();System.out.println("convert success ... "+outpath);} catch (Exception e) {e.printStackTrace();} }
3、参考资料
Android音频实时传输与播放(三):AMR硬编码与硬解码
https://github.com/kevinho/opencore-amr-android
源码下载
1 1
- Android多媒体之 wav和amr的互转
- Android多媒体之AMR
- Android录音、WAV、AMR
- wav和amr互转
- iphone开发 服务器、android、iphone音频文件播放和传输 amr和wav的转换
- 即时通讯收发语音,兼容安卓wav和amr格式互转
- iOS录音转码:amr转wav,wav转amr
- iOS 语音wav转amr amr转war三方库
- IOS音频格式之AMR和WAV互转(更新支持64位)
- iOS音频格式之AMR和WAV互转(更新支持64位)
- wav转amr以及简单调整音量
- FFMPEG 音频转换命令 wav转amr
- ios语音通讯解决方案 wav转amr
- FFMPEG 音频转换命令 wav转amr
- 讯飞文字转PCM语音 PCM转WAV WAV转AMR AMR转MP3
- (转)语音编码算法AMR NB , AMR WB 和AMR WB+的区别
- iOS 录音Wav 音频 转换 Amr ,Android 播放
- iOS 录音Wav 音频 转换 Amr ,Android 播放
- (三)hive与hive、RDBMS的集成配置与测试
- git 及 tortoisegit
- 在C++中获取内存大小
- 在android下使用i2c tools
- 安卓屏幕适配(2015终极版)
- Android多媒体之 wav和amr的互转
- Codeforces 609A USB Flash Drives 【水题】
- 程序人生(一)开始
- Ceph块存储性能预估与测试
- gradle配置
- 一道面试题 设计4个线程,其中两个每次对j增加1,另外两个对j每次减少1。循环100次。
- android中 init()
- 短信验证的实现(基于云之讯短信验证SDK)
- c语言项目感受