android中具有固定数据报包长度的datagramsocket中的音频流质量不好
Posted
技术标签:
【中文标题】android中具有固定数据报包长度的datagramsocket中的音频流质量不好【英文标题】:Audio streaming quality not good in datagramsocket with fixed datagram packets length in android 【发布时间】:2016-10-31 09:37:35 【问题描述】:我使用数据报套接字通过数据报包传输音频以进行电话对话。我可以听到另一端的音频。但是另一端听不到我的声音。
这里的数据包固定为340byte。前 20 个字节用于连接建立、源地址、目的地址等。接下来的 320 个字节是音频数据。这里音频数据总是固定为 320 字节。
private static final int SAMPLE_INTERVAL = 20;
private static final int SAMPLE_SIZE = 2;
private static final int BUF_SIZE = SAMPLE_INTERVAL * SAMPLE_INTERVAL * SAMPLE_SIZE * 2; //1600Bytes
// the audio recording options
private static final int RECORDING_RATE = 8000; // Hertz
private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;//16
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;//2
AudioRecord audioRecorder = new AudioRecord(MediaRecorder.Audiosource.MIC,
RECORDING_RATE, CHANNEL, FORMAT, BUF_SIZE * 10);
//音频传输线程
@Override
public void run()
long seq=0;
byte[] buffer = new byte[BUF_SIZE];
WiphoneProp callProp=new WiphoneProp();
callProp.setOpCode(WiPhonePacket.OpCodes.Call.getValue());//1 byte
callProp.setOperand(WiPhonePacket.OperandCodes.Talk.getValue());//1 byte
callProp.setSelfState(0);//1 byte
callProp.setCallId(1);//1 byte
callProp.setCheckSum(BitConverter.checkSum(deviceSignature.getSignatureData(),0,deviceSignature.getSignatureData().length));//4 byte
try
int offset=0;
audioRecorder.startRecording();
while (mic)
// Capture audio from the mic and transmit it
try
callProp.setWiphoneId(ByteBuffer.wrap(getBroadcastQuadIp().getAddress()).order(ByteOrder.LITTLE_ENDIAN).getInt());//4 byte
callProp.setClientId(ByteBuffer.wrap(getLocalIp().getAddress()).order(ByteOrder.LITTLE_ENDIAN).getInt());//4 byte
catch (IOException e)
e.printStackTrace();
callProp.setSequence(seq);//4 byte
byte[] buff=new byte[320];
audioRecorder.read(buffer, offset, 320);
System.arraycopy(buffer,offset,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
byte[] voice = callProp.ToBuffer();
DatagramPacket packet = new DatagramPacket(voice, voice.length, InetAddress.getByName(deviceSignature.getDeviceIP()), WIPHONEPORT);
commonSoc.send(packet);
offset+=320;
if(offset>=1600)
offset=0;
seq++;
// Stop recording and release resources
audioRecorder.stop();
audioRecorder.release();
mic = false;
/* catch(InterruptedException e)
Log.e(TAG, "InterruptedException: " + e.toString());
mic = false;
*/
catch(SocketException e)
Log.e(TAG, "SocketException: " + e.toString());
mic = false;
catch(UnknownHostException e)
Log.e(TAG, "UnknownHostException: " + e.toString());
mic = false;
catch(IOException e)
Log.e(TAG, "IOException: " + e.toString());
mic = false;
现在我的音频缓冲区大小为 1600 字节。 另一端没有音频。
我推荐了this
这是我的 c# 代码,用于获取 320 字节的音频流
CaptureDevicesCollection captureDeviceCollection = new CaptureDevicesCollection();
DeviceInformation deviceInfo = captureDeviceCollection[0];
capture = new Capture(deviceInfo.DriverGuid);
short channels = 1; //Stereo.
short bitsPerSample = 16; //16Bit, alternatively use 8Bits.
int samplesPerSecond = 8000;//Default: 22050; //11KHz use 11025 , 22KHz use 22050, 44KHz use 44100 etc.
//Set up the wave format to be captured.
waveFormat = new WaveFormat();
waveFormat.Channels = channels;
waveFormat.FormatTag = WaveFormatTag.Pcm;
waveFormat.SamplesPerSecond = samplesPerSecond;
waveFormat.BitsPerSample = bitsPerSample;
waveFormat.BlockAlign = (short)(channels * (bitsPerSample / (short)8));
waveFormat.AverageBytesPerSecond = waveFormat.BlockAlign * samplesPerSecond;
captureBufferDescription = new CaptureBufferDescription();
captureBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5; //Approx 200 milliseconds of PCM data.
captureBufferDescription.Format = waveFormat;
playbackBufferDescription = new BufferDescription();
playbackBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5;
playbackBufferDescription.Format = waveFormat;
playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device);
bufferSize = captureBufferDescription.BufferBytes;
//// 获取 320 字节音频数据的逻辑
try
//The following lines get audio from microphone and then send them
//across network.
captureBuffer = new CaptureBuffer(captureBufferDescription, capture);
CreateNotifyPositions();
int blockSize = bufferSize / 10;
int halfBuffer = bufferSize / 2;
captureBuffer.Start(true);
//bool readFirstBufferPart = true;
int offset = 0;
MemoryStream memStream = new MemoryStream(halfBuffer);
bStop = false;
uint seq = 0;
while (!bStop)
autoResetEvent.WaitOne();
this.Invoke(new Action(() =>
listBox1.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff"));
));
offset = (int)(((seq + 5) % 10) * blockSize);
halfBuffer = blockSize;
memStream.Seek(0, SeekOrigin.Begin);
captureBuffer.Read(offset, memStream, halfBuffer, LockFlag.None);
//readFirstBufferPart = !readFirstBufferPart;
//offset = readFirstBufferPart ? 0 : halfBuffer;
byte[] dataToWrite = memStream.GetBuffer();
var voicePacket = new WiPhonePacket
OpCode = (byte)WiPhonePacket.OpCodes.Call,
Operand = (byte)WiPhonePacket.OperandCodes.Talk,
SelfState = 0,
CallId = 1,
WiPhoneId = broadcastIp,
ClientId = localIp,
CheckSum = Helpers.General.CalculateChecksum(Configurations.Signature.SignatureData, 0, Configurations.Signature.SignatureData.Length),
Sequence = seq++
;
voicePacket.SetVoice(dataToWrite);
var voiceBuffer = voicePacket.ToBuffer();
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(Configurations.Signature.DeviceIP), 2739);
clientSocket.SendTo(voiceBuffer, ep);
//udpClient.Send(dataToWrite, dataToWrite.Length, otherPartyIP.Address.ToString(), 2739);
catch (Exception ex)
MessageBox.Show(ex.Message, "VoiceChat-Send ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
finally
captureBuffer.Stop();
captureBuffer.Dispose();
captureBuffer = null;
autoResetEvent.Dispose();
autoResetEvent = null;
notify.Dispose();
notify = null;
//Increment flag by one.
nUdpClientFlag += 1;
//When flag is two then it means we have got out of loops in Send and Receive.
//while (nUdpClientFlag != 2)
//
//Clear the flag.
nUdpClientFlag = 0;
//Close the socket.
//udpClient.Close();
private void CreateNotifyPositions()
try
autoResetEvent = new AutoResetEvent(false);
notify = new Notify(captureBuffer);
BufferPositionNotify[] nots = new BufferPositionNotify[10];
for (int i = 0; i < 10; i++)
var bufferPositionNotify1 = new BufferPositionNotify();
bufferPositionNotify1.Offset = (i + 1) * (bufferSize / 10) - 1;
bufferPositionNotify1.EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();
nots[i] = bufferPositionNotify1;
notify.SetNotificationPositions(nots);
catch (Exception ex)
MessageBox.Show(ex.Message, "VoiceChat-CreateNotifyPositions ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
【问题讨论】:
我看到您的评论“从麦克风捕获音频”,但我没有看到相应的代码,例如读()。我错过了什么吗? @HartmutPfitzinger 谢谢你的回复。它的复制粘贴错误。我更新了我的帖子,请检查一下。 【参考方案1】:感谢您添加源代码的缺失部分。这非常有用,因为它包含最明显的错误:
if(i==0)
bytes_read = audioRecorder.read(buffer, 0, 319);
System.arraycopy(buffer,0,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
else
bytes_read = audioRecorder.read(buffer, 320, 639);
System.arraycopy(buffer,320,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
audioRecorder.read()
将要读取的字节数作为第三个参数(而不是要写入的数组的最后一个元素的索引)。所以你应该把代码改成:
if(i==0)
bytes_read = audioRecorder.read(buffer, 0, 320);
System.arraycopy(buffer,0,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
else
bytes_read = audioRecorder.read(buffer, 320, 320);
System.arraycopy(buffer,320,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
之后音质应该会好很多,但不要期望太高:您的采样率只有 8kHz,这意味着可能的最高频率低于 4kHz,这与旧电话质量差不多。
【讨论】:
谢谢。之前缓冲区大小为 640 字节,所以我分两部分发送音频数据,但现在我更改为 1600 字节(半缓冲区),就像在 C# 中一样。无论如何,我会试试这个,让你知道状态。 实际上缓冲区大小因设备而异,有些设备有 640,有些设备有 1024。所以我创建了 1600 字节的常量缓冲区并读取固定 320 字节的音频数据。我更新了我的代码。请检查一下 更好。那么现在的声音呢? 现在没有声音了 我没有看到mic
in 设置为 true
的行 while (mic)
【参考方案2】:
我解决了以下问题
private static final int SAMPLE_INTERVAL = 20; // Milliseconds
private static final int SAMPLE_SIZE = 2; // Bytes
private static final int BUF_SIZE = SAMPLE_INTERVAL * SAMPLE_INTERVAL * SAMPLE_SIZE * 2;
private static final int RECORDING_RATE = 8000; // Hertz
private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;//16
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;//2
audioRecorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,
RECORDING_RATE, CHANNEL, FORMAT,BUF_SIZE*10);
@Override
public void run()
// Create an instance of the AudioRecord class
long seq=0;
int offset=0;
byte[] buffer = new byte[BUF_SIZE];
try
// Create a socket and start recording
Log.i(TAG, "Packet destination: " + deviceSignature.getDeviceIP()+ " "+BUF_SIZE);
audioRecorder.startRecording();
while (mic)
// Capture audio from the mic and transmit it
WiphoneProp callProp=new WiphoneProp();
callProp.setOpCode(WiPhonePacket.OpCodes.Call.getValue());
callProp.setOperand(WiPhonePacket.OperandCodes.Talk.getValue());
callProp.setSelfState(0);
callProp.setCallId(1);
callProp.setCheckSum(BitConverter.checkSum(deviceSignature.getSignatureData(),0,deviceSignature.getSignatureData().length));
try
callProp.setWiphoneId(ByteBuffer.wrap(getBroadcastQuadIp().getAddress()).order(ByteOrder.LITTLE_ENDIAN).getInt());//broadcast ip or wiphone ip
callProp.setClientId(ByteBuffer.wrap(getLocalIp().getAddress()).order(ByteOrder.LITTLE_ENDIAN).getInt());//device ip or local ip
catch (IOException e)
e.printStackTrace();
callProp.setSequence(seq);
byte[] buff=new byte[320];
audioRecorder.read(buffer, offset, buff.length);
System.arraycopy(buffer,offset,buff,0,buff.length);
callProp.setVoice(buff);//320 byte
byte[] voice = callProp.ToBuffer();
DatagramPacket packet = new DatagramPacket(voice, voice.length, InetAddress.getByName(deviceSignature.getDeviceIP()), WIPHONEPORT);
commonSoc.send(packet);
seq++;
offset+=320;
if(offset>=1600)
offset=0;
// Stop recording and release resources
audioRecorder.stop();
audioRecorder.release();
mic = false;
/* catch(InterruptedException e)
Log.e(TAG, "InterruptedException: " + e.toString());
mic = false;
*/
catch(SocketException e)
Log.e(TAG, "SocketException: " + e.toString());
mic = false;
catch(UnknownHostException e)
Log.e(TAG, "UnknownHostException: " + e.toString());
mic = false;
catch(IOException e)
Log.e(TAG, "IOException: " + e.toString());
mic = false;
【讨论】:
以上是关于android中具有固定数据报包长度的datagramsocket中的音频流质量不好的主要内容,如果未能解决你的问题,请参考以下文章