3

我一直在努力解决这个问题,但我不知道出了什么问题,你们能帮帮我吗?我尝试了像这样修改的 WebRTC 代码。

  • 调用者和被调用者都进入网络
  • Web 将根据他们的用户名创建频道
  • 呼叫者单击呼叫按钮并将消息发送到特定频道到某个用户的用户名。当呼叫者单击呼叫按钮时,他然后创建 peerConnection 并添加 localStream
  • 被调用者将收到消息,该过程像 WebRTC 示例代码一样继续进行。当被调用者收到一个offer时,他会创建peerConnection并添加localStream,然后创建并发送answer

我的代码是这样的

        var my_username = '{{ current_username }}';
        var friend;

        var localVideo;
        var remoteVideo;
        var localStream;
        var remoteStream;
        var channel;
        var channelReady = false;
        var pc;
        var socket;
        var started = false;
        // Set up audio and video regardless of what devices are present.
        var mediaConstraints = {'mandatory': {
                        'OfferToReceiveAudio':true, 
                        'OfferToReceiveVideo':true }};
        var isVideoMuted = false;
        var isAudioMuted = false;


        function choiceFriendInitialize() {
            var choice_visible = false;

            $('ul#friendlist > li').click(function(e) {
                $(this).css('background-color', '#808080');
                friend = $(this).text();
                var choice = $('ul#choice');
                choice_visible = true;
                choice.css('display', 'inline-block');
                choice.css('top', e.pageY);
                choice.css('left', e.pageX);
            });

            // trigger call from here
            $('li#call').click(function() {
                $('ul#friendlist > li').css('background-color', 'transparent');
                $('ul#choice').css('display', 'none');
                choice_visible = false;

                // call
                maybeStart();
                doCall();
            });

            $('li#unfriend').click(function() {
                $('ul#friendlist > li').css('background-color', 'transparent');
                $('ul#choice').css('display', 'none');
                choice_visible = false;
            });

            $("body").mouseup(function(){ 
                if (choice_visible) {
                    $('ul#friendlist > li').css('background-color', 'transparent');
                    $('ul#choice').css('display', 'none');
                    choice_visible = false;
                }
            });
        }

        function initialize() {
            console.log("Initializing..");
            choiceFriendInitialize();

            localVideo = document.getElementById("localVideo");
            remoteVideo = document.getElementById("remoteVideo");
            openChannel();
            doGetUserMedia();
        }

        function openChannel() {
            console.log("Opening channel.");
            var channel = new goog.appengine.Channel('{{ token }}');
            var handler = {
                'onopen': onChannelOpened,
                'onmessage': onChannelMessage,
                'onerror': onChannelError,
                'onclose': onChannelClosed
            };
            socket = channel.open(handler);
        }

        function doGetUserMedia() {
            // Call into getUserMedia via the polyfill (adapter.js).
            var constraints = {"mandatory": {}, "optional": []};
            try {
                getUserMedia({'audio':true, 'video':constraints}, onUserMediaSuccess, onUserMediaError);
                console.log("Requested access to local media with mediaConstraints:\n" + "  \"" + JSON.stringify(constraints) + "\"");
            } catch (e) {
                alert("getUserMedia() failed. Is this a WebRTC capable browser?");
                console.log("getUserMedia failed with exception: " + e.message);
            }
        }

        function createPeerConnection() {
            var pc_config = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};
            try {
                // Create an RTCPeerConnection via the polyfill (adapter.js).
                pc = new RTCPeerConnection(pc_config);
                pc.onicecandidate = onIceCandidate;
                console.log("Created RTCPeerConnnection with config:\n" + "  \"" + JSON.stringify(pc_config) + "\".");
            } catch (e) {
                console.log("Failed to create PeerConnection, exception: " + e.message);
                alert("Cannot create RTCPeerConnection object; WebRTC is not supported by this browser.");
                return;
            }
            pc.onconnecting = onSessionConnecting;
            pc.onopen = onSessionOpened;
            pc.onaddstream = onRemoteStreamAdded;
            pc.onremovestream = onRemoteStreamRemoved;
        }

        function maybeStart() {
            if (!started && localStream && channelReady) {
                console.log("Creating PeerConnection.");
                createPeerConnection();
                console.log("Adding local stream.");
                pc.addStream(localStream);
                started = true;
                // Caller initiates offer to peer.
                //if (initiator)
                    //doCall();
            }
        }

        function doCall() {
            console.log("Sending offer to peer.");
            pc.createOffer(setLocalAndSendMessage, null, mediaConstraints);
        }

        function doAnswer() {
            console.log("Sending answer to peer.");
            pc.createAnswer(setLocalAndSendMessage, null, mediaConstraints);
        }

        function setLocalAndSendMessage(sessionDescription) {
            // Set Opus as the preferred codec in SDP if Opus is present.
            sessionDescription.sdp = preferOpus(sessionDescription.sdp);
            pc.setLocalDescription(sessionDescription);
            sendMessage({from: my_username, to: friend}, sessionDescription);
        }

        function sendMessage(client, message) {
            console.log('C->S: ' + JSON.stringify(message));
            var xhr = new XMLHttpRequest();
            xhr.open('POST', '/send', true);
            xhr.setRequestHeader("Content-type","application/json");
            var msgString = {send_info: client, data_message: message};
            xhr.send(JSON.stringify(msgString));
        }

        function processSignalingMessage(message) {
            var msg = JSON.parse(message);
            var data_message = msg.data_message;
            var send_info = msg.send_info;
            if (data_message.type === 'offer') {
                // Callee creates PeerConnection
                if (!started)
                    maybeStart();
                pc.setRemoteDescription(new RTCSessionDescription(data_message));

                friend = send_info.from;
                doAnswer();
            } else if (data_message.type === 'answer' && started) {
                pc.setRemoteDescription(new RTCSessionDescription(data_message));
            } else if (data_message.type === 'candidate' && started) {
                var candidate = new RTCIceCandidate({sdpMLineIndex:data_message.label, candidate:data_message.candidate});
                pc.addIceCandidate(candidate);
            } else if (data_message.type === 'bye' && started) {
                onRemoteHangup();
            }
        }

        function onChannelOpened() {
            console.log('Channel opened.');
            channelReady = true;
        }

        function onChannelMessage(message) {
            console.log('S->C: ' + message.data);
            processSignalingMessage(message.data);
        }

        function onChannelError() {
            console.log('Channel error.');
        }

        function onChannelClosed() {
            console.log('Channel closed.');
        }

        function onUserMediaSuccess(stream) {
            console.log("User has granted access to local media.");
            // Call the polyfill wrapper to attach the media stream to this element.
            attachMediaStream(localVideo, stream);
            localVideo.style.opacity = 1;
            localStream = stream;
            // Caller creates PeerConnection.
            //if (initiator) maybeStart();
        }

        function onUserMediaError(error) {
            console.log("Failed to get access to local media. Error code was " + error.code);
            alert("Failed to get access to local media. Error code was " + error.code + ".");
        }

        function onIceCandidate(event) {
            if (event.candidate) {
                sendMessage({from: my_username, to: friend}, {type: 'candidate',
                    label: event.candidate.sdpMLineIndex,
                    id: event.candidate.sdpMid,
                    candidate: event.candidate.candidate});
            } else {
                console.log("End of candidates.");
            }
        }

        function onSessionConnecting(message) {
            console.log("Session connecting.");
        }

        function onSessionOpened(message) {
            console.log("Session opened.");
        }

        function onRemoteStreamAdded(event) {
            console.log("Remote stream added.");
            attachMediaStream(remoteVideo, event.stream);
            remoteStream = event.stream;
            waitForRemoteVideo();
        }

        function onRemoteStreamRemoved(event) {
            console.log("Remote stream removed.");
        }

        function onHangup() {
            console.log("Hanging up.");
            transitionToDone();
            stop();
            // will trigger BYE from server
            socket.close();
        }

        function onRemoteHangup() {
            console.log('Session terminated.');
            transitionToWaiting();
            stop();
        }

        function stop() {
            started = false;
            isAudioMuted = false;
            isVideoMuted = false;
            pc.close();
            pc = null;
        }

        function waitForRemoteVideo() {
            if (remoteStream.videoTracks.length === 0 || remoteVideo.currentTime > 0) {
                console.log('ada remote stream');
                transitionToActive();
            } else {
                console.log('ga ada remote stream');
                setTimeout(waitForRemoteVideo, 100);
            }
        }

        function transitionToActive() {
            remoteVideo.style.opacity = 1;
        }

        function transitionToWaiting() {
            remoteVideo.style.opacity = 0;
        }

        function transitionToDone() {
            localVideo.style.opacity = 0;
            remoteVideo.style.opacity = 0;
        }

        function toggleVideoMute() {
            if (localStream.videoTracks.length === 0) {
                console.log("No local video available.");
                return;
            }

            if (isVideoMuted) {
                for (i = 0; i < localStream.videoTracks.length; i++) {
                    localStream.videoTracks[i].enabled = true;
                }
                console.log("Video unmuted.");
            } else {
                for (i = 0; i < localStream.videoTracks.length; i++) {
                    localStream.videoTracks[i].enabled = false;
                }
                console.log("Video muted.");
            }
            isVideoMuted = !isVideoMuted;
        }

        function toggleAudioMute() {
            if (localStream.audioTracks.length === 0) {
                console.log("No local audio available.");
                return;
            }

            if (isAudioMuted) {
                for (i = 0; i < localStream.audioTracks.length; i++) {
                    localStream.audioTracks[i].enabled = true;
                }
                console.log("Audio unmuted.");
            } else {
                for (i = 0; i < localStream.audioTracks.length; i++){
                    localStream.audioTracks[i].enabled = false;
                }
                console.log("Audio muted.");
            }

            isAudioMuted = !isAudioMuted;
        }

        setTimeout(initialize, 1);

        // Send BYE on refreshing(or leaving) a demo page
        // to ensure the room is cleaned for next session.
        window.onbeforeunload = function() {
            sendMessage({from: my_username, to: friend}, {type: 'bye'});
            //Delay 100ms to ensure 'bye' arrives first.
            setTimeout(function(){}, 100);
        }

        // Ctrl-D: toggle audio mute; Ctrl-E: toggle video mute.
        // On Mac, Command key is instead of Ctrl.
        // Return false to screen out original Chrome shortcuts.
        document.onkeydown = function() {
            if (navigator.appVersion.indexOf("Mac") != -1) {
                if (event.metaKey && event.keyCode === 68) {
                    toggleAudioMute();
                    return false;
                }
                if (event.metaKey && event.keyCode === 69) {
                    toggleVideoMute();
                    return false;
                }
            } else {
                if (event.ctrlKey && event.keyCode === 68) {
                    toggleAudioMute();
                    return false;
                }
                if (event.ctrlKey && event.keyCode === 69) {
                    toggleVideoMute();
                    return false;
                }
            }
        }

        // Set Opus as the default audio codec if it's present.
        function preferOpus(sdp) {
            var sdpLines = sdp.split('\r\n');
            // Search for m line.
            for (var i = 0; i < sdpLines.length; i++) {
                if (sdpLines[i].search('m=audio') !== -1) {
                    var mLineIndex = i;
                    break;
                }
            }
            if (mLineIndex === null)
                return sdp;
                // If Opus is available, set it as the default in m line.
            for (var i = 0; i < sdpLines.length; i++) {
                if (sdpLines[i].search('opus/48000') !== -1) {
                    var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
                    if (opusPayload)
                        sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
                    break;
                }
            }
            // Remove CN in m line and sdp.
            sdpLines = removeCN(sdpLines, mLineIndex);
            sdp = sdpLines.join('\r\n');
            return sdp;
        }

        function extractSdp(sdpLine, pattern) {
            var result = sdpLine.match(pattern);
            return (result && result.length == 2)? result[1]: null;
        }

        // Set the selected codec to the first in m line.
        function setDefaultCodec(mLine, payload) {
            var elements = mLine.split(' ');
            var newLine = new Array();
            var index = 0;
            for (var i = 0; i < elements.length; i++) {
                if (index === 3) // Format of media starts from the fourth.
                    newLine[index++] = payload; // Put target payload to the first.
                if (elements[i] !== payload)
                    newLine[index++] = elements[i];
            }
            return newLine.join(' ');
        }

        // Strip CN from sdp before CN constraints is ready.
        function removeCN(sdpLines, mLineIndex) {
            var mLineElements = sdpLines[mLineIndex].split(' ');
            // Scan from end for the convenience of removing an item.
            for (var i = sdpLines.length-1; i >= 0; i--) {
                var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
                if (payload) {
                    var cnPos = mLineElements.indexOf(payload);
                    if (cnPos !== -1) {
                        // Remove CN payload from m line.
                        mLineElements.splice(cnPos, 1);
                    }
                    // Remove CN line in sdp
                    sdpLines.splice(i, 1);
                }
            }
            sdpLines[mLineIndex] = mLineElements.join(' ');
            return sdpLines;
        }

当涉及到 waitForRemoteVideo 时,该函数调用 else 条件。但是 remoteVideo 的 blob url 存在。

4

1 回答 1

1

有趣的是,我一直在寻找错误并重写代码三遍,突然在这里发布我的问题后,我意识到我的错误。

这里在函数 processSignallingMessage..

if (data_message.type === 'offer') {
    // Callee creates PeerConnection
    if (!started)
        maybeStart();
        pc.setRemoteDescription(new RTCSessionDescription(data_message));

        friend = send_info.from;
        doAnswer();
    } else ....

应该是这样的:

if (data_message.type === 'offer') {
    friend = send_info.from;
    // Callee creates PeerConnection
    if (!started)
        maybeStart();
        pc.setRemoteDescription(new RTCSessionDescription(data_message));


        doAnswer();
    } else ....

friend因为被调用者需要在发送候选消息之前填充变量。

于 2012-12-17T16:20:59.220 回答