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
原创粉丝点击