记录一下 Sipdroid 适配过程中的几个小修改。
情况1:wifi 情况下正常,4G或者以太网情况下无法播放提示音
4G 情况下偶然一次呼叫时,在刚开始通话中发送了一次 dtmf,发现提示音直接播放,怀疑和流发送的相关逻辑有关,查看发送代码如下:
sipUA/src/main/java/org/sipdroid/media/RtpStreamSender.java
/** Runs it in a new Thread. */
@TargetApi(23)
public void run() {
long lastsent = 0;
if (rtp_socket == null)
return;
int seqn = 0;
long time = 0;
double p = 0;
boolean improve = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getBoolean(Settings.PREF_IMPROVE, Settings.DEFAULT_IMPROVE);
int micgain = 0;
long last_tx_time = 0;
long next_tx_delay;
long now;
running = true;
m = 1;
int dtframesize = 4;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
if (Receiver.mContext.checkSelfPermission(Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
mu = p_type.codec.samp_rate()/8000;
int min = AudioRecord.getMinBufferSize(p_type.codec.samp_rate(),
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
if (min == 640) {
if (frame_size == 960) frame_size = 320;
if (frame_size == 1024) frame_size = 160;
min = 4096*3/2;
} else if (min < 4096) {
if (min <= 2048 && frame_size == 1024) frame_size /= 2;
min = 4096*3/2;
} else if (min == 4096) {
min *= 3/2;
if (frame_size == 960) frame_size = 320;
} else {
if (frame_size == 960) frame_size = 320;
if (frame_size == 1024) frame_size = 160; // frame_size *= 2;
}
frame_rate = p_type.codec.samp_rate()/frame_size;
long frame_period = 1000 / frame_rate;
frame_rate *= 1.5;
byte[] buffer = new byte[frame_size + 12];
RtpPacket rtp_packet = new RtpPacket(buffer, 0);
rtp_packet.setPayloadType(p_type.number);
if (DEBUG)
println("Reading blocks of " + buffer.length + " bytes");
println("Sample rate = " + p_type.codec.samp_rate());
println("Buffer size = " + min);
AudioRecord record = null;
short[] lin = new short[frame_size*(frame_rate+2)];
int num,ring = 0,pos;
random = new Random();
InputStream alerting = null;
try {
alerting = Receiver.mContext.getAssets().open("alerting");
} catch (IOException e2) {
if (!Sipdroid.release) e2.printStackTrace();
}
p_type.codec.init();
while (running) {
if (changed || record == null) {
if (record != null) {
record.stop();
record.release();
if (RtpStreamReceiver.samsung) {
AudioManager am = (AudioManager) Receiver.mContext.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_CALL);
am.setMode(AudioManager.MODE_NORMAL);
}
}
changed = false;
record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, p_type.codec.samp_rate(), AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
min);
if (record.getState() != AudioRecord.STATE_INITIALIZED) {
record = null;
continue;
}
if (Build.VERSION.SDK_INT >= 16) {
RtpStreamSenderNew_SDK16.aec(record);
}
record.startRecording();
micgain = (int)(Settings.getMicGain()*10);
}
if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD) {
if (Receiver.call_state == UserAgent.UA_STATE_HOLD)
RtpStreamReceiver.restoreMode();
record.stop();
while (running && (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)) {
try {
sleep(1000);
} catch (InterruptedException e1) {
}
}
record.startRecording();
}
//DTMF change start
if (dtmf.length() != 0) {
byte[] dtmfbuf = new byte[dtframesize + 12];
RtpPacket dt_packet = new RtpPacket(dtmfbuf, 0);
dt_packet.setPayloadType(dtmf_payload_type);
dt_packet.setPayloadLength(dtframesize);
dt_packet.setSscr(rtp_packet.getSscr());
long dttime = time;
int duration;
for (int i = 0; i < 6; i++) {
time += 160;
duration = (int)(time - dttime);
dt_packet.setSequenceNumber(seqn++);
dt_packet.setTimestamp(dttime);
dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
dtmfbuf[13] = (byte)0x0a;
dtmfbuf[14] = (byte)(duration >> 8);
dtmfbuf[15] = (byte)duration;
try {
rtp_socket.send(dt_packet);
sleep(20);
} catch (Exception e1) {
}
}
for (int i = 0; i < 3; i++) {
duration = (int)(time - dttime);
dt_packet.setSequenceNumber(seqn);
dt_packet.setTimestamp(dttime);
dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
dtmfbuf[13] = (byte)0x8a;
dtmfbuf[14] = (byte)(duration >> 8);
dtmfbuf[15] = (byte)duration;
try {
rtp_socket.send(dt_packet);
} catch (Exception e1) {
}
}
time += 160; seqn++;
dtmf=dtmf.substring(1);
}
//DTMF change end
if (frame_size < 480) {
now = System.currentTimeMillis();
next_tx_delay = frame_period - (now - last_tx_time);
last_tx_time = now;
if (next_tx_delay > 0) {
try {
sleep(next_tx_delay);
} catch (InterruptedException e1) {
}
last_tx_time += next_tx_delay-sync_adj;
}
}
pos = Integer.parseInt(Build.VERSION.SDK) == 21?0:((ring+delay*frame_rate*frame_size/2)%(frame_size*(frame_rate+1)));
num = record.read(lin,pos,frame_size);
if (num <= 0)
continue;
if (!p_type.codec.isValid())
continue;
// Call recording: Save the frame to the CallRecorder.
if (call_recorder != null)
call_recorder.writeOutgoing(lin, pos, num);
if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) {
calc(lin,pos,num);
if (RtpStreamReceiver.nearend != 0 && RtpStreamReceiver.down_time == 0)
noise(lin,pos,num,p/2);
else if (nearend == 0)
p = 0.9*p + 0.1*s;
} else switch (micgain) {
case 1:
calc1(lin,pos,num);
break;
case 2:
calc2(lin,pos,num);
break;
case 10:
calc10(lin,pos,num);
break;
}
if (Receiver.call_state != UserAgent.UA_STATE_INCALL &&
Receiver.call_state != UserAgent.UA_STATE_OUTGOING_CALL && alerting != null) {
try {
if (alerting.available() < num/mu)
alerting.reset();
alerting.read(buffer,12,num/mu);
} catch (IOException e) {
if (!Sipdroid.release) e.printStackTrace();
}
if (p_type.codec.number() != 8) {
G711.alaw2linear(buffer, lin, num, mu);
num = p_type.codec.encode(lin, 0, buffer, num);
}
} else {
num = p_type.codec.encode(lin, Integer.parseInt(Build.VERSION.SDK) == 21?0:(ring%(frame_size*(frame_rate+1))), buffer, num);
}
ring += frame_size;
rtp_packet.setSequenceNumber(seqn++);
rtp_packet.setTimestamp(time);
rtp_packet.setPayloadLength(num);
now = SystemClock.elapsedRealtime();
if (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan || now-lastsent > 500)
try {
lastsent = now;
rtp_socket.send(rtp_packet);
if (m > 1 && (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan))
for (int i = 1; i < m; i++)
rtp_socket.send(rtp_packet);
} catch (Exception e) {
if (new_socket == null) {
rtp_socket.getDatagramSocket().close();
try {
if (running)
rtp_socket = new RtpSocket(new_socket = new SipdroidSocket(local_port), InetAddress
.getByName(dest_addr), dest_port);
} catch (Exception e1) {
}
}
}
if (p_type.codec.number() == 9)
time += frame_size/2;
else
time += frame_size;
if (RtpStreamReceiver.good != 0 &&
RtpStreamReceiver.loss2/RtpStreamReceiver.good > 0.01) {
if (improve && delay == 0 &&
(p_type.codec.number() == 0 || p_type.codec.number() == 8 || p_type.codec.number() == 9))
m = 2;
else
m = 1;
} else
m = 1;
}
if (Integer.parseInt(Build.VERSION.SDK) < 5)
while (RtpStreamReceiver.getMode() == AudioManager.MODE_IN_CALL)
try {
sleep(1000);
} catch (InterruptedException e) {
}
if (record != null) {
record.stop();
record.release();
}
m = 0;
p_type.codec.close();
rtp_socket.getDatagramSocket().close();
rtp_socket = null;
// Call recorder: stop recording outgoing.
if (call_recorder != null)
{
call_recorder.stopOutgoing();
call_recorder = null;
}
if (DEBUG)
println("rtp sender terminated");
}
发现在线程启动时,“RtpStreamReceiver.timeout == 0 || Receiver.on_wlan || now-lastsent > 500” 条件下会发送一个 rtp 包,此处感觉和发送 dtmf 很像,而且和 wifi 网络下提示音播放正常也对应上了,于是查看 Receiver.on_wlan 这个参数在哪里定义。
sipUA/src/main/java/org/sipdroid/sipua/ui/Receiver.java
public static boolean on_wlan = true;
public static boolean isFast(int i) {
WifiManager wm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
WifiInfo wi = wm.getConnectionInfo();
if (PreferenceManager.getDefaultSharedPreferences(mContext).getString(org.sipdroid.sipua.ui.Settings.PREF_USERNAME + (i != 0 ? i : ""), "").equals("") ||
PreferenceManager.getDefaultSharedPreferences(mContext).getString(org.sipdroid.sipua.ui.Settings.PREF_SERVER + (i != 0 ? i : ""), "").equals(""))
return false;
if (wi != null) {
if (!Sipdroid.release)
Log.i("SipUA:", "isFastWifi() " + WifiInfo.getDetailedStateOf(wi.getSupplicantState())
+ " " + wi.getIpAddress());
if (wi.getIpAddress() != 0 && (WifiInfo.getDetailedStateOf(wi.getSupplicantState()) == DetailedState.OBTAINING_IPADDR
|| WifiInfo.getDetailedStateOf(wi.getSupplicantState()) == DetailedState.CONNECTED)
|| WifiInfo.getDetailedStateOf(wi.getSupplicantState()) == DetailedState.CONNECTING) {
on_wlan = true;
if (!on_vpn())
return PreferenceManager.getDefaultSharedPreferences(mContext).getBoolean(org.sipdroid.sipua.ui.Settings.PREF_WLAN + (i != 0 ? i : ""), org.sipdroid.sipua.ui.Settings.DEFAULT_WLAN);
else
return PreferenceManager.getDefaultSharedPreferences(mContext).getBoolean(org.sipdroid.sipua.ui.Settings.PREF_VPN + (i != 0 ? i : ""), org.sipdroid.sipua.ui.Settings.DEFAULT_VPN);
}
}
// on_wlan = false;
return isFastGSM(i);
}
将其默认值修改为 true,注释置为 false 的语句,再次运行,提示音播放成功。感觉像是在通话建立成功后,需要发送消息通知给服务器通话开始。如有其他见解烦请指导我下。
情况2:编码类型为 G722,通话无法成功。
直接去掉不可用的编码类型,只保留了 PCMA 和 PCMU。
sipUA/src/main/java/org/sipdroid/codecs/Codecs.java
public class Codecs {
private static final Vector<Codec> codecs = new Vector<Codec>() {{
// add(new G722());
// add(new SILK24()); save space (until a common library for all bitrates gets available?)
// add(new SILK16());
// add(new SILK8());
add(new alaw());
add(new ulaw());
// add(new Speex());
// add(new GSM());
// add(new BV16());
}};
...