Android 手机麦克风PCM 格式 到 ULAW的转换

          公司项目中使用到了摄像头的对讲功能,由于摄像头录音格式是PCM,需要将其转换为ULAW,顾做笔记如下:

        public class CameraTalk {

private String cameralIp;
private int cameralPort=80;

private int audioSource = MediaRecorder.AudioSource.MIC;
// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
private static int sampleRateInHz = 8000;
// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
private static int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
// 缓冲区字节大小
private int bufferSizeInBytes = 0;

private AudioRecord audioRecord;



private boolean isTalk = false;// 设置正在录制的状态

private AudioSendThread audioSendThread=null;

public static CameraTalk talker=null;

public static CameraTalk getInstance()
{
if(talker==null)
{
talker = new CameraTalk();
}
return talker;
}

public void talk()
{
bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,
channelConfig, audioFormat);
audioRecord = new AudioRecord(audioSource, sampleRateInHz,
channelConfig, audioFormat, bufferSizeInBytes);
audioRecord.startRecording();
// 让录制状态为true
isTalk = true;
// 开启音频文件写入线程
audioSendThread = new AudioSendThread();
audioSendThread.start();
}

public String getCameralIp() {
return cameralIp;
}


public void setCameralIp(String cameralIp) {
this.cameralIp = cameralIp;
}


public int getCameralPort() {
return cameralPort;
}


public CameraTalk() {
super();
}


public CameraTalk(String cameralIp, int cameralPort) {
super();
this.cameralIp = cameralIp;
this.cameralPort = cameralPort;
}


public void setCameralPort(int cameralPort) {
this.cameralPort = cameralPort;
}

public void stopTalk()
{
isTalk=false;
if(audioSendThread!=null)
{
try{audioSendThread.interrupt();}catch(Exception ex){}
}
}

class AudioSendThread extends Thread 
{
@Override
public void run() 
{
OutputStream os = null;
Socket socket = null;
try
{
socket = new Socket(cameralIp, cameralPort);
os = socket.getOutputStream();
String header = "POST /cgi-bin/instream.cgi HTTP/1.1\r\n"
+ "Content-Type: audio/basic\r\n"
+ "Cache-Control: no-cache\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; )\r\n"
+ "Content-Length:30000000\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Cookie: NetworkCamera_Volume=100\r\n"
+ "Authorization: Basic YWRtaW46YWRtaW4=\r\n" + "\r\n\r\n";
os.write(header.getBytes());
os.flush();
} catch (Exception e1) {
e1.printStackTrace();
return;
}


byte[] audiodata = new byte[bufferSizeInBytes];
int readsize = 0;


while (isTalk == true)
{
readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
if (AudioRecord.ERROR_INVALID_OPERATION != readsize) {
InputStream is = new ByteArrayInputStream(audiodata);
UlawEncoderInputStream uis=null;
try {
uis = new UlawEncoderInputStream(is,0);
byte buff[] = new byte[1024];
int len = uis.read(buff);
while (len > 0) {
os.write(buff, 0, len);
os.flush();
len = uis.read(buff);
}
} catch (Exception e) {
}
finally
{
try {uis.close();} catch (Exception e) {}
}


}
}

try 
{
os.close();
socket.close();
} catch (IOException e) {
}
}
}




}





/**
 * InputStream which transforms 16 bit pcm data to ulaw data.
 * 
 * Not yet ready to be supported, so
 * @hide
 */
public final class UlawEncoderInputStream extends InputStream {
    private final static String TAG = "UlawEncoderInputStream";
    
    private final static int MAX_ULAW = 8192;
    private final static int SCALE_BITS = 16;
    
    private InputStream mIn;
    
    private int mMax = 0;
    
    private final byte[] mBuf = new byte[1024];
    private int mBufCount = 0; // should be 0 or 1
    
