script does exchange the candidates but after answer candidate will be null. i have no console's logs. i tried public turn servers and didn't work. every thing looks just fine. how can i trace or resolve it? here is my code:
client3.js that handles users:
const conn = new WebSocket('wss://domain.com/wss');
const signaler = new SignalingChannel(conn);
const pc = new RTCPeerConnection({
iceServers: [
{
urls: "stun:domain.com:3478",
},
{
urls: "turn:ip:3478",
username: "user",
credential: "pass",
},
],
});
const constraints = { audio: true, video: false };
var localAudio = document.querySelector('#localAudio');
var remoteAudio = document.querySelector('#remoteAudio');
var callBtn = $('#callBtn');
async function start() {
console.log('start');
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
for (const track of stream.getTracks()) {
pc.addTrack(track, stream);
}
localAudio.srcObject = stream;
} catch (err) {
console.error(err);
}
}
let makingOffer = false;
let isSettingRemoteAnswerPending = false;
pc.onnegotiationneeded = async () => {
console.log('onnegotiationneeded');
try {
makingOffer = true;
await pc.setLocalDescription();
signaler.send({ description: pc.localDescription });
} catch (err) {
console.error(err);
} finally {
makingOffer = false;
}
};
pc.oniceconnectionstatechange = () => {
console.log('oniceconnectionstatechange');
if (pc.iceConnectionState === "failed") {
pc.restartIce();
}
};
pc.onicecandidate = ({ candidate }) => {
console.log('onicecandidate');
console.log(candidate);
signaler.send({ candidate });
}
pc.ontrack = ({ track, streams }) => {
console.log('ontrack');
track.onunmute = () => {
console.log('onunmute');
if (remoteAudio.srcObject) {
return;
}
remoteAudio.srcObject = streams[0];
};
};
let ignoreOffer = false;
signaler.onmessage = async ({data: {description, candidate}}) => {
console.log('onmessage');
try {
if (description) {
console.log('description');
// An offer may come in while we are busy processing SRD(answer).
// In this case, we will be in "stable" by the time the offer is processed
// so it is safe to chain it on our Operations Chain now.
const readyForOffer =
!makingOffer &&
(pc.signalingState == "stable" || isSettingRemoteAnswerPending);
const offerCollision = description.type == "offer" && !readyForOffer;
ignoreOffer = !polite && offerCollision;
if (ignoreOffer) {
return;
}
isSettingRemoteAnswerPending = description.type == "answer";
await pc.setRemoteDescription(description); // SRD rolls back as needed
isSettingRemoteAnswerPending = false;
if (description.type == "offer") {
await pc.setLocalDescription();
signaling.send({description: pc.localDescription});
}
} else if (candidate) {
console.log('candidate');
try {
await pc.addIceCandidate(candidate);
} catch (err) {
if (!ignoreOffer) throw err; // Suppress ignored offer's candidates
}
}
} catch (err) {
console.error(err);
}
}
callBtn.on("click",function (){
start();
});
server.php for handling websocket:
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
require dirname(__DIR__) . '/rtc/vendor/autoload.php';
$app = new \Ratchet\Http\HttpServer(
new \Ratchet\WebSocket\WsServer(
new Chat()
)
);
$loop = React\EventLoop\Loop::get();
$webSock = new \React\Socket\SocketServer('0.0.0.0:9090',array('tls' => array('local_cert' => 'file.com.cert')) ,$loop);
$webSock = new \React\Socket\SecureServer($webSock, $loop, [
'local_cert' => 'file.com.cert',
'local_pk'=> 'file.com.key',
'allow_self_signed' => false,
'verify_peer' => false
]);
$webSock = new \Ratchet\Server\IoServer($app, $webSock, $loop);
$webSock->run();
chat.php:
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface
{
protected $clients;
public $userObj, $data;
public function __construct()
{
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
echo "New connection!";
}
public function onMessage(ConnectionInterface $from, $msg) {
foreach ($this->clients as $client) {
if ($from != $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
?>
and this is my turnserver.conf:
#added
max-allocation-lifetime=600
allocation-refresh-time=300
#listening-device=eth0
listening-port=3478
#tls-listening-port=5349
#alt-listening-port=0
#alt-tls-listening-port=0
#tcp-proxy-port=5555
listening-ip=0.0.0.0
#listening-ip=10.207.21.238
#listening-ip=2607:f0d0:1002:51::4
#aux-server=172.17.19.110:33478
#aux-server=[2607:f0d0:1002:51::4]:33478
#udp-self-balance
#relay-device=eth1
#relay-ip=myserverip
#relay-ip=2607:f0d0:1002:51::5
external-ip=myserverip
#external-ip=60.70.80.91/172.17.19.101
#external-ip=60.70.80.92/172.17.19.102
#relay-threads=0
min-port=10000
max-port=99000
verbose
#Verbose
fingerprint
lt-cred-mech
#no-auth
#prometheus
# usercombo -> "timestamp:userid"
# turn user -> usercombo
# turn password -> base64(hmac(secret key, usercombo))
#use-auth-secret
#static-auth-secret=north
server-name=domain.com
user=test:test
#user=username2:key2
# OR:
#user=username1:password1
#user=username2:password2
#user=ninefingers:youhavetoberealistic
# /var/lib/turn/turndb.
#userdb=/var/db/turndb
#secret-key-file=/path/
realm=domain.com
#check-origin-consistency
#user-quota=0
#total-quota=0
#max-bps=0
#bps-capacity=0
#no-udp
#no-tcp
#no-tls
#no-dtls
#no-udp-relay
#no-tcp-relay
#stale-nonce=600
max-allocate-lifetime=136000
#channel-lifetime=600
#permission-lifetime=300
cert=/home/domain/domains/domain.com/public_html/rtc/file.com.cert
pkey=/home/domain/domains/domain.com/public_html/rtc/domain.com.key
#pkey-pwd=...
#cipher-list="DEFAULT"
#CA-file=/etc/ssh/id_rsa.cert
#ec-curve-name=prime256v1
#dh566
#dh1066
#dh-file=<DH-PEM-file-name>
#no-stdout-log
log-file=/var/log/coturn/turnserver.log
#syslog
#syslog-facility="LOG_LOCAL1"
simple-log
#new-log-timestamp-format "%FT%T%z"
#log-binding
#alternate-server=1.2.3.4:5678
#alternate-server=11.22.33.44:56789
#alternate-server=5.6.7.8
#alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
# Examples:
#tls-alternate-server=1.2.3.4:5678
#tls-alternate-server=11.22.33.44:56789
#tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
#stun-only
#no-software-attribute
#no-stun
#rest-api-separator=:
#allow-loopback-peers
#no-multicast-peers
max-allocate-timeout=16600
# denied-peer-ip=83.166.64.0-83.166.95.255
# allowed-peer-ip=83.166.68.45
#pidfile="/var/run/turnserver.pid"
#secure-stun
#mobility
#keep-address-family
#allocation-default-address-family="ipv4"
#allocation-default-address-family="ipv4"
#proc-user=<user-name>
#proc-group=<group-name>
#no-cli
#cli-ip=127.0.0.1
#cli-port=5766
#cli-password=$5$79a316b350311570$81df9cfb9af7f5e5a76eada31e7097b663a0670f99a3c07ded3f1c8e59c5658a
#cli-password=qwerty
#web-admin
#web-admin-ip=127.0.0.1
#web-admin-port=8080
#web-admin-listen-on-workers
#acme-redirect=http://redirectserver/.well-known/acme-challenge/
#server-relay
#cli-max-output-sessions
#ne=[1|2|3]
#no-tlsv1
#no-tlsv1_1
#no-tlsv1_2
no-rfc5780
no-stun-backward-compatibility
response-origin-only-with-rfc5780
i tried public turn servers.