公司项目中使用到了摄像头的对讲功能,由于摄像头录音格式是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;
}
}