1 背景

webrtc的代码中有peerconnectionclient和peerconnectionserver的例子,但是没有对应的web端的例子,这里简单的写了一个测试例子,具体如下:

2 具体操作

2.1 操作流程

webrtc-m79-测试peerconnectionserver的webclient-p2p-demo-LMLPHP

2.2 测试效果

使用webclient与peerconnectionclient的测试效果如下:

webrtc-m79-测试peerconnectionserver的webclient-p2p-demo-LMLPHP

3 前端代码

<html>
<head>
    <title>webclient p2p demo</title>
    <meta charset="utf-8">
    <style>
        .left_part {
            width: 50%;
            float: left;
        }
        .right_part {
            width: 50%;
            float: right;
            overflow-y: scroll;
        }
    </style>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

<body>
    <div class="right_part" id="left_part_container">
        <div>
            SignalServer:
            <input type="text" id="signal_url" value="127.0.0.1:8888">
            <button id="btn_sign_in">登入</button>
            <button id="btn_sign_out">登出</button><br>

            <input type="text" id="selected_peerid_for_p2p" disabled value="不允许修改的文本">
            <button id="btn_start_p2p" disabled>start_p2p</button>
        </div>
        <br>

        <table id="PeersTable">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>PeerId</th>
                    <th>Status</th>
                </tr>
            </thead>
        </table>
    </div>

    <div class="left_part" id="left_part_container">
        <video controls autoplay id="rtc_video_play" height="500" style="width:100%"></video>
    </div>

    <script type="text/javascript">
        var myselfPeerId = -1;
        var pc = new RTCPeerConnection({ "offerExtmapAllowMixed": false });
        var datachannel = null;
        var stream = new MediaStream();

        async function initPC() {
            pc.addTransceiver("audio", {direction: "recvonly"});
            pc.addTransceiver("video", {direction: "recvonly"});

            pc.onconnectionstatechange = function(event){
                console.log("connection state change: ", pc.connectionState);
            };
            
            pc.onicecandidate = async (ev) => {
                console.log('=======>' + JSON.stringify(ev.candidate));        
            };

            pc.ontrack = function(event) {
                stream.addTrack(event.track);                         
            };
            
            datachannel = pc.createDataChannel('chat');

            datachannel.onopen = function(event) {
                console.log("datachannel onopen: ", event.data);
            }

            datachannel.onmessage = function(event) {
              console.log("receive message: ", event.data);
            }

            datachannel.onerror = function(event) {
              console.log("datachannel error: ", event.data);
            }

            datachannel.onclose = function(event) {
              console.log("datachannel close: ");
            }                    
        }


        async function startP2P(signal_url) {
            initPC();

            var offer = await pc.createOffer();
            await pc.setLocalDescription(offer);

            let sendOfferPromise = await fetch(signal_url, {
                "method": "POST",
                "body": JSON.stringify(offer)
            });

            if (sendOfferPromise.ok) {
                console.log("send offer: " + JSON.stringify(offer));                         
            } else {
                alert("HTTP-Error: " + sendOfferPromise.status);
            }               
        }   

        async function nfyFromSignal(nfyPromise) {
            let nfyString = await nfyPromise.text();
            if (nfyString.includes("answer")) {
                console.log("receive answer: " + nfyString);
                pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(nfyString)));
            } else if (nfyString.includes("candidate")) {
                console.log("receive candidate: " + nfyString);
                pc.addIceCandidate(new RTCIceCandidate(JSON.parse(nfyString)));
            } else {
                let peersString = nfyString;
                // console.log("rsp: ", peersString)
                let peersChunk = peersString.split("\n");
                for (let i=0; i<peersChunk.length && peersChunk[i].length>0; ++i) {
                  const peerColumns = peersChunk[i].split(",");
                  // console.log("index: " + i + ", data: " + peerColumns)

                  // for example: kevin@home-pc,36,1
                  // peerColumns[0]: peer_name
                  // peerColumns[1]: peer_id
                  // peerColumns[2]: peer_status
                  // table's column index starts from 1, not 0
                  const row_find_peer_id = $("#PeersTable tr:has(td:nth-child(2):contains('" + peerColumns[1] + "'))");
                  if (row_find_peer_id.length > 0) {
                    console.log("peerid:" + peerColumns[1] + ", status change from " + row_find_peer_id.find('td:nth-child(3)').text() + " to " + peerColumns[2]);
                    row_find_peer_id.find('td:nth-child(3)').text(peerColumns[2]);
                    continue;
                  }

                  const row_find_peer_name = $("#PeersTable tr:has(td:nth-child(1):contains('" + peerColumns[0] + "'))");
                  if (row_find_peer_name.length > 0) {
                    console.log("peerid:" + peerColumns[1] + ", status change from " + row_find_peer_name.find('td:nth-child(3)').text() + " to " + peerColumns[2]);
                    row_find_peer_name.find('td:nth-child(2)').text(peerColumns[1]);
                    row_find_peer_name.find('td:nth-child(3)').text(peerColumns[2]);
                    continue;
                  }                  

                  const row_peer = $("<tr>");
                  row_peer.append($("<td>").text(peerColumns[0]));
                  row_peer.append($("<td>").text(peerColumns[1]));
                  row_peer.append($("<td>").text(peerColumns[2]));
                  $("#PeersTable").append(row_peer);
                }                
            }
        }

        async function pendingWait(myself_peerid) {
            const signal_url = document.getElementById("signal_url").value;
            while (true) {
                let v = await fetch(`http://${signal_url}/wait?peer_id=${myself_peerid}`);
                nfyFromSignal(v);
            }
        }

        $('#btn_sign_in').on('click', async () => {
            const signal_url = document.getElementById("signal_url").value;
            let signinResultPromise = await fetch(`http://${signal_url}/sign_in`);
            if (signinResultPromise.ok) {
                myselfPeerId = parseInt(signinResultPromise.headers.get("Pragma"));
                console.log("my peerid is: " + myselfPeerId);
                nfyFromSignal(signinResultPromise);
                pendingWait(myselfPeerId);               
            } else {
                alert("HTTP-Error: " + signinResultPromise.status);
            }
        })        

        $('#btn_sign_out').on('click', async () => {
            $("#PeersTable tbody").empty();
            const signal_url = document.getElementById("signal_url").value;
            let signoutResultPromise = await fetch(`http://${signal_url}/sign_out?peer_id=${myselfPeerId}`);
            if (signoutResultPromise.ok) {
                console.log("sign_out: " + myselfPeerId + " successful");       
            } else {
                alert("HTTP-Error: " + signoutResultPromise.status);
            }
        })

        $('#PeersTable').on('click', 'tr', function() {
            const row_selected = $(this);
            const row_selected_peer_id = parseInt(row_selected.find('td:nth-child(2)').text());
            
            if (row_selected_peer_id === myselfPeerId) {
                console.log("You should not choose yourself [" + myselfPeerId + "] to start p2p");
                $("#btn_start_p2p").prop("disabled", true);
                return;
            }

            const row_selected_peer_id_status = parseInt(row_selected.find('td:nth-child(3)').text());
            if (row_selected_peer_id_status === 1) {
                console.log("selected peerid: " + row_selected_peer_id);
                $('#selected_peerid_for_p2p').val(row_selected_peer_id);
                $("#btn_start_p2p").prop("disabled", false);                
            } else {
                console.log("The peer [" + row_selected_peer_id + "] you choose is offline");
                $("#btn_start_p2p").prop("disabled", true);
            }
        })

        $('#btn_start_p2p').on('click', async () => {
            const signal_url = document.getElementById("signal_url").value;
            const remote_peer_id = $('#selected_peerid_for_p2p').val();
            const sendmsg_url = `http://${signal_url}/message?peer_id=${myselfPeerId}&to=${remote_peer_id}`;
            console.log("sendmsg url: " + sendmsg_url);

            // $('#rtc_video_play').show();
            // $('#rtc_video_play').prop('muted', false);
            $('#rtc_video_play').prop('srcObject', stream);

            console.log("Start P2P from [" + myselfPeerId + "] to [" + remote_peer_id + "]");
            startP2P(sendmsg_url);
        })

    </script>
</body>
</html>
09-12 06:18