h5端如何使用webrtc

1.無(wú)意中接觸到webrtc,了解到的就是直播過(guò)程中延遲很短哄芜,控制在1-2s左右貌亭,開(kāi)發(fā)過(guò)程中發(fā)現(xiàn)關(guān)于webrtc的參考案例很少,就把demo貼出來(lái)认臊,也順便把遇到的問(wèn)題和如何解決的也貼出來(lái)圃庭。

<!DOCTYPE html>
<html>
<head>
  <title>CDN Demo</title>
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1" name="viewport">
  <link  rel="stylesheet">
  <link  rel='stylesheet'>
  <style>
      #remote-video-wrap div{max-width:800px; display: inline-block; padding:10px;}
      #remote-video-wrap video{max-width:800px; display: inline-block; padding:10px} 
  </style>
</head>
<body>
    <div id="input-container" class="container">
    
        <div class="login-box">
          <div class="form-group">
              <label for="streamurl">拉流地址:</label>
              <input class="form-control" id="streamurl" type="text" value="webrtc://xxxxxx" /> //webrtc類型的流地址
          </div>
          <button  style="float: left;margin:10px;" type="button" onclick="pullStream()" id="pullFun">拉流</button>
          <button  style="float: left;margin:10px;" type="button" onclick="stopStream()">停止拉流</button>
          <!-- <button  style="float: left;margin:10px;" type="button" onclick="refreshStream()">刷新</button>
          <button  style="float: left;margin:10px;" type="button" onclick="opennewpage()">打開(kāi)新的頁(yè)面</button> -->
          <!-- <a  target="_top">打開(kāi)新的頁(yè)面</a> -->
        </div>
      </div>
    

    <div id="video-section" style="float:left;margin:195px 0px 0px 75px;">
            <div id="remote-video-wrap">
    
            </div>
        </div>
      </div>

      <script>
        window._tx_webrtc_enable  = true;
        // delete window.RTCPeerConnection;
        // delete window.webkitRTCPeerConnection;
        // delete window.RTCIceGatherer;
      </script>

  <script src="https://unpkg.com/flyio/dist/fly.min.js"></script>
  <script src="https://cloud.webrtc.qq.com/httpdemo/js/jquery.min.js"></script>
  <script src="https://cloud.webrtc.qq.com/httpdemo/js/adapter.js"></script>  //很有必要 兼容瀏覽器
  <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>  //用于ios端視頻不能自動(dòng)播放
  <script>
      
