基於surging 如何利用peerjs進行語音視頻通話

来源:https://www.cnblogs.com/fanliang11/p/18388027
-Advertisement-
Play Games

本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...


  一 、 概述

          PeerJS 是一個基於瀏覽器WebRTC功能實現的js功能包,簡化了WebrRTC的開發過程,對底層的細節做了封裝,直接調用API即可,再配合surging 協議組件化從而做到穩定,高效可擴展的微服務,再利用RtmpToWebrtc 引擎組件可以做到不僅可以利用httpflv 觀看rtmp推流直播,還可以採用基於 Webrtc的peerjs 進行觀看,那麼今天要講的是如何結合開發語音視頻通話功能。放到手機和電腦上都可以實現語音視頻通話。

    一鍵運行打包成品下載:https://pan.baidu.com/s/1MVsjKtVYpUonauAz9ZXtPg?pwd=1q2g

    測試用戶:fanly

    測試密碼:123456

      為了讓大家節約時間,能儘快運行產品看到效果,上面有 一鍵運行打包成品可以進行下載測試運行。

  二、如何測試運行

以下是目錄結構,

IDE:consul 註冊中心

kayak.client: 網關

kayak.server:微服務

apache-skywalking-apm:skywalking鏈路跟蹤

 

 以上是目錄結構, 不需要進入管理界面配置網路組件,啟動後自帶埠96的ws協議主機,只要打開video文件夾,裡面有兩個語音通話的html測試文件,在同一一個區域網只要輸入對方的name就可以進行語音通話

 打開界面如圖

 

三、基於surging如何開發

以上是沒有開發環境的進行下載進行下載測試,那麼正在使用surging 的如何開發此功能呢?

1. 創建服務介面,繼承於IServiceKey

 [ServiceBundle("Device/{Service}")]
 public  interface IChatService : IServiceKey
 {
 }

2. 創建中間服務,繼承於WSBehavior, IChatService

  internal class ChatService : WSBehavior, IChatService
  {
      private static readonly ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
      private static readonly ConcurrentDictionary<string, string> _clients = new ConcurrentDictionary<string, string>();

      protected override void OnOpen()
      {
         var _name = Context.QueryString["name"]; 
          if (!string.IsNullOrEmpty(_name))
          {
              _clients[ID] = _name;
              _users[_name] = ID;
          }
      }

      protected override void OnError( WebSocketCore.ErrorEventArgs e)
      {
        var msg = e.Message;
      }

      protected override void OnMessage(MessageEventArgs e)
      {
          if (_clients.ContainsKey(ID))
          {

              var message = JsonConvert.DeserializeObject<Dictionary<string, object>>(e.Data);
              //消息類型
             message.TryGetValue("type",out object @type);
              message.TryGetValue("toUser", out object toUser);
              message.TryGetValue("fromUser", out object fromUser);
              message.TryGetValue("msg", out object msg);
              message.TryGetValue("sdp", out object sdp);
              message.TryGetValue("iceCandidate", out object iceCandidate);
              

              Dictionary<String, Object> result = new Dictionary<String, Object>();
              result.Add("type", @type);

              //呼叫的用戶不線上
              if (!_users.ContainsKey(toUser?.ToString()))
              {
                  result["type"]= "call_back";
                  result.Add("fromUser", "系統消息");
                  result.Add("msg", "Sorry,呼叫的用戶不線上!");

                  this.Client().SendTo(JsonConvert.SerializeObject(result), ID);
                  return;
              }

              //對方掛斷
              if ("hangup".Equals(@type))
              {
                  result.Add("fromUser", fromUser);
                  result.Add("msg", "對方掛斷!");
              }

              //視頻通話請求
              if ("call_start".Equals(@type))
              {
                  result.Add("fromUser", fromUser);
                  result.Add("msg", "1");
              }

              //視頻通話請求回應
              if ("call_back".Equals(type))
              {
                  result.Add("fromUser", toUser);
                  result.Add("msg", msg);
              }

              //offer
              if ("offer".Equals(type))
              {
                  result.Add("fromUser", toUser); 
                  result.Add("sdp", sdp);
              }

              //answer
              if ("answer".Equals(type))
              {
                  result.Add("fromUser", toUser);
                  result.Add("sdp", sdp);
              }

              //ice
              if ("_ice".Equals(type))
              {
                  result.Add("fromUser", toUser);
                  result.Add("iceCandidate", iceCandidate);
              }

              this.Client().SendTo(JsonConvert.SerializeObject(result), _users.GetValueOrDefault(toUser?.ToString()));
          }
      }

      protected override void OnClose(CloseEventArgs e)
      {
         if( _clients.TryRemove(ID, out string name))
          _users.TryRemove (name, out string value);
      }

  }

3.設置surgingSettings的WSPort埠配置,預設96

以上就是利用websocket協議中轉消息,下麵是頁面如何編號,代碼如下:

