Видео / аудио вызовы WebRTC не удались в 90% случаев в другой сети, но были успешными в 90% случаев в той же сети

Я создал приложение для видеочата с использованием webRTC и канала Django. Мое приложение работает правильно 90% времени при использовании в той же сети, но не работает при использовании в другой сети. Я не могу видеть видео удаленного человека при использовании это в другой сети.

Я использовал chrome: // webrtc-internals / для отслеживания своего ответа webRTC и получил iceconnectionstate: failed при вызове из другой сети  введите описание изображения здесь

Ниже показан снимок экрана, когда я добился успеха в той же сети введите описание изображения здесь

А после успеха он также дал ошибку addIceCandidateFailed в той же сети, но видеозвонки работают правильно, и эта ошибка появляется только в Chrome, но не в firefox. Ниже приведен снимок экрана  введите описание изображения здесь

Ниже представлена ​​моя бесплатная конфигурация сервера STUN / TUNE. Я получил ее по одной из ссылок на StackOverflow.

 var peerConnectionConfig = {
        iceServers: [{
                urls: ["turn:173.194.72.127:19305?transport=udp",
                    "turn:[2404:6800:4008:C01::7F]:19305?transport=udp",
                    "turn:173.194.72.127:443?transport=tcp",
                    "turn:[2404:6800:4008:C01::7F]:443?transport=tcp"
                ],
                username: "CKjCuLwFEgahxNRjuTAYzc/s6OMT",
                credential: "u1SQDR/SQsPQIxXNWQT7czc/G4c="
            },
            {
                urls: ["stun:stun.l.google.com:19302"]
            }
        ]
    };

Ниже мой код javascript webRTC

$(function() {
    var initiator,pc;
    var isSender = false;
    var peerConnectionConfig = {
        iceServers: [{
                urls: ["turn:173.194.XX.127:19305?transport=udp",
                    "turn:[2404:XXXX:XXXX:C01::7F]:19305?transport=udp",
                    "turn:173.194.XX.127:443?transport=tcp",
                    "turn:[2404:XXXX:XXXX:C01::7F]:443?transport=tcp"
                ],
                username: "XXXXXXXXXX",
                credential: "YYYYYYYYYYY"
            },
            {
                urls: ["stun:stun.l.google.com:19302"]
            }
        ]
    };
    $.ajax({
        type: "GET",
        url: '/isRoomExist/?roomName=121' ,
        beforeSend: function() {},
        success: function(data) {
            data = JSON.parse(data);
            initiatorCtrl(data[0].flgInitiator);
        }
    });
    var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
    var chatsock = new ReconnectingWebSocket(ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname);

    function initiatorCtrl(event) {
        if (event == "fullhouse") {
            alert("full house");
        }
        if (event == "initiator") {
            initiator = false;
            init();
        }
        if (event == "not initiator") {
            initiator = true;
            init();
        }
    }

    function init() {
        var constraints = {
            audio: true,
            video: true
        };
        getUserMedia(constraints, connect, fail);
    }

    function connect(stream) {
        pc = new RTCPeerConnection(peerConnectionConfig);

        if (stream) {
            pc.addStream(stream);
            $('#local').attachStream(stream);
        }

        pc.onaddstream = function(event) {
            $('#remote').attachStream(event.stream);
            logStreaming(true);
        };
        pc.onicecandidate = function(event) {
            if (event.candidate) {
                chatsock.send(JSON.stringify(event.candidate));
                isSender = true;
            }
        };
        if (initiator) {
            createOffer();
        } else {
            log('waiting for offer...');
        }
        logStreaming(false);

        chatsock.onmessage = function(event) {
            var signal1 = JSON.parse(event.data);
            var signal = JSON.parse(signal1);

            if (isSender) {
                isSender = false
            } else {
                if (signal.sdp) {
                    if (initiator) {
                        receiveAnswer(signal);
                    } else {
                        receiveOffer(signal);
                    }
                } else if (signal.candidate) {
                    pc.addIceCandidate(new RTCIceCandidate(signal));
                }
            }
        };
    }

    function createOffer() {
        pc.createOffer(function(offer) {
            pc.setLocalDescription(offer, function() {
                chatsock.send(JSON.stringify(offer));
                isSender = true;
            }, fail);
        }, fail);
    }

    function receiveOffer(offer) {
        pc.setRemoteDescription(new RTCSessionDescription(offer), function() {
            pc.createAnswer(function(answer) {
                pc.setLocalDescription(answer, function() {
                    chatsock.send(JSON.stringify(answer));
                    isSender = true;
                }, fail);
            }, fail);
        }, fail);
    }

    function receiveAnswer(answer) {
        pc.setRemoteDescription(new RTCSessionDescription(answer));
    }
    function log() {
        console.log(Array.prototype.join.call(arguments, ' '))
        console.log.apply(console, arguments);
    }
    function logStreaming(streaming) {
        $('#streaming').text(streaming ? '[streaming]' : '[..]');
    }
    function fail() {
        console.error.apply(console, arguments);
    }
    jQuery.fn.attachStream = function(stream) {
        this.each(function() {
            this.src = URL.createObjectURL(stream);
            this.play();
        });
    };


});

