Требуется помощь WebRTC

Пришло время экспериментировать с WebRTC.

Мне кажется, я понимаю принцип работы, но удаленное видео чёрное, где моя ошибка?

Когда я добавляю console.log(), все в порядке, это сводит меня с ума!

Я использую socket.io для сигнализации:

io.sockets.on('connection', function(socket) {
    socket.on('rtcOffer', function(offer) {
        socket.broadcast.emit('rtcOffer', offer);
    });

    socket.on('rtcOfferAnswer', function(offer) {
        socket.broadcast.emit('rtcOfferAnswer', offer);
    });

    socket.on('rtcCandidate', function(candidate) {
        socket.broadcast.emit('rtcCandidate', candidate);
    });
});

HTML:

<video id="localVideo" width="400" autoplay="autoplay" style="border:#000 1px solid;"></video>
<video id="remoteVideo" width="400" autoplay="autoplay" style="border:#000 1px solid;"></video>
<button type="button" id="bCall">Call</button>

JS:

navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.getUserMedia;
window.RTCPeerConnection = window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection;
window.createSignalingChannel = window.mozCreateSignalingChannel || window.webkitCreateSignalingChannel || window.createSignalingChannel;
window.RTCSessionDescription = window.webkitRTCSessionDescription || window.mozRTCSessionDescription || window.RTCSessionDescription;
window.RTCIceCandidate = window.webkitRTCIceCandidate || window.mozRTCIceCandidate || window.RTCIceCandidate;

var _peerConn = null;
var _mediaConstraints = {
    mandatory : {
        /*OfferToReceiveAudio : true,*/
        OfferToReceiveVideo : true
    }
};
var _rtcConfig = {
    iceServers : [{url : 'stun:stun.ekiga.net'},
    {url : 'stun:stun.ideasip.com'},
    {url : 'stun:stun.rixtelecom.se'},
    {url : 'stun:stun.schlund.de'},
    {url : 'stun:stun.l.google.com:19302'},
    {url : 'stun:stun1.l.google.com:19302'},
    {url : 'stun:stun2.l.google.com:19302'},
    {url : 'stun:stun3.l.google.com:19302'},
    {url : 'stun:stun4.l.google.com:19302'},
    {url : 'stun:stun.voiparound.com'},
    {url : 'stun:stun.voipbuster.com'},
    {url : 'stun:stun.voipstunt.com'},
    {url : 'stun:stun.voxgratia.org'},
    {url : 'stun:stun.xten.com'},
    {
        url : 'turn:numb.viagenie.ca',
        credential : 'muazkh',
        username : 'webrtc@live.com'
    }]
};

function call(localStream) {
    _peerConn = new RTCPeerConnection(_rtcConfig, {
       optional : [{
           DtlsSrtpKeyAgreement : true
        }]
    });

    _peerConn.onicecandidate = function(e) {
        if(e.candidate) {
            _peerConn.onicecandidate = null;

            socketEmit('rtcCandidate', e.candidate);
        }
    };

    _peerConn.onaddstream = function(e) {
        if(e) {
            $('#remoteVideo')[0].src = URL.createObjectURL(e.stream);
        }
    };

    _peerConn.addStream(localStream);

    _peerConn.createOffer(function(offer) {
        _peerConn.setLocalDescription(new RTCSessionDescription(offer));

        socketEmit('rtcOffer', offer);
    }, logError, _mediaConstraints);
}
function callEnd() {
    $('video').each(function() {
        this.pause();
    });

    _peerConn.close();
}
function callAccept(localStream, offer) {
    _peerConn = new RTCPeerConnection(_rtcConfig, {
       optional : [{
           DtlsSrtpKeyAgreement: true
        }]
    });

    _peerConn.addStream(localStream);

    _peerConn.onaddstream = function(e) {
        if(e) {
            $('#remoteVideo')[0].src = window.URL.createObjectURL(e.stream);
        }
    };

    _peerConn.onicecandidate = function(e) {
        if(e.candidate) {
            _peerConn.onicecandidate = null;

            socketEmit('rtcCandidate', e.candidate);
        }
    };

    _peerConn.setRemoteDescription(new RTCSessionDescription(offer), function() {
        _peerConn.createAnswer(function(answer) {
            _peerConn.setLocalDescription(new RTCSessionDescription(answer));

            socketEmit('rtcOfferAnswer', answer);
        }, logError, _mediaConstraints);
    }, logError);
}
function rtcCandidateIncoming(candidate) {
    if(_peerConn === null) {
        setTimeout(function() {
            rtcCandidateIncoming(candidate);
        }, 500);

        return;
    }

    _peerConn.addIceCandidate(new RTCIceCandidate({
        sdpMLineIndex : candidate.sdpMLineIndex,
        candidate : candidate.candidate
    }));
}
function userMediaAsk(callback) {
    navigator.getUserMedia({
        video : true/*,
        audio : true*/
    }, function(localStream) {
        $('#localVideo')[0].src = window.URL.createObjectURL(localStream);

        callback(localStream);
    }, logError);
}
function logError(e) {
    if(typeof e == typeof {}) {
        console.log('Error : '+JSON.stringify(e));
    }
    else {
        console.log('Error : '+e);
    }

    callEnd();
}

$(document).ready(function() {
    $('#bCall').click(function() {
        userMediaAsk(function(stream) {
            call(stream);
        });
    });

    socketRegisterSignal('rtcOffer', function(offer) {
        userMediaAsk(function(stream) {
            callAccept(stream, offer);
        });
    });

    socketRegisterSignal('rtcOfferAnswer', function(answer) {
        _peerConn.setRemoteDescription(new RTCSessionDescription(answer));
    });

    socketRegisterSignal('rtcCandidate', function(candidate) {
        rtcCandidateIncoming(candidate);
    });
});

window.onbeforeunload = function() {
    if(_peerConn) {
        _peerConn.close();
    }
};

Большое спасибо!!

1 ответ

Я не могу точно сказать, что является причиной проблемы в вашем коде, но мой опыт показывает, что это вызвано пропущенным обменом кандидатами в лед.

Ледовые кандидаты генерируются, когда вы выполняете setLocalDecription и тем, как PeerConnection обрабатывает ледяные кандидаты с помощью метода onicecandidate, иногда удаленный узел не готов принять кандидатов. Я вижу таймер, который вы установили, но я бы подтвердил, что вы отправляете / получаете их правильно. Вы можете проверить состояние льда в chrome с помощью chrome://webrtc-internals url.

Другие вопросы по тегам