WebRTC with Asterisk: ICE Connected but No Audio in Browser

When integrating WebRTC with Asterisk, it’s common to encounter this scenario:

  • The call establishes successfully (ICE: connected or Connection: connected)

  • Call status shows Call connected

  • For some calls, audio works; for other calls, it doesn’t in the browser

  • Meanwhile, Asterisk records the remote party correctly

In our tests, there is no NAT issue. The problem is usually related to **browser audio playback, media stream handling.
**
const handleCall = async () => {
if (!userAgentRef.current || !phoneNumber) return;

let formattedNumber = phoneNumber.startsWith(“+”) ? phoneNumber : “+” + phoneNumber.replace(/^+?/, “”);
setPhoneNumber(formattedNumber);

const recfile = ${sipId}_${phoneNumber}_${new Date().toISOString().replace(/[:.]/g,'-')}.wav;
const callId = await createCall(sipId, formattedNumber, ‘outbound’, recfile);
setCurrentCallId(callId);

const target = UserAgent.makeURI(sip:${formattedNumber}@**SERVER_ADDRESS**);
if (!target) return;

const inviter = new Inviter(userAgentRef.current, target, {
sessionDescriptionHandlerOptions: {
constraints: { audio: true, video: false },
offerOptions: { offerToReceiveAudio: true, offerToReceiveVideo: false }
},
extraHeaders: [X-Recfile: ${recfile}]
});

sessionRef.current = inviter;

inviter.stateChange.addListener(async (state) => {
if (state === SessionState.Establishing) {
setCallStatus(‘Connecting…’);
await updateCall(callId, { status: ‘connecting’ });
} else if (state === SessionState.Established) {
setCallStatus(‘Call connected’);
setCalling(true);
setupAudioStreams(inviter);
await updateCall(callId, { status: ‘answered’, answer_time: new Date().toISOString() });
await logCallEvent(callId, ‘call_connected’);
} else if (state === SessionState.Terminated) {
setCallStatus(‘Call ended’);
setCalling(false);
sessionRef.current = null;
await updateCall(callId, { status: ‘completed’, end_time: new Date().toISOString() });
await logCallEvent(callId, ‘call_ended’);
setCurrentCallId(null);
}
});

try {
await inviter.invite();
setCallStatus(Calling ${phoneNumber}...);
await logCallEvent(callId, ‘outbound_call_initiated’, To: ${phoneNumber});
} catch (error) {
setCallStatus(Call failed: ${error.message});
setCalling(false);
await updateCall(callId, { status: ‘failed’ });
await logCallEvent(callId, ‘call_failed’, error.message);
}
};

const setupAudioStreams = (session) => {
try {
const sdh = session.sessionDescriptionHandler;
if (!sdh || !sdh.peerConnection) return;

const pc = sdh.peerConnection;

const remoteStream = new MediaStream();
pc.getReceivers().forEach(r => r.track?.kind === 'audio' && remoteStream.addTrack(r.track));
if (remoteAudioRef.current && remoteStream.getTracks().length) {
  remoteAudioRef.current.srcObject = remoteStream;
  remoteAudioRef.current.volume = volume;
  remoteAudioRef.current.play().catch(() => {});
}

const localStream = new MediaStream();
pc.getSenders().forEach(s => s.track?.kind === 'audio' && localStream.addTrack(s.track));
if (localAudioRef.current && localStream.getTracks().length) {
  localAudioRef.current.srcObject = localStream;
  localAudioRef.current.muted = true;
}

pc.onconnectionstatechange = () => setCallStatus(`Connection: ${pc.connectionState}`);
pc.oniceconnectionstatechange = () => setCallStatus(`ICE: ${pc.iceConnectionState}`);

} catch (error) {
console.error(‘Error setting up audio streams:’, error);
}
};

////////////////////
[global]
type=global
user_agent=AsteriskPBX
rtp_symmetric=yes

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060
external_media_address=**
external_signaling_address=**
local_net=10.0.0.0/16

[transport-ws]
type=transport
protocol=ws
bind=0.0.0.0:8088
external_media_address=**
external_signaling_address=**
local_net=10.0.0.0/16

[transport-wss]
type=transport
protocol=wss
bind=0.0.0.0:8089
cert_file=/etc/asterisk/keys/asterisk.crt
priv_key_file=/etc/asterisk/keys/asterisk.key
external_media_address=**
external_signaling_address=**
local_net=10.0.0.0/16

[2002]
type=endpoint
transport=transport-wss
context=from-internal
disallow=all
allow=ulaw,opus
auth=2002_auth
aors=2002
media_encryption=dtls
webrtc=yes
direct_media=no
rewrite_contact=yes
ice_support=yes
media_address=**
rtp_symmetric=yes
force_rport=yes

[2002_auth]
type=auth
auth_type=userpass
username=**
password=**

[2002]
type=aor
max_contacts=100
qualify_frequency=60

[trunk-sip]
type=endpoint
context=from-siptrunk
disallow=all
allow=ulaw,opus
aors=trunk-sip-aor
from_user=**
outbound_auth=trunk-sip-auth
direct_media=no
ice_support=yes

[trunk-sip-aor]
type=aor
contact=sip:**:5060

[trunk-sip-auth]
type=auth
auth_type=userpass
username=**
password=**

[trunk-identify]
type=identify
match=sip.**
endpoint=trunk-sip

[registration-sip]
type=registration
outbound_auth=trunk-sip-auth
server_uri=sip::5060
client_uri=sip:
@**
contact_user=**
retry_interval=60
expiration=10
auth_rejection_permanent=no

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.