    private final byte[] mOneByte = new byte[1];


    
    public static void encode(byte[] pcmBuf, int pcmOffset,
            byte[] ulawBuf, int ulawOffset, int length, int max) {
        
        // from  'ulaw' in wikipedia
        // +8191 to +8159                          0x80
        // +8158 to +4063 in 16 intervals of 256   0x80 + interval number
        // +4062 to +2015 in 16 intervals of 128   0x90 + interval number
        // +2014 to  +991 in 16 intervals of  64   0xA0 + interval number
        //  +990 to  +479 in 16 intervals of  32   0xB0 + interval number
        //  +478 to  +223 in 16 intervals of  16   0xC0 + interval number
        //  +222 to   +95 in 16 intervals of   8   0xD0 + interval number
        //   +94 to   +31 in 16 intervals of   4   0xE0 + interval number
        //   +30 to    +1 in 15 intervals of   2   0xF0 + interval number
        //     0                                   0xFF
        
        //    -1                                   0x7F
        //   -31 to    -2 in 15 intervals of   2   0x70 + interval number
        //   -95 to   -32 in 16 intervals of   4   0x60 + interval number
        //  -223 to   -96 in 16 intervals of   8   0x50 + interval number
        //  -479 to  -224 in 16 intervals of  16   0x40 + interval number
        //  -991 to  -480 in 16 intervals of  32   0x30 + interval number
        // -2015 to  -992 in 16 intervals of  64   0x20 + interval number
        // -4063 to -2016 in 16 intervals of 128   0x10 + interval number
        // -8159 to -4064 in 16 intervals of 256   0x00 + interval number
        // -8192 to -8160                          0x00
        
        // set scale factors
        if (max <= 0) max = MAX_ULAW;
        
        int coef = MAX_ULAW * (1 << SCALE_BITS) / max;
        
        for (int i = 0; i < length; i++) {
            int pcm = (0xff & pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] << 8);
            pcm = (pcm * coef) >> SCALE_BITS;
            
            int ulaw;
            if (pcm >= 0) {
                ulaw = pcm <= 0 ? 0xff :
                        pcm <=   30 ? 0xf0 + ((  30 - pcm) >> 1) :
                        pcm <=   94 ? 0xe0 + ((  94 - pcm) >> 2) :
                        pcm <=  222 ? 0xd0 + (( 222 - pcm) >> 3) :
                        pcm <=  478 ? 0xc0 + (( 478 - pcm) >> 4) :
                        pcm <=  990 ? 0xb0 + (( 990 - pcm) >> 5) :
                        pcm <= 2014 ? 0xa0 + ((2014 - pcm) >> 6) :
                        pcm <= 4062 ? 0x90 + ((4062 - pcm) >> 7) :
                        pcm <= 8158 ? 0x80 + ((8158 - pcm) >> 8) :
                        0x80;
            } else {
                ulaw = -1 <= pcm ? 0x7f :
                          -31 <= pcm ? 0x70 + ((pcm -   -31) >> 1) :
                          -95 <= pcm ? 0x60 + ((pcm -   -95) >> 2) :
                         -223 <= pcm ? 0x50 + ((pcm -  -223) >> 3) :
                         -479 <= pcm ? 0x40 + ((pcm -  -479) >> 4) :
                         -991 <= pcm ? 0x30 + ((pcm -  -991) >> 5) :
                        -2015 <= pcm ? 0x20 + ((pcm - -2015) >> 6) :
                        -4063 <= pcm ? 0x10 + ((pcm - -4063) >> 7) :
                        -8159 <= pcm ? 0x00 + ((pcm - -8159) >> 8) :
                        0x00;
            }
            ulawBuf[ulawOffset++] = (byte)ulaw;
        }
    }
    
    /**
     * Compute the maximum of the absolute value of the pcm samples.
     * The return value can be used to set ulaw encoder scaling.
     * @param pcmBuf array containing 16 bit pcm data.
     * @param offset offset of start of 16 bit pcm data.
     * @param length number of pcm samples (not number of input bytes)
     * @return maximum abs of pcm data values
     */
    public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) {
        int max = 0;
        for (int i = 0; i < length; i++) {
            int pcm = (0xff & pcmBuf[offset++]) + (pcmBuf[offset++] << 8);
            if (pcm < 0) pcm = -pcm;
            if (pcm > max) max = pcm;
        }
        return max;
    }


    /**
     * Create an InputStream which takes 16 bit pcm data and produces ulaw data.
     * @param in InputStream containing 16 bit pcm data.
     * @param max pcm value corresponding to maximum ulaw value.
     */
    public UlawEncoderInputStream(InputStream in, int max) {
        mIn = in;
        mMax = max;
    }
    
    @Override
    public int read(byte[] buf, int offset, int length) throws IOException {
        if (mIn == null) throw new IllegalStateException("not open");


        // return at least one byte, but try to fill 'length'
        while (mBufCount < 2) {
            int n = mIn.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
            if (n == -1) return -1;
            mBufCount += n;
        }
        
        // compand data
        int n = Math.min(mBufCount / 2, length);
        encode(mBuf, 0, buf, offset, n, mMax);
        
        // move data to bottom of mBuf
        mBufCount -= n * 2;
        for (int i = 0; i < mBufCount; i++) mBuf[i] = mBuf[i + n * 2];
        
        return n;
    }
    
    @Override
    public int read(byte[] buf) throws IOException {
        return read(buf, 0, buf.length);
    }
    
    @Override
    public int read() throws IOException {
        int n = read(mOneByte, 0, 1);
        if (n == -1) return -1;
        return 0xff & (int)mOneByte[0];
    }
    
    @Override
    public void close() throws IOException {
        if (mIn != null) {
            InputStream in = mIn;
            mIn = null;
            in.close();
        }
    }
    
    @Override
    public int available() throws IOException {
        return (mIn.available() + mBufCount) / 2;
    }
}

你可能感兴趣的:(Android 手机麦克风PCM 格式 到 ULAW的转换)