اگر WebRTC وصل میشود اما صدا ندارید، از روی «Registered» یا «Call Connected» نتیجه نگیرید که Media سالم است. در WebRTC مسیر Signaling و مسیر صدا از هم جدا هستند. ممکن است SIP روی WSS کاملاً سالم باشد، تماس Answer شود و حتی Timer تماس جلو برود، اما ICE هیچ مسیر قابلاستفادهای پیدا نکرده باشد، TURN کار نکند، DTLS Handshake شکست بخورد یا RTP/SRTP اصلاً به مقصد نرسد.
برای عیبیابی درست باید از حدسزدن فاصله بگیرید و Media Path را مرحلهبهمرحله بررسی کنید: Candidateها → ICE Candidate Pair → TURN → DTLS → SRTP/RTP → Audio Track. این مقاله دقیقاً همین مسیر را دنبال میکند تا مشخص شود صدا در کدام نقطه متوقف شده است.
این راهنما برای WebRTC عمومی نوشته شده، اما مثالهای سمت PBX بر پایه Asterisk/PJSIP هستند. در اکتبر ۲۰۲۶، آخرین نسخه LTS عمومی Asterisk برابر 22.11.0 است و شاخه 22 همچنان Fully Supported است.
قاعده طلایی: اگر ICE وصل نشده، هنوز سراغ Codec نروید. اگر ICE وصل شده ولی DTLS در وضعیت Failed است، مشکل RTP Port نیست. اگر DTLS Connected است ولی packetsReceived افزایش پیدا نمیکند، مسیر Media یا Firewall/NAT را بررسی کنید. عیبیابی WebRTC باید لایهبهلایه باشد.
اول مشخص کنید «بدون صدا» دقیقاً چه نوع مشکلی است
همه خطاهای WebRTC no audio یکسان نیستند. قبل از بازکردن Firewall یا تغییر TURN، نوع خرابی را مشخص کنید.
| نشانه | احتمال بیشتر |
|---|---|
| هیچکدام صدای طرف مقابل را ندارند | ICE، TURN، DTLS، Firewall یا Media Negotiation |
| فقط یک سمت صدا دارد | NAT، Candidate اشتباه، RTP Route یا Firewall یکطرفه |
| تماس برقرار است ولی Browser هیچ RTP دریافت نمیکند | مسیر Media، TURN، PBX یا NAT |
| RTP دریافت میشود ولی صدا پخش نمیشود | Audio Track، Autoplay، Output Device یا Codec/Decode |
| فقط روی شبکه شرکتی/موبایل خراب است | TURN، UDP Blocking، NAT سخت یا Firewall |
| ICE Connected است ولی Media شروع نمیشود | DTLS-SRTP یا SDP/Fingerprint |
مسیر واقعی صدا در WebRTC را بشناسید
وقتی Browser با Asterisk یا هر WebRTC Gateway تماس میگیرد، چند اتفاق مستقل رخ میدهد:
1. Signaling
Browser ---- WSS/SIP ----> Asterisk
2. ICE
host / srflx / prflx / relay candidates
|
v
selected candidate pair
3. Security
DTLS handshake
|
v
SRTP keys
4. Media
Browser <==== SRTP/RTP ====> PBX / Media Proxy
WebRTC.org توضیح میدهد که ICE از STUN یا TURN برای پیدا کردن Candidateهای اتصال استفاده میکند. بنابراین موفق بودن Signaling بهتنهایی هیچ تضمینی برای برقراری Media نیست.
مرحله ۱: Candidateهای ICE را ببینید؛ host، srflx، prflx یا relay؟
طبق WebRTC API، Candidateها چهار نوع مهم دارند:
| نوع | معنی | اگر دیده نشود چه چیزی را بررسی کنیم؟ |
|---|---|---|
host |
آدرس محلی خود Endpoint | Interface و Browser Policy |
srflx |
Public Mapping کشفشده با STUN | STUN، UDP و NAT |
prflx |
Peer Reflexive Candidate کشفشده هنگام Connectivity Check | معمولاً خود ICE آن را مدیریت میکند |
relay |
آدرس Relay گرفتهشده از TURN | TURN Auth، Port، Firewall و Relay Range |
MDN Candidateهای relay را Candidateهایی معرفی میکند که از TURN Server گرفته میشوند. بنابراین اگر انتظار دارید TURN کار کند ولی هیچ relay Candidate ندارید، هنوز Media اصلاً از TURN عبور نمیکند.
در کد Browser میتوانید Candidateهای محلی را Log کنید:
pc.addEventListener('icecandidate', (event) => {
if (!event.candidate) {
console.log('ICE gathering complete');
return;
}
console.log({
type: event.candidate.type,
protocol: event.candidate.protocol,
address: event.candidate.address,
port: event.candidate.port
});
});
مرحله ۲: فقط Candidate داشتن کافی نیست؛ Candidate Pair انتخابشده را پیدا کنید
ممکن است Browser چند Candidate داشته باشد اما هیچ Pair موفقی بین دو سمت انتخاب نشود. WebRTC تا زمانی که Connectivity Checkها یک مسیر قابلاستفاده پیدا نکنند، Media پایدار نخواهد داشت.
وضعیت عمومی ICE را Log کنید:
pc.addEventListener('iceconnectionstatechange', () => {
console.log('ICE:', pc.iceConnectionState);
});
pc.addEventListener('connectionstatechange', () => {
console.log('PeerConnection:', pc.connectionState);
});
وضعیتهای مهم:
🔹 checking: هنوز Pair مناسب پیدا نشده است.
🔹 connected یا completed: حداقل یک مسیر ICE کار میکند.
🔹 failed: Connectivity Checkها مسیر سالم پیدا نکردهاند.
🔹 disconnected: مسیر قبلی موقتاً از دست رفته است.
برای دیدن Pair واقعی از getStats() استفاده کنید:
async function showSelectedCandidatePair(pc) {
const stats = await pc.getStats();
for (const report of stats.values()) {
if (
report.type === 'candidate-pair' &&
report.state === 'succeeded' &&
report.nominated
) {
const local = stats.get(report.localCandidateId);
const remote = stats.get(report.remoteCandidateId);
console.log('Selected ICE pair:', {
localType: local?.candidateType,
localProtocol: local?.protocol,
localAddress: local?.address,
remoteType: remote?.candidateType,
remoteProtocol: remote?.protocol,
remoteAddress: remote?.address,
currentRoundTripTime: report.currentRoundTripTime
});
}
}
}
showSelectedCandidatePair(pc);
مرحله ۳: بفهمید TURN واقعاً استفاده میشود یا فقط در Config نوشته شده است
وجود URL مربوط به TURN در JavaScript ثابت نمیکند TURN سالم است. TURN باید Authentication موفق داشته باشد، Relay Allocation بسازد و Browser بتواند relay Candidate دریافت کند.
WebRTC.org توضیح میدهد TURN زمانی لازم میشود که اتصال مستقیم بین Peerها ممکن نباشد. در شبکههای سازمانی سختگیر، Carrier-grade NAT یا شبکههایی که UDP مستقیم محدود شده است، TURN میتواند تنها مسیر قابلاستفاده باشد.
برای تست تشخیصی، موقتاً Browser را مجبور کنید فقط Relay Candidate استفاده کند:
const pc = new RTCPeerConnection({
iceTransportPolicy: 'relay',
iceServers: [
{
urls: [
'turn:turn.example.com:3478?transport=udp',
'turn:turn.example.com:3478?transport=tcp'
],
username: 'test-user',
credential: 'TURN_PASSWORD'
}
]
});
این تنظیم برای تشخیص است. اگر با relay اجباری تماس اصلاً ICE Connected نمیشود، TURN را بررسی کنید. اگر با TURN اجباری صدا دارید ولی در حالت عادی One-way Audio دارید، مسیر Direct/NAT مشکلدار است.
در Coturn چه چیزهایی را بررسی کنیم؟
Coturn یک TURN/STUN Server متنباز است و مستند رسمی آن Port پیشفرض 3478 برای Listener عادی و 5349 برای TLS را ذکر میکند. Relay Port Range نیز باید مطابق Config خودتان در Firewall باز باشد.
وضعیت سرویس:
systemctl status coturn
journalctl -u coturn -f
نمونه Config محدودکردن Relay Range:
listening-port=3478
tls-listening-port=5349
min-port=49160
max-port=49260
اگر این Range را خودتان تغییر دادهاید، Firewall هم باید دقیقاً با همان Range هماهنگ باشد.
مرحله ۴: ICE Connected است؛ حالا DTLS را بررسی کنید
بعد از انتخاب Candidate Pair، WebRTC باید DTLS Handshake را انجام دهد تا کلیدهای SRTP ایجاد شوند. اگر ICE موفق است ولی DTLS در وضعیت failed بماند، Media امن شروع نمیشود.
از ۲۰۲۶، RTCTransportStats.dtlsState در مرورگرهای جدید بهصورت گستردهتری قابلاستفاده است. مقدارهای اصلی آن new، connecting، connected، closed و failed هستند.
Transport را از Stats بخوانید:
async function showTransport(pc) {
const stats = await pc.getStats();
for (const report of stats.values()) {
if (report.type === 'transport') {
console.log({
iceState: report.iceState,
dtlsState: report.dtlsState,
dtlsRole: report.dtlsRole,
dtlsCipher: report.dtlsCipher,
srtpCipher: report.srtpCipher,
packetsSent: report.packetsSent,
packetsReceived: report.packetsReceived
});
}
}
}
showTransport(pc);
اگر مرورگر قدیمی بعضی Propertyها را نشان نمیدهد، این موضوع لزوماً نشانه خرابی نیست؛ پشتیبانی برخی فیلدهای Stats بین Browserها متفاوت است.
دلایل رایج شکست DTLS
🔹 Fingerprint در SDP دو طرف درست نیست.
🔹 یک سمت DTLS-SRTP و سمت دیگر RTP ساده انتظار دارد و Media Gateway مناسب وجود ندارد.
🔹 ICE Pair انتخاب شده اما UDP Packetهای DTLS روی همان مسیر Block میشوند.
🔹 Endpoint/PBX با Profile مورد انتظار WebRTC مثل AVPF/RTCP-MUX هماهنگ نیست.
🔹 SDP در Proxy یا SBC بهاشتباه Rewrite شده است.
مرحله ۵: اگر Asterisk دارید، تنظیمات WebRTC Endpoint را کنترل کنید
در Asterisk، گزینه webrtc=yes مجموعهای از تنظیمات لازم را فعال میکند؛ از جمله ICE، DTLS، AVPF، received transport و RTCP-MUX. بنابراین اگر Endpoint فقط یک PJSIP داخلی معمولی باشد، ممکن است REGISTER انجام شود اما Media WebRTC درست Negotiation نشود.
نمونه:
[1001-web]
type=endpoint
aors=1001-web
auth=1001-web
webrtc=yes
disallow=all
allow=opus,ulaw
وضعیت Endpoint:
asterisk -rx "pjsip show endpoint 1001-web"
مستند رسمی Asterisk توضیح میدهد webrtc=yes تنظیماتی مثل media_encryption=dtls، dtls_verify=fingerprint، ice_support=yes و rtcp_mux=yes را فعال میکند.
مرحله ۶: DTLS Connected است ولی هنوز صدا ندارید؛ RTP را اندازه بگیرید
حالا باید ببینید Packet صوتی واقعاً وارد Browser میشود یا نه. حدس نزنید؛ از getStats() استفاده کنید.
برای Inbound Audio:
async function showInboundAudio(pc) {
const stats = await pc.getStats();
for (const report of stats.values()) {
if (
report.type === 'inbound-rtp' &&
report.kind === 'audio'
) {
console.log({
packetsReceived: report.packetsReceived,
packetsLost: report.packetsLost,
bytesReceived: report.bytesReceived,
jitter: report.jitter,
audioLevel: report.audioLevel
});
}
}
}
showInboundAudio(pc);
MDN توضیح میدهد packetsReceived تعداد RTP Packetهای دریافتشده و bytesReceived حجم Media Payload دریافتشده را نشان میدهد. اگر این اعداد طی تماس ثابت بمانند، Browser عملاً Media دریافت نمیکند.
تفسیر سریع Stats
| وضعیت | نتیجه |
|---|---|
packetsReceived = 0 |
Media به Browser نمیرسد |
| Packets بالا میرود، AudioLevel صفر است | ممکن است طرف مقابل Silence بفرستد یا Track مشکل داشته باشد |
| Packets بالا میرود، Jitter/Loss زیاد است | کیفیت شبکه مشکل دارد |
| Bytes بالا میرود ولی صدایی شنیده نمیشود | Playback، Output Device، Track یا Browser Policy را بررسی کنید |
مرحله ۷: سمت Asterisk بررسی کنید RTP وارد و خارج میشود یا نه
برای مدت کوتاه میتوانید RTP Debug را روشن کنید:
asterisk -rvvv
rtp set debug on
بعد از تست خاموش کنید:
rtp set debug off
روی سرور پرترافیک این Debug بسیار پرحجم است؛ آن را دائمی روشن نگذارید.
برای Signaling و SDP نیز موقتاً:
pjsip set logger on
در SDP به این موارد دقت کنید:
🔹 a=ice-ufrag و a=ice-pwd
🔹 خطوط a=candidate
🔹 a=fingerprint برای DTLS
🔹 a=rtcp-mux
🔹 Codecهای مشترک مثل Opus یا PCMU
مرحله ۸: One-way Audio را با جهت Packetها تشخیص دهید
اگر A صدا را از B میشنود ولی B از A نمیشنود، مشکل معمولاً در همان جهتی است که Packet نمیرسد.
یک جدول ساده بسازید:
| سمت | Outbound RTP | Inbound RTP | برداشت |
|---|---|---|---|
| Browser | دارد | ندارد | مسیر PBX → Browser مشکل دارد |
| Browser | ندارد | دارد | Microphone/Track یا Browser → PBX مشکل دارد |
| Asterisk | دارد | ندارد | مسیر Browser → Asterisk مشکل دارد |
| Asterisk | ندارد | دارد | Route/Bridge سمت خروجی PBX را بررسی کنید |
این روش خیلی سریعتر از تغییر تصادفی NAT Settingهاست؛ چون مشخص میکند Packet در کدام جهت گم شده است.
مرحله ۹: اگر Packet میرسد ولی صدا شنیده نمیشود، Browser را بررسی کنید
این مرحله معمولاً نادیده گرفته میشود. حتی با ICE، DTLS و RTP سالم، Browser ممکن است Audio را پخش نکند.
این موارد را بررسی کنید:
🔹 Remote Track واقعاً به <audio> متصل شده است.
🔹 Element روی muted نیست.
🔹 Volume صفر نیست.
🔹 Browser Autoplay Policy پخش را Block نکرده است.
🔹 Output Device اشتباه انتخاب نشده است.
🔹 سیستمعامل صدای Browser را Mute نکرده است.
نمونه استاندارد اتصال Remote Track:
const remoteAudio = document.querySelector('#remoteAudio');
pc.addEventListener('track', (event) => {
const [remoteStream] = event.streams;
if (remoteStream) {
remoteAudio.srcObject = remoteStream;
remoteAudio.play().catch(console.error);
}
});
مرحله ۱۰: Codec را فقط بعد از سالم بودن مسیر بررسی کنید
Codec mismatch واقعاً میتواند صدا را خراب کند، اما نباید اولین مظنون باشد. اگر packetsReceived=0 دارید، تغییر Opus به G.711 مشکل Route را حل نمیکند.
وقتی RTP واقعاً ردوبدل میشود، SDP Offer/Answer را برای Codec مشترک بررسی کنید. روی Asterisk:
disallow=all
allow=opus,ulaw
اگر یک سمت فقط Opus و سمت دیگر فقط PCMA/PCMU دارد، Asterisk یا Media Gateway باید Transcoding مناسب داشته باشد.
یک اسکریپت کوچک برای Snapshot کامل WebRTC بسازید
برای اینکه هر بار چند بخش را جدا بررسی نکنید، میتوانید یک Snapshot تشخیصی از Connection بگیرید:
async function diagnoseWebRTC(pc) {
const stats = await pc.getStats();
const result = {
connectionState: pc.connectionState,
iceConnectionState: pc.iceConnectionState,
iceGatheringState: pc.iceGatheringState,
signalingState: pc.signalingState,
transport: [],
candidatePairs: [],
inboundAudio: [],
outboundAudio: []
};
for (const report of stats.values()) {
if (report.type === 'transport') {
result.transport.push({
iceState: report.iceState,
dtlsState: report.dtlsState,
srtpCipher: report.srtpCipher,
packetsSent: report.packetsSent,
packetsReceived: report.packetsReceived
});
}
if (
report.type === 'candidate-pair' &&
report.state === 'succeeded'
) {
result.candidatePairs.push({
nominated: report.nominated,
localCandidateId: report.localCandidateId,
remoteCandidateId: report.remoteCandidateId,
currentRoundTripTime: report.currentRoundTripTime
});
}
if (report.type === 'inbound-rtp' && report.kind === 'audio') {
result.inboundAudio.push({
packetsReceived: report.packetsReceived,
packetsLost: report.packetsLost,
bytesReceived: report.bytesReceived,
jitter: report.jitter,
audioLevel: report.audioLevel
});
}
if (report.type === 'outbound-rtp' && report.kind === 'audio') {
result.outboundAudio.push({
packetsSent: report.packetsSent,
bytesSent: report.bytesSent
});
}
}
console.log(result);
return result;
}
diagnoseWebRTC(pc);
این Snapshot برای مقایسه تماس سالم و خراب بسیار مفید است. بهخصوص اگر مشکل فقط روی یک ISP، VPN یا شبکه سازمانی رخ میدهد.
خطاهای رایج TURN که باعث WebRTC no audio میشوند
🔹 TURN URL در Browser وجود دارد، اما Username/Credential منقضی یا اشتباه است.
🔹 Port 3478 باز است ولی Relay Port Range بسته است.
🔹 Server پشت NAT است و External IP در Coturn درست تعریف نشده است.
🔹 فقط UDP TURN دارید ولی شبکه کاربر UDP را Block میکند.
🔹 TLS/TCP TURN لازم است ولی 5349 یا Certificate درست نیست.
🔹 Browser اصلاً relay Candidate دریافت نکرده ولی تصور میکنید تماس از TURN عبور میکند.
خطاهای رایج NAT و Firewall
🔹 SDP آدرس Private را به سمت اینترنت Advertise میکند.
🔹 Firewall UDP Media Range را فقط در یک جهت اجازه میدهد.
🔹 NAT Mapping سریع Expire میشود.
🔹 SIP Server و Media Server روی IPهای متفاوت هستند ولی ACL فقط یکی را باز کرده است.
🔹 VPN MTU یا Policy باعث Drop شدن Packetهای Media میشود.
🔹 SBC/Proxy SDP را Rewrite میکند اما RTP Route با Rewrite هماهنگ نیست.
در چه زمانی RTPengine میتواند مشکل را سادهتر کند؟
اگر فقط یک Asterisk دارید، Asterisk خودش WebRTC را مدیریت میکند و RTPengine الزامی نیست. اما وقتی چند PBX، Kamailio/OpenSIPS، کاربران پشت NATهای متفاوت یا تعداد WebRTC Client زیاد دارید، RTPengine میتواند Media را Anchor کند و ICE/SRTP/NAT را در یک لایه مستقل نگه دارد.
در چنین معماریای عیبیابی سادهتر میشود، چون Signaling و Media Plane از هم جدا و قابلمشاهده هستند. البته خود RTPengine هم باید Health Check، Port Capacity و Packet Stats داشته باشد.
جدول تشخیص سریع WebRTC بدون صدا
| نتیجه تست | تمرکز بعدی |
|---|---|
| WSS وصل نمیشود | TLS/Certificate/WebSocket؛ هنوز Media را بررسی نکنید |
| WSS سالم، ICE Failed | Candidate، STUN، TURN، NAT، Firewall |
| ICE Connected، DTLS Failed | Fingerprint، DTLS Profile، SDP، AVPF/RTCP-MUX |
| DTLS Connected، RTP صفر | Media Route، Firewall، TURN، PBX/RTPengine |
| Inbound RTP صفر، Outbound RTP دارد | مسیر برگشت به Browser |
| Inbound RTP دارد، صدای شنیداری صفر | Audio Element، Autoplay، Device، Codec/Decode |
| فقط در بعضی شبکهها خراب است | TURN و Policy شبکه |
| با relay-only سالم است | Direct ICE/NAT path مشکل دارد |
تماس WebRTC وصل میشود اما صدا ندارید؟
اگر Asterisk، TURN یا Media Proxy روی VPS دارید، میتوان مسیر ICE، DTLS و RTP را جداگانه بررسی کرد تا مشخص شود خرابی از Browser، NAT، TURN، Firewall یا PBX است.
ایرانیکاسرور میتواند برای تهیه VPS، تنظیم Asterisk، Coturn، RTPengine، Firewall و تست Media Path در WebRTC کمک کند.
تماس با پشتیبانی ایرانیکاسرور: 021-91302467
پرسشهای رایج
چرا WebRTC تماس را وصل میکند ولی صدا ندارد؟
چون Signaling و Media مسیرهای جدا دارند. WSS و SIP میتوانند سالم باشند ولی ICE، TURN، DTLS یا RTP مشکل داشته باشد. ابتدا ICE Pair و DTLS State را بررسی کنید و بعد سراغ RTP بروید.
فرق host، srflx و relay Candidate چیست؟
host آدرس مستقیم Endpoint است، srflx آدرس Public Mapping کشفشده از NAT و relay آدرسی است که TURN Server برای Relay کردن Media اختصاص میدهد.
چطور مطمئن شویم TURN واقعاً کار میکند؟
باید relay Candidate ببینید. برای تست میتوانید موقتاً iceTransportPolicy را روی relay قرار دهید؛ اگر ICE در این حالت وصل نشد، TURN Auth، Firewall و Relay Port Range را بررسی کنید.
DTLS Failed یعنی چه؟
یعنی ICE ممکن است مسیر شبکه را پیدا کرده باشد، اما مذاکره امنیتی DTLS برای ایجاد کلیدهای SRTP موفق نشده است. Fingerprint، SDP، DTLS Profile و مسیر Packet را بررسی کنید.
چطور بفهمیم RTP به Browser میرسد؟
با RTCPeerConnection.getStats مقدارهای inbound-rtp مانند packetsReceived و bytesReceived را بررسی کنید. اگر این اعداد افزایش پیدا نمیکنند، Media وارد Browser نمیشود.
چرا فقط در یک جهت صدا داریم؟
One-way Audio معمولاً نشاندهنده مشکل جهت خاصی از NAT، Firewall، Candidate یا Media Route است. Outbound و Inbound RTP را روی هر دو سمت جدا بررسی کنید.
STUN میتواند جای TURN را بگیرد؟
خیر. STUN معمولاً برای کشف Public Mapping استفاده میشود و Media Relay نمیکند. وقتی اتصال مستقیم ممکن نیست، TURN ترافیک را Relay میکند.
اگر RTP میرسد ولی باز هم صدا نداریم چه کنیم؟
Remote Audio Track، اتصال آن به عنصر audio، Autoplay Policy، Output Device و Codec را بررسی کنید. در این مرحله مسیر شبکه احتمالاً سالم است.
جمعبندی
برای عیبیابی WebRTC بدون صدا، ترتیب بررسی از خود مشکل مهمتر است. اول Candidateهای ICE را ببینید، بعد Candidate Pair انتخابشده را پیدا کنید، سپس TURN را با relay Candidate ثابت کنید، وضعیت DTLS را بررسی کنید و در نهایت با getStats مطمئن شوید RTP واقعاً وارد Browser میشود.
اگر ICE در وضعیت Failed است، تغییر Codec کمکی نمیکند. اگر DTLS Failed است، بازکردن تصادفی RTP Portها بدون بررسی Fingerprint و SDP مسیر درستی نیست. و اگر RTP Packetها وارد Browser میشوند ولی صدا شنیده نمیشود، مشکل را در Audio Track، Autoplay و Output Device جستوجو کنید.
این رویکرد باعث میشود بهجای تغییر چندین Config همزمان، دقیقاً همان لایهای را اصلاح کنید که Media در آن متوقف شده است.
برای عیبیابی تماس WebRTC بدون صدا کمک میخواهید؟
اگر Browser، Asterisk و TURN Server آماده باشند، میتوان مسیر Media را با ICE Stats، DTLS State و RTP Counters بررسی کرد و محل دقیق قطع صدا را مشخص کرد.
تماس با پشتیبانی ایرانیکاسرور: 021-91302467






دیدگاهها (۰)