<!DOCTYPE>
<!--解決idea thymeleaf 表達式模板報紅波浪線-->
<!--suppress ALL -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>WebRTC + WebSocket</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
    <style>
        html,body{
            margin: 0;
            padding: 0;
        }
        #main{
            position: absolute;
            width: 370px;
            height: 550px;
        }
        #localVideo{
            position: absolute;
            background: #757474;
            top: 10px;
            right: 10px;
            width: 100px;
            height: 150px;
            z-index: 2;
        }
        #remoteVideo{
            position: absolute;
            top: 0px;
            left: 0px;
            width: 100%;
            height: 100%;
            background: #222;
        }
        #buttons{
            z-index: 3;
            bottom: 20px;
            left: 90px;
            position: absolute;
        }
        #toUser{
            border: 1px solid #ccc;
            padding: 7px 0px;
            border-radius: 5px;
            padding-left: 5px;
            margin-bottom: 5px;
        }
        #toUser:focus{
            border-color: #66afe9;
            outline: 0;
            -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
            box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
        }
        #call{
            width: 70px;
            height: 35px;
            background-color: #00BB00;
            border: none;
            margin-right: 25px;
            color: white;
            border-radius: 5px;
        }
        #hangup{
            width:70px;
            height:35px;
            background-color:#FF5151;
            border:none;
            color:white;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div id="main">
        <video id="remoteVideo" playsinline autoplay></video>
        <video id="localVideo" playsinline autoplay muted></video>

        <div id="buttons">
            <input id="toUser" placeholder="輸入線上好友賬號"/><br/>
            <button id="call">視頻通話</button>
            <button id="hangup">掛斷</button>
        </div>
    </div>
</body>
<!-- 可引可不引 -->
<!--<script th:src="@{/js/adapter-2021.js}"></script>-->
<script type="text/javascript" th:inline="javascript">
    let username = "fanly";
    let localVideo = document.getElementById('localVideo');
    let remoteVideo = document.getElementById('remoteVideo');
    let websocket = null;
    let peer = null;

    WebSocketInit();
    ButtonFunInit();

    /* WebSocket */
    function WebSocketInit(){
        //判斷當前瀏覽器是否支持WebSocket
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://127.0.0.1:961/device/chat?name="+username);
        } else {
            alert("當前瀏覽器不支持WebSocket!");
        }

        //連接發生錯誤的回調方法
        websocket.onerror = function (e) {
            alert("WebSocket連接發生錯誤!");
        };

        //連接關閉的回調方法
        websocket.onclose = function () {
            console.error("WebSocket連接關閉");
        };

        //連接成功建立的回調方法
        websocket.onopen = function () {
            console.log("WebSocket連接成功");
        };

        //接收到消息的回調方法
        websocket.onmessage = async function (event) {
            let { type, fromUser, msg, sdp, iceCandidate } = JSON.parse(event.data.replace(/\n/g,"\\n").replace(/\r/g,"\\r"));

            console.log(type);

            if (type === 'hangup') {
                console.log(msg);
                document.getElementById('hangup').click();
                return;
            }

            if (type === 'call_start') {
                let msg = "0"
                if(confirm(fromUser + "發起視頻通話,確定接聽嗎")==true){
                    document.getElementById('toUser').value = fromUser;
                    WebRTCInit();
                    msg = "1"
                }

                websocket.send(JSON.stringify({
                    type:"call_back",
                    toUser:fromUser,
                    fromUser:username,
                    msg:msg
                }));

                return;
            }

            if (type === 'call_back') {
                if(msg === "1"){
                    console.log(document.getElementById('toUser').value + "同意視頻通話");

                    //創建本地視頻併發送offer
                    let stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
                    localVideo.srcObject = stream;
                    stream.getTracks().forEach(track => {
                        peer.addTrack(track, stream);
                    });

                    let offer = await peer.createOffer();
                    await peer.setLocalDescription(offer); 
                    let newOffer = offer;
   
                    newOffer["fromUser"] = username;
                    newOffer["toUser"] = document.getElementById('toUser').value;
                    websocket.send(JSON.stringify(newOffer));
                }else if(msg === "	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Springboot黑馬點評(3)——優惠券秒殺 【還剩Redisson的最後兩節沒測試 後續補上】 另外,後期單獨整理一份關於分散式鎖筆記 1 優惠券秒殺實現 1.1 用戶-優惠券訂單設計 1.1.1 全局ID生成器 使用資料庫自增ID作為訂單ID存在問題 1.1.2 考慮全局唯一ID生成邏輯 時 ...
  • 原文地址https://blog.fanscore.cn/a/61/ 1. wssh 1.1 開發背景 公司內部的發佈系統提供一個連接到k8s pod的web終端,可以在網頁中連接到k8s pod內。實現原理大概為通過websocket協議代理了k8s pod ssh,然後在前端通過xterm.js ...
  • 我們在某寶或某多多上搶購商品時,如果只是下了訂單但沒有進行實際的支付,那在訂單頁面會有一個支付倒計時,要是過了這個時間點那麼訂單便會自動取消。在這樣的業務場景中,一般情況下就會使用到延時隊列。 ...
  • 國內文章 【音視頻通話】使用asp.net core 8+vue3 實現高效音視頻通話 https://www.cnblogs.com/1996-Chinese-Chen/p/18384394 該文章描述了使用SRS實現音視頻通話和共用桌面的經驗。從最初使用nginx的RTMP到研究SRS和ZLMe ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...