person Arti Berde    schedule 26.05.2017    source источник


Ответы (2)


Согласно вашим webrtc-internals, вы добавляете удаленных кандидатов перед установкой удаленного описания.

Поместите кандидатов в очередь, пока не получите удаленное описание.
После настройки удаленного описания
вы можете отправить локальных кандидатов удаленному пользователю из очереди или из onicecandidate
и добавить удаленных кандидатов на свой компьютер. .

Обновление:
Порядок сообщений в целом не совпадает с порядком POST от другого клиента, потому что POST асинхронны, и сервер может обрабатывать кандидатов быстрее (просто ретранслировать и меньшего размера), чем предложение / ответ (необходимо обработать маршрутизацию / cdr / разветвление).

Очередь удаленных кандидатов: нам необходимо обработать удаленное предложение перед добавлением кандидатов.
Очередь локальных кандидатов: если вызов / предложение разветвляется по нескольким адресатам (пользователь вошел в & браузера или группового вызова), то инициатором будет принят только первый ответ. Таким образом, все принимающие конечные точки должны поставить в очередь местных кандидатов, пока их ответ не будет подтвержден.

Справочный пример

person Ajay    schedule 26.05.2017
comment
Но когда я получил addIceCandidateFailed, я смог правильно выполнять видеозвонки в той же сети. Я изменю свой код в соответствии с вашим предложением, чтобы решить ошибку addIceCandidateFailed. Но я не понял, почему он не работает в разных сетях. Спасибо за ваше решение. . - person Arti Berde; 26.05.2017
comment
Прочтите html5rocks.com/en/tutorials/webrtc/infrastructure. кандидатам требуется установить соединение между одноранговыми узлами, расположенными в сетях. Все зависит от условий сети / NAT. - person Ajay; 26.05.2017
comment
Я читал это ранее, и для NAT я использую STUN / TURN. [{Urls: [turn: 173.194.72.127: 19305? Transport = udp, turn: [2404: 6800: 4008: C01 :: 7F]: 19305? Transport = udp, Turn: 173.194.72.127: 443? transport = tcp, Turn: [2404: 6800: 4008: C01 :: 7F]: 443? transport = tcp], имя пользователя: CKjCuLwFEgahxNRjuTAYzc / s6OMT, учетные данные: u1SQDR4xcXzcWPQI =}, {urls: [stun: stun.l.google.com: 19302]}]. Я получил это по одной из ссылок stackoverflow. Мне нужно настроить собственный сервер TURN для повышения производительности - person Arti Berde; 26.05.2017
comment
Никто не предоставит бесплатный сервер поворота, вам нужно настроить свой собственный. `{urls: [stun: stun.l.google.com: 19302]}` этот сервер оглушения Google, который вы можете использовать, в основном он работает в обычной сети, если вы правильно настроили кандидатов. - person Ajay; 26.05.2017
comment
@Ajay Где ваше свидетельство того, что Chrome генерирует кандидатов перед установкой localDescription? Это было бы нарушением спецификации. Очередь должна быть ненужной, и это неправильный совет здесь ИМХО. - Настоящий ответ здесь, кажется, заключается в том, что серверы с бесплатным ходом - это миф. - person jib; 26.05.2017
comment
@jib Я тестировал последнюю канарейку, кандидаты собираются только после setLocalDescription. Недавно я где-то читал, они собираются поразить серверы оглушения / разворота перед setLD, чтобы ускорить сбор. Обновил ответ на очередь. - person Ajay; 27.05.2017
comment
@Ajay Браузеры, вероятно, могут сэкономить время, подключившись к серверам раньше, не меняя, когда кандидаты отправляются в JavaScript. Этот инвариант времени JavaScript важен для гарантии того, что кандидаты никогда не попадут в сигнальный канал до предложения / ответа, которому они принадлежат. - person jib; 27.05.2017

Проверка порядка сообщений, предложенная Аджаем, действительно помогает.

Осталось добавить: ссылки на диаграммы, которые упрощают проверку порядка: диаграмма обмена SDP-пакетами и Схема обмена кандидатами ICE

Также есть отличная статья, объясняющая диаграммы: статья о mdn. mozillademos.org

person Fedorov7890    schedule 25.07.2020