function getStreamId(url){
    //var url = "webrtc://domain/path/stream_id[?txSecret=xxx&txTime=xxx]";
    var parseStreamid = /^(?:webrtc:\/\/)(?:[0-9.\-A-Za-z_]+)(?:\/)(?:[0-9.\-A-Za-z_]+)(?:\/)([^?#]*)(?:\?*)(?:[^?#]*)/;
    var result = parseStreamid.exec(url);
    if(result)
        return result[1];
    return null;
}

function createVideoElement(id) {
    var videoDiv = document.createElement("div");
    videoDiv.innerHTML = `<video id="${id}" autoplay unmuted playsinline controls></video>`; //width="480" height="320"
    document.querySelector("#remote-video-wrap").appendChild(videoDiv);
    
    return document.getElementById(id);
}

function onAddStream(e){
    console.log(`onAddStream, streamId:${e.streamId}`, e.stream);
    var streamId = e.streamId;
    var video = document.getElementById(streamId);
    if(!video){
        video = createVideoElement(streamId);
    }

    video.srcObject = e.stream;
    //video.muted = true
    video.autoplay = true
    video.playsinline = true
    var playPromise = video.play();
    if(playPromise){
        playPromise.then(() => {
            //此處監(jiān)聽(tīng)到webrtc流地址可以播放 
            console.log(`play ok!`);
        }).catch(() => {
            //此處可做其他類型的流地址切換
            //webrtc地址不可播放
            console.log(`play failed!`);
        });
    }
}

var streamUrl;
var svrSig;
var peerConnection;

function pullStream(){

    var clientInfo = "clientinfo_test";
    var sessionId = "sessionId_Test";
    streamUrl = $("#streamurl").val();
    var streamId = getStreamId(streamUrl);
    var video = document.getElementById(streamId);
    if(!video){
        video = createVideoElement(streamId);
    }

    var config = {
        iceServers: [],
        bundlePolicy: "max-bundle",
        rtcpMuxPolicy: "require",
        tcpCandidatePolicy: "disable",
        IceTransportsType: "nohost",
        sdpSemantics: 'unified-plan'
    };

    var optional = {
        optional: [{
            DtlsSrtpKeyAgreement: true
        }]
    };

    var offerSdpOption = {
        offerToReceiveAudio: true,
        offerToReceiveVideo: true,
        voiceActivityDetection: false
      };

    peerConnection = new RTCPeerConnection(config, optional);
    peerConnection.onicecandidate = function(e) {
        console.log("peerConnection.onicecandidate:", e);
    };
    peerConnection.onaddstream = function(e) {
        console.log("peerConnection.onaddstream");
    };
    peerConnection.onremovestream = function(e) {
        console.log("peerConnection.onremovestream");
    };
    peerConnection.ontrack = function(e) {
        console.log("peerConnection.ontrack, kind:" + e.track.kind + ",track.id:" + e.track.id);

        var track = e.track;
        if(!peerConnection.stream){
            peerConnection.streamId = streamId;
            peerConnection.stream = new MediaStream();
            peerConnection.stream.addTrack(track);

            onAddStream(peerConnection);
        } else{
            peerConnection.stream.addTrack(track);
            console.log("track.lenght:" + peerConnection.stream.getTracks().length);
        }
    };
    peerConnection.onconnectionstatechange = function(e) {
        console.log("onconnectionstatechange", e);
    }
    peerConnection.oniceconnectionstatechange = function(e) {
        console.log("peerConnection.oniceconnectionstatechange: " + JSON.stringify(e), e);
    };
    peerConnection.onicegatheringstatechange = function(e) {
        console.log("peerConnection.onicegatheringstatechange : " + e.target.iceGatheringState, e);
    };
    peerConnection.onsignalingstatechange = function(e) {
        console.log("peerConnection.onsignalingstatechange : " + peerConnection.signalingState, e);
    };
    peerConnection.onnegotiationneeded = function(e) {
        console.log("peerConnection.onnegotiationneeded", e);
    };


    peerConnection.createOffer(offerSdpOption).then(function(offer) {
        peerConnection.setLocalDescription(offer);
        var url = "https://webrtc.liveplay.myqcloud.com/webrtc/v1/pullstream"; 
        var reqBody = {
            streamurl: streamUrl,
            sessionid:  sessionId,
            clientinfo: clientInfo,
            localsdp: offer
        };

         $.ajax({
             type: "post",
             url: url,
             data: JSON.stringify(reqBody),
             //contentType: "application/json; charset=utf-8",
             dataType: "json",
             success: pullStreamRes,
             crossDomain: true
         });
        //fly.interceptors.request.use((request) => {
         //   request.headers['Content-Type'] ='application/json';
         //   request.headers['Access-Control-Allow-Origin'] ='*';
         //   request.headers['Access-Control-Allow-Credentials'] ='true';
        //})
        //fly.post(url, JSON.stringify(reqBody)).then(pullStreamRes).catch(function(error){
         //   console.log("fly post err:", error);
        //});

        function pullStreamRes(data){
            console.log("pullStreamRes:", data);
        //    data = data.data;
            var errCode = data.errcode;
            var errMsg = data.errmsg;
            if(errCode != 0){
                console.log(`pull stream failed!errCode:${errCode}, errmsg:${errMsg}`);
                return;
            }

            var remoteSdp = data.remotesdp;
            svrSig = data.svrsig;

            console.log(`svrSig:${svrSig}`);

            peerConnection.setRemoteDescription(
                new RTCSessionDescription(remoteSdp),
                function(){
                    console.log("setRemoteSdp succ!");
                },
                function(e){
                    console.log("setRemoteSdp failed, exception = " + e.message);
                }
            );
        }
    }).catch(function(reason) {
        console.log('create offer failed : reason = ' + reason);
      });
}


function stopStream(){
    var url = "https://webrtc.liveplay.myqcloud.com/webrtc/v1/stopstream";
    var reqBody = {
        streamurl: streamUrl,
        svrsig:  svrSig
    };

     $.ajax({
         type: "post",
         url: url,
         data: JSON.stringify(reqBody),
         //contentType: "application/json; charset=utf-8",
         dataType: "json",
         success: stopStreamRes,
        crossDomain: true
     });

    //fly.interceptors.request.use((request) => {
    //    request.headers['Content-Type'] ='application/json';
    //    request.headers['Access-Control-Allow-Origin'] ='*';
    //    request.headers['Access-Control-Allow-Credentials'] ='true';
    //})
   // fly.post(url, JSON.stringify(reqBody)).then(stopStreamRes).catch(function(error){
    //    console.log("fly post err:", error);
    //});

    function stopStreamRes(data){
        console.log(`stopStreamRes:`, data);
        //data = data.data;
        var errCode = data.errcode;
        var errMsg = data.errmsg;
        if(errCode != 0){
            console.log(`pull stream failed!errCode:${errCode}, errmsg:${errMsg}`);
        }

        peerConnection.close();
        var streamId = getStreamId(streamUrl);
        var videoNode = document.getElementById(streamId);
        if (videoNode) {
            document.getElementById(streamId).parentElement.removeChild(videoNode);
            console.log(`RemoveStream, streamUrl:${streamUrl}`);
        } else{
            console.log(`RemoveStream, streamUrl:${streamUrl} not exist`);
        }
    }
    
}

//此處用來(lái)調(diào)取微信console面板  用于開(kāi)發(fā)審查(很有必要)
function createVConsole() {
    var scriptEle = document.createElement("script");
    scriptEle.src = '//sqimg.qq.com/expert_qq/vConsole/vconsole.min.js';
    document.body.appendChild(scriptEle)
    scriptEle.onload = function(){
        window.vConsole = new VConsole();
    }
};

createVConsole();

//此處作用:因ios端視頻禁止自動(dòng)播放  故寫此代碼用來(lái)調(diào)取視頻播放
let video = document.querySelectorAll("video")[0];
  document.addEventListener("WeixinJSBridgeReady", function () {
    pullStream();  //拉流(相當(dāng)于手動(dòng)操作拉流按鈕,因?qū)嶋H項(xiàng)目中沒(méi)有此按鈕來(lái)手動(dòng)拉流)
     video.play();
}, false);
  </script>
  </body>
</html>

2.遇到的問(wèn)題
2.1 問(wèn)題:ios微信瀏覽器播放器不能自動(dòng)播放 導(dǎo)致stream監(jiān)聽(tīng)事件提示play failed美尸,因業(yè)務(wù)中需區(qū)分webrtc能不能播放冤议,不能播放則切換至其他流地址進(jìn)行播放
解決方案:引入jweixin-1.0.0.js 監(jiān)聽(tīng)WeixinJSBridgeReady觸發(fā)事件即可(參考網(wǎng)絡(luò)案例,還有其他方式师坎,此處只提供一種恕酸,如不能解決,歡迎留言胯陋。)
2.2 ios微信瀏覽器一開(kāi)始不能播放webrtc蕊温,但是在微信開(kāi)發(fā)者工具則正常播放
解決方案:引入adapter.js即可解決瀏覽器兼容性問(wèn)題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遏乔,隨后出現(xiàn)的幾起案子义矛,更是在濱河造成了極大的恐慌,老刑警劉巖盟萨,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凉翻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捻激,警方通過(guò)查閱死者的電腦和手機(jī)制轰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門前计,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人垃杖,你說(shuō)我怎么就攤上這事男杈。” “怎么了调俘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵伶棒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我彩库,道長(zhǎng)肤无,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任骇钦,我火速辦了婚禮舅锄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘司忱。我一直安慰自己,他們只是感情好畴蹭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布坦仍。 她就那樣靜靜地躺著,像睡著了一般叨襟。 火紅的嫁衣襯著肌膚如雪繁扎。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天糊闽,我揣著相機(jī)與錄音梳玫,去河邊找鬼。 笑死右犹,一個(gè)胖子當(dāng)著我的面吹牛提澎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播念链,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盼忌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了掂墓?” 一聲冷哼從身側(cè)響起谦纱,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎君编,沒(méi)想到半個(gè)月后跨嘉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吃嘿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年祠乃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梦重。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跳纳,死狀恐怖忍饰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寺庄,我是刑警寧澤艾蓝,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站斗塘,受9級(jí)特大地震影響赢织,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜馍盟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一于置、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贞岭,春花似錦八毯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至芯侥,卻和暖如春泊交,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柱查。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工廓俭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唉工。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓研乒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酵紫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子告嘲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354