该文章面向对WebRTC有一定基础的码农,如果完全没了解过WebRTC的朋友,可以点赞并私信我进行一对一辅导哦。
因为项目需要直播功能,所以用到了WebRTC,但是由于项目本身有一个特殊功能需要一直使用麦克风,而此时直播无法传输声音。经过一番定位后,应该是该特殊功能在底层直接占用了声卡的驱动,导致WebRTC去实例化AudioRecord时卡住,具体卡住的位置是:
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int sessionId) throws IllegalArgumentException {
......
int initResult = native_setup( new WeakReference<AudioRecord>(this),
mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
......
}
因此如果要该特殊功能和直播都能正常使用,就需要改变WebRTC的音频输入源为从特殊功能获取到的音频PCM裸流。
既然要改变音频输入源,那么首先找一下WebRTC有没有相关接口,找了一圈发现没有,无奈只能继续深挖WebRTC究竟是在哪里采集音频数据。
最终找到了org.webrtc.audio.WebRtcAudioRecord.java这个类(具体的音频采集流程参考:https://blog.piasy.com/2018/09/14/WebRTC-ADM/index.html#%E9%9F%B3%E9%A2%91%E9%87%87%E9%9B%86)。
大致流程就是:
到这里,思路就明确了,关键核心就是在这个ByteBuffer,要改变音频输入源,只需要把音频数据填充到这个ByteBuffer就可以了。
既然找到了思路,那么我们就开始对WebRTC的SDK进行改造,首先目前要修改的目标已经很明确了,就是org.webrtc.audio.WebRtcAudioRecord.java这个类,那么首先就聊聊要怎么把这个类替换成自己的代码:
通过上述步骤,我们就成功把WebRTC-Android SDK中的org.webrtc.audio.WebRtcAudioRecord类替换成自己的代码了。
然后我们就改里面的代码就好了,下面是核心代码:
class WebRtcAudioRecord {
......
// 自己的音频输入源
private byte[] mAudioData;
// 特殊功能回调音频数据的回调方法
@Override
public void onAudioData(byte[] audioData) {
this.mAudioData = audioData;
}
private class AudioRecordThread extends Thread {
private volatile boolean keepAlive = true;
public AudioRecordThread(String name) {
super(name);
}
public void run() {
Process.setThreadPriority(-19);
Logging.d("WebRtcAudioRecordExternal", "AudioRecordThread" + WebRtcAudioUtils.getThreadInfo());
WebRtcAudioRecord.this.doAudioRecordStateCallback(0);
// 开始接收特殊功能的音频数据
RobotSDK.getInstance().setMediaDataCallback(WebRtcAudioRecord.this);
byte[] feedingAudioData = null;
while (this.keepAlive) {
while (feedingAudioData == mAudioData || mAudioData == null) {
// wait for new audio data
}
feedingAudioData = mAudioData;
byteBuffer.clear();
int offset = 0;
int length;
// 因为从特殊功能接收到的音频字节数组的长度固定是1024,而byteBuffer的长度是320
// 所以要做个循环分段把数据填充给byteBuffer
while (offset < feedingAudioData.length) {
length = Math.min(byteBuffer.capacity(), feedingAudioData.length - offset);
byteBuffer.position(0);
if (microphoneMute) {
byteBuffer.clear();
byteBuffer.put(emptyBytes);
} else {
// 填充音频数据给byteBuffer
byteBuffer.put(Arrays.copyOfRange(feedingAudioData, offset, length + offset));
}
offset += length;
if (this.keepAlive) {
// 把byteBuffer的数据传递给native层
nativeDataIsRecorded(nativeAudioRecord, length);
}
if (audioSamplesReadyCallback != null) {
byte[] data = Arrays.copyOfRange(byteBuffer.array(),
byteBuffer.arrayOffset(), byteBuffer.capacity() + byteBuffer.arrayOffset());
audioSamplesReadyCallback.onWebRtcAudioRecordSamplesReady(
new JavaAudioDeviceModule.AudioSamples(audioFormat, audioChannelCount, audioSampleRate, data));
}
}
}
WebRtcAudioRecord.this.byteBuffer = null;
WebRtcAudioRecord.this.doAudioRecordStateCallback(1);
// 停止接收特殊功能的音频数据
RobotSDK.getInstance().setMediaDataCallback(null);
}
public void stopThread() {
Logging.d("WebRtcAudioRecordExternal", "stopThread");
this.keepAlive = false;
}
}
......
}
完美结束~