WebRTC-H5视频通话

什么是webRTC?

WebRTC(Web Real-Time Communication)是一种用于在Web浏览器之间实时传输音频、视频和数据的开放标准和技术集合。 WebRTC 提供了一组 API 和协议,使得开发者可以直接在Web浏览器中实现点对点的实时通信,而无需使用第三方插件或应用程序。它通过使用浏览器内置的音频、视频和数据通道,实现了实时的音视频传输和数据传输。 主要特点和功能:

  1. 实时音视频通信:WebRTC 可以在不同的浏览器之间直接传输音频和视频流,实现实时的语音通话和视频通话。
  2. 数据传输:除了音视频流,WebRTC 还提供了可靠的数据传输通道(DataChannel),可以用于传输任意类型的数据。
  3. 点对点通信:WebRTC 的设计目标之一是支持点对点的通信,即直接在浏览器之间建立连接,不需要经过服务器的转发。
  4. 安全性:WebRTC 内置了强大的安全性机制,包括加密和身份验证,以确保通信过程的机密性和完整性。
  5. 跨平台支持:WebRTC 支持在不同的平台和设备上进行实时通信,包括桌面浏览器、移动浏览器和移动应用程序。

WebRTC 在实时通信、视频会议、在线教育、远程协作、在线游戏和物联网等领域有着广泛的应用。它为开发者提供了强大的工具和标准,使得构建实时通信应用变得更加简便和可靠。

音视频采集

WebRTC使用getUserMedia获取摄像头与话筒对应的媒体流对象MediaStream,媒体流可以通过WebRTC进行传输,并在多个对等端之间共享。将流对象赋值给视频元素的srcObject,实现本地播放音视频

配置项

描述

audio

指定是否捕获音频流。值为 true 表示捕获音频流,值为 false 表示不捕获音频流。

video

指定是否捕获视频流。值为 true 表示捕获视频流,值为 false 表示不捕获视频流。

audio.deviceId

指定要使用的音频输入设备的唯一标识符。

audio.groupId

指定音频输入设备所属的设备组的唯一标识符。

audio.echoCancellation

指定是否启用回声消除。值为 true 表示启用回声消除,值为 false 表示禁用回声消除。

audio.autoGainControl

指定是否启用自动增益控制。值为 true 表示启用自动增益控制,值为 false 表示禁用自动增益控制。

audio.noiseSuppression

指定是否启用降噪。值为 true 表示启用降噪,值为 false 表示禁用降噪。

audio.latency

指定音频流的延迟时间(以毫秒为单位)。

video.deviceId

指定要使用的视频输入设备的唯一标识符。

video.groupId

指定视频输入设备所属的设备组的唯一标识符。

video.width

指定视频流的宽度(以像素为单位)。

video.height

指定视频流的高度(以像素为单位)。

video.frameRate

指定视频流的帧率(每秒帧数)。

video.facingMode

指定所需的摄像头方向。可以是 "user"(前置摄像头)或 "environment"(后置摄像头)。

 function getUserMedia() {
        navigator.mediaDevices.getUserMedia({
            video: {facingMode: facingMode},
            audio: true
        }).then(stream => {
            localStream = stream
            const localVideo = document.getElementById('localVideo');
            localVideo.srcObject = stream;

        });
    }

连接管理

不采用原生webrtc 进行管理连接,原生 API 使用起来较为复杂 ;使用peerJs 组件进行管理连接,PeerJS 在 WebRTC 的基础上提供了更高层次的抽象,使得开发者能够更轻松地构建基于点对点通信的应用程序。

什么是 PeerJS?

PeerJS 是一个基于 WebRTC 技术的 JavaScript 库,用于简化点对点(peer-to-peer)通信的实现。 WebRTC 是一种在浏览器之间实现实时音视频通信的开放标准,但其原生 API 使用起来较为复杂。PeerJS 在 WebRTC 的基础上提供了更高层次的抽象,使得开发者能够更轻松地构建基于点对点通信的应用程序。 PeerJS 提供了以下功能和特性:

  1. 简化的 API:PeerJS 提供了简洁的 API,使得创建和管理对等连接(peer connection)变得更加容易。
  2. 自动信令服务:PeerJS 使用一个信令服务器来协助对等连接的建立。开发者无需自行设置信令服务器,PeerJS 提供了默认的信令服务器供使用。
  3. 可靠的数据通道:PeerJS 提供了可靠的数据通道,使得在对等连接中传输数据变得简单可靠。
  4. 兼容性:PeerJS 支持主流的现代浏览器,并提供了跨平台的支持,包括桌面浏览器和移动浏览器。

PeerJS 的使用示例:

// 创建 Peer 对象
const peer = new Peer('my-peer-id', {
  host: 'peerjs-server.com',
  port: 9000,
  path: '/myapp'
});

// 监听连接建立事件
peer.on('open', id => {
  console.log('Connected with ID:', id);
});

// 建立对等连接
const conn = peer.connect('another-peer-id');

// 监听连接打开事件
conn.on('open', () => {
  conn.send('Hello from peer 1!');
});

// 监听接收到消息事件
conn.on('data', data => {
  console.log('Received:', data);
});

peerjs-server

PeerJS Server 可以被部署在您自己的服务器上,或者使用 PeerJS 提供的公共信令服务器。公共信令服务器是免费提供给开发者使用的,但也可以选择自己托管信令服务器以获得更高的灵活性和控制。 使用 PeerJS Server 的好处包括:

  1. 信令交换:PeerJS Server 用于在对等连接的建立过程中传递信令数据,包括对等连接的元数据、ICE 候选项和 SDP(会话描述协议)等。
  2. 中继服务器:在某些情况下,对等连接的直接建立可能受到网络环境的限制,需要通过中继服务器进行中转。PeerJS Server 可以作为中继服务器,帮助在无法直接连接的情况下建立对等连接。
  3. 认证和安全性:PeerJS Server 可以提供认证和安全性机制,确保只有授权的用户可以进行对等连接,并保护数据的隐私和完整性。

如何使用 PeerJS Server:

  1. 使用 PeerJS 的默认公共信令服务器:PeerJS 提供了一个默认的公共信令服务器,您可以在使用 PeerJS 库时直接使用它。
  2. 使用npm 下载 npm install peer -g ;运行:** ****peerjs --port 9000 --key peerjs --path /myapp**
  3. 自己部署 PeerJS Server:您也可以下载 PeerJS Server 的源代码,自行部署在自己的服务器上。PeerJS Server 是基于 Node.js 的,您需要安装 Node.js 环境,并按照 PeerJS Server 的文档进行配置和部署。

PeerJS Server 的源代码可以在 PeerJS 的 GitHub 仓库中找到,您可以在那里获取更多关于 PeerJS Server 的详细信息和配置说明。 需要注意的是,如果您选择使用 PeerJS 的默认公共信令服务器,请遵守 PeerJS 的使用政策和条款,并确保适用于您的特定用途。

广域网连接

由于NAT(网络地址转换)和防火墙的存在,直接在两个设备之间建立点对点连接通常是困难的。为了解决这个问题,WebRTC使用了打洞技术,利用中间服务器作为中继来帮助设备建立直接连接,从而实现端到端的实时通信。 打洞服务通过以下步骤来建立直接连接:

  1. 设备A和设备B都向打洞服务发送连接请求。
  2. 打洞服务将设备A和设备B的连接信息(IP地址和端口号)传递给对方。
  3. 设备A和设备B尝试直接连接对方,通过在NAT和防火墙上创建映射,使得两个设备可以直接通信。
  4. 如果直接连接失败,设备A和设备B将尝试使用中继服务器进行通信。

打洞服务通常使用一些技术来实现这种直接连接,例如STUN(会话遍历实用工具)和TURN(中继通信)。STUN用于获取设备的公共IP地址和端口号,TURN用于在直接连接失败时提供中继服务器作为备用通信路径。 打洞服务对于WebRTC中的实时通信至关重要,它使得两个设备可以直接通信,无需经过中间服务器的转发,从而实现更低的延迟和更高的性能。 需要注意的是,打洞服务只是WebRTC连接建立的一部分,实际的通信数据传输仍然是直接的点对点连接。

示例Demo

使用webRTC实现一个1V1网页视频通话

引入PeerJs组件

这里使用cdn 的方式与引入,可查看文档使用其他引入方式 <script src="https://unpkg.com/peerjs@1.4.7/dist/peerjs.min.js"></script> <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> 注意: 上面引入了adapter-latest.js组件 **adapter-latest.js** 是一个 JavaScript 库,它是由 WebRTC 团队维护的一个开源项目,旨在提供对不同浏览器之间的 WebRTC API 的规范化和标准化。 WebRTC API 在不同的浏览器中实现存在一些差异,而 adapter-latest.js 的目的是通过封装和规范化这些差异,使得开发者可以更方便地使用 WebRTC API 跨浏览器开发实时通信应用。 adapter-latest.js 提供了以下功能:

  1. 规范化 API:adapter-latest.js 通过提供规范化的 API,使开发者可以使用统一的方式访问 WebRTC API,无论是在 Chrome、Firefox、Safari 还是其他支持 WebRTC 的浏览器上。
  2. 自动适配:adapter-latest.js 可以根据浏览器的类型和版本自动适配相关的 WebRTC API 实现,从而隐藏不同浏览器之间的差异。
  3. Polyfill 功能:对于不支持某些 WebRTC API 的浏览器,adapter-latest.js 可以提供 Polyfill 功能,通过模拟这些 API 的行为来实现兼容性。

adapter-latest.js 直接引入就OK了。

切换前后摄像头

切换前后摄像头需要重新获取视频流并替换原有的视频流 通过 facingMode 属性控制前后摄像头,facingMode :user:前置摄像头;environment 后置摄像头, 关键步骤: stream:视频流 replaceTrack() 替换当前正在使用的轨道

stream.getVideoTracks().forEach(track => {
                        call.peerConnection.getSenders().forEach((sender) => {
                            if (sender.track.kind === 'video') {
                                sender.replaceTrack(track).then(() => console.log('replace'));
                            }
                        });
                    })

ios 微信自带浏览器上webrtc不能正常使用的解决方法

解决方法:

 window.onload = () => {
        document.addEventListener("WeixinJSBridgeReady", function () {
            document.getElementById("localVideo").play();
        }, false);
    }
 <video id="localVideo" preload="auto" autoplay="autoplay"
               x-webkit-airplay="true"
               playsinline="true" webkit-playsinline="true" x5-video-player-type="h5" x5-video-player-fullscreen="true"
               x5-video-orientation="portraint" muted></video>
               

值得注意的是,WeixinJSBridgeReady这个事件会在页面加载后马上触发,因此,上面的这个代码最好写在window.οnlοad=>(){}函数体中,所以video标签也要提前写在html网页中,不要等webrtc通道建立后再去动态创建video。

完整代码

<!DOCTYPE html>
<html lang="zh">
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <head>
    <title>WebRTC 视频通话演示</title>
    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  </head>
  <body>
    <h1>WebRTC 视频通话演示</h1>
    <div>
      <p>
        我的peerId:<span id="myPid"></span>
      </p>
      <label for="remotePeerIdInput">对方 Peer ID:</label>
      <input type="text" id="remotePeerIdInput">
      <button id="callButton">呼叫</button>
      <button id="hangupButton">挂断</button>
      <button id="muteButton">静音</button>
      <button id="switchButton">切换摄像头</button>
    </div>
    <div style="display: flex">
      <div style="background: aquamarine">
        <h2>本地视频</h2>
        <video id="localVideo" width="720" height="240" width="100%" height="300" preload="auto" autoplay="autoplay"
          x-webkit-airplay="true"
          playsinline="true" webkit-playsinline="true" x5-video-player-type="h5" x5-video-player-fullscreen="true"
          x5-video-orientation="portraint" muted></video>
      </div>
      <div style="background: bisque">
        <h2>远程视频</h2>
        <video id="remoteVideo" width="720" height="240" autoplay></video>
      </div>
    </div>

    <script src="https://unpkg.com/peerjs@1.4.7/dist/peerjs.min.js"></script>
    <script>
      let peer = null;
      // 处理来自远程 Peer 的呼叫请求
      let call;
      let dataConn;
      // 初始化摄像头方向为前置
      let facingMode = 'user';
      let localStream = null
      const localVideo = document.getElementById('localVideo');

      const init = () => {
        // 初始化 Peer 对象,指定唯一的 ID
        peer = new Peer({
          /* host: "xxxxxxxx",
             path: "/peerjswss/myapp",
             port: 443,
             debug: 2,
             secure: true,*/
        });

        // 输出 Peer ID 到控制台
        peer.on('open', function (peerId) {
          console.log('我的 Peer ID 是: ' + peerId);
          document.getElementById("myPid").innerText = peerId
        });

        peer.on('connection', (conn) => {
          dataConn = conn
          conn.send("连接成功...............")
          conn.on('data', function (data) {
            console.log('被呼叫方-接收消息--》', data);
            if (data === "挂断") {
              console.log("被呼叫方信息——》对方挂断电话")
              call.close()
              call = null
              dataConn.close()
              dataConn = null
              console.log("call", call)
              console.log("dataConn", dataConn)
              // 清空远程视频元素
              const remoteVideo = document.getElementById('remoteVideo');
              remoteVideo.srcObject = null;
              alert("对方已挂断............")
            }
          });

        });

        peer.on('call', async incomingCall => {
          console.log("监听呼叫")
          // 如果已有呼叫正在进行中,则拒接新的呼叫
          if (call && call.open) {
            console.log('拒绝新的呼叫');
            incomingCall.answer();
            incomingCall.close();
            return;
          }

          // 显示确认对话框,以便用户确定是否接听呼叫
          const confirmed = window.confirm('来自 ' + incomingCall.peer + ' 的呼叫。是否接听?');
          if (!confirmed) {
            console.log('呼叫被用户拒绝');
            incomingCall.answer();
            incomingCall.close();
            return;
          }
          await getUserMedia()
          // 接听呼叫,并发送本地媒体流
          console.log('接听呼叫');
          call = incomingCall;
          incomingCall.answer(localStream);

          // 处理来自远程 Peer 的媒体流
          incomingCall.on('stream', remoteStream => {
            const remoteVideo = document.getElementById('remoteVideo');
            remoteVideo.srcObject = remoteStream;
          });

          // 处理呼叫关闭事件
          incomingCall.on('close', () => {
            console.log('呼叫被远程 Peer 关闭');
            if (call && call.open) {
              console.log('关闭已有呼叫');
              call.close();
              call = null;
            }
            const remoteVideo = document.getElementById('remoteVideo');
            remoteVideo.srcObject = null;
          });
        });
      }

      // 获取用户媒体流的函数,根据facingMode参数获取不同摄像头的流
      function getUserMedia() {

        return new Promise((resolve, reject) => {
          // 检查浏览器是否支持 getUserMedia API
          if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
            alert("getUserMedia is not supported");
            reject(new Error("getUserMedia is not supported"));
          }
          navigator.mediaDevices.getUserMedia({
            video: {
              // enviroment 后置 | user 前置摄像头
              facingMode: facingMode,
              // 视频帧率
              frameRate: 30,
            },
            audio: {}
          }).then(stream => {
            resolve(stream);
            localStream = stream
            localVideo.srcObject = stream;

            // 如果已经存在call,则需要将新的流替换到call中
            if (call) {
              console.log("---> ", call.peerConnection.getSenders())
              stream.getVideoTracks().forEach(track => {
                call.peerConnection.getSenders().forEach((sender) => {
                  if (sender.track.kind === 'video') {
                    sender.replaceTrack(track).then(() => console.log('replace'));
                  }
                });
              })
            }
          }).catch(error => {
            reject(error);
          });
        })
      }

      // 切换摄像头的函数
      function switchCamera() {
        // 切换facingMode
        facingMode = facingMode === 'user' ? 'environment' : 'user';
        // 重新获取用户媒体流
        getUserMedia();
      }

      // 在需要切换摄像头的地方调用switchCamera函数,例如:
      const switchButton = document.getElementById('switchButton');
      switchButton.addEventListener('click', switchCamera);


      // 当点击“呼叫”按钮时,向远程 Peer 发起呼叫请求
      const callButton = document.getElementById('callButton');
      const remotePeerIdInput = document.getElementById('remotePeerIdInput');
      callButton.addEventListener('click', async () => {
        const remotePeerId = remotePeerIdInput.value.trim();
        if (!remotePeerId) return;
        await getUserMedia()
        // 如果已有呼叫正在进行中,则关闭它,并发起新的呼叫请求
        if (call && call.open) {
          console.log('关闭已有呼叫');
          call.close();
        }
        dataConn = peer.connect(remotePeerId);
        dataConn.on('open', function () {
          // Send messages
          dataConn.send('我要给你打电话了............!');
          // Receive messages
          dataConn.on('data', function (data) {
            console.log('呼叫方接收消息--》', data);
            if (data === "挂断") {
              console.log("呼叫方接收消息->对方挂断电话")
              call.close()
              call = null
              dataConn.close()
              dataConn = null
              console.log("call", call)
              console.log("dataConn", dataConn)
              // 清空远程视频元素
              const remoteVideo = document.getElementById('remoteVideo');
              remoteVideo.srcObject = null;
              alert("对方已挂断............")
            }
          });

        });
        // 创建新的呼叫请求,并注册事件监听器
        console.log('发起呼叫', localStream);
        call = peer.call(remotePeerId, localStream);
        console.log("call-->", call)
        // 处理来自远程 Peer 的媒体流
        call.on('stream', remoteStream => {
          const remoteVideo = document.getElementById('remoteVideo');
          remoteVideo.srcObject = remoteStream;
        });

        // 处理呼叫关闭事件
        call.on('close', () => {
          console.log('呼叫被远程 Peer 关闭');
          if (call && call.open) {
            console.log('关闭已有呼叫');
            call.close();
            call = null;
          }
          const remoteVideo = document.getElementById('remoteVideo');
          remoteVideo.srcObject = null;
          // 停止本地媒体流
          localStream.getTracks().forEach(track => track.stop());
          localVideo.srcObject = null;
        });

        // 处理呼叫错误事件
        call.on('error', error => {
          console.error('呼叫错误:', error);
        });
      });

      // 当点击“挂断”按钮时,关闭呼叫并停止本地媒体流
      const hangupButton = document.getElementById('hangupButton');
      hangupButton.addEventListener('click', () => {
        dataConn.send("挂断")
        // 如果已有呼叫正在进行中,则关闭它
        if (call && call.open) {
          console.log('挂断呼叫');
          call.close();
          call = null;
        }

        // 停止本地媒体流
        localStream.getTracks().forEach(track => track.stop());
        localVideo.srcObject = null;

        // 清空远程视频元素
        const remoteVideo = document.getElementById('remoteVideo');
        remoteVideo.srcObject = null;
      });

      // 当点击“静音”按钮时,切换音频静音状态
      const muteButton = document.getElementById('muteButton');
      muteButton.addEventListener('click', () => {
        const audioTracks = localStream.getAudioTracks();
        audioTracks.forEach(track => {
          track.enabled = !track.enabled;
        });
        muteButton.innerText = audioTracks[0].enabled ? "静音" : "取消静音";
      });

      init()

    </script>
  </body>
</html>

粉丝福利,博主耗时2个月整理了一份详细的音视频开发学习路线,涵盖了音视频开发FFmmpeg、流媒体客户端、流媒体服务器、WebRTC、Android NDK开发、IOS音视频开发等等全栈技术栈,并提供了配套的免费领取C++音视频学习资料包、技术视频/代码,内容包括(FFmpeg ,WebRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs流媒体服务器,音视频通话等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: webrtc-qt-example是一个基于Qt框架开发WebRTC示例项目。 WebRTC是一种开源的实时通信技术,能够支持音频、视频和数据的实时传输。它通过浏览器之间的端对端连接,实现了高质量的实时通信。 webrtc-qt-example的目的是展示如何使用Qt进行WebRTC开发。Qt是一套跨平台的C++应用程序开发框架,它提供了丰富的工具和库,使开发者能够快速构建可靠的应用程序。 这个示例项目提供了一些基本的功能和界面,使开发者能够了解和学习如何将WebRTC集成到Qt应用程序中。它包含了常见的WebRTC功能,如媒体流捕获、媒体流传输、信令交换等。 通过webrtc-qt-example,开发者可以学习到如何使用Qt的多媒体模块来捕获音频、视频和媒体设备。同时,也可以学习到如何使用Qt的网络模块来进行实时信令交换和流传输。 这个示例项目还提供了一些简单的界面,方便开发者进行测试和调试。开发者可以通过该界面实现与其他WebRTC应用的通信,例如建立视频通话、音频通话等。 总之,webrtc-qt-example是一个非常实用的示例项目,可以帮助开发者快速上手并掌握WebRTC在Qt中的开发。 ### 回答2: webrtc-qt-example是一个基于Qt框架的WebRTC示例应用程序。WebRTC是一种开源项目,它提供了在浏览器之间进行实时通信的能力,包括视频和音频的传输。而webrtc-qt-example则是将这种技术集成到Qt应用程序中的一个示例。 在webrtc-qt-example中,它使用了Qt的多媒体框架和WebRTC提供的API来实现音视频的传输和显示。通过使用WebRTC的API,webrtc-qt-example可以建立点对点的连接,进行音频和视频实时传输。 webrtc-qt-example中的代码结构清晰,易于理解和扩展。它提供了一些基本的功能,如建立连接、发送和接收音视频流、呼叫取消等。开发者可以根据自己的需求来对这些功能进行定制和扩展。 此外,webrtc-qt-example还支持一些高级特性,如媒体设备的选择、音视频的编码和解码等。开发者可以通过修改代码来选择不同的媒体设备,并且可以使用不同的编码和解码算法来满足自己的需求。 总之,webrtc-qt-example是一个很棒的WebRTC示例应用程序,它可以帮助开发者快速了解和使用WebRTC技术。无论是为了实现实时视频通话视频会议还是其他需要音视频传输的应用场景,webrtc-qt-example都提供了一个良好的起点,帮助开发者快速上手并实现自己的需求。 ### 回答3: webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用。WebRTC是一种用于在Web浏览器上实现实时通信的开源项目,它提供了一套丰富的API和协议,可以实现音视频通话、数据传输以及屏幕共享等功能。 webrtc-qt-example利用Qt框架提供的跨平台能力,结合WebRTC技术,展示了在Qt应用中如何实现实时通信功能。这个示例应用具有以下特点和功能: 1. 界面友好:webrtc-qt-example使用Qt的GUI绘制工具,具有美观、直观的用户界面,便于用户操作和使用。 2. 实时通信:webrtc-qt-example内置了WebRTC音视频通信功能,可以实现实时的语音和视频通话,支持两个或多个用户之间的通信。 3. 数据传输:除了音视频通话webrtc-qt-example还支持在通话中传输数据。可以通过编写代码,实现实时文本传输或共享文件等功能。 4. 屏幕共享:webrtc-qt-example还支持屏幕共享功能,可以将自己的屏幕内容分享给其他用户,实现远程协助或在线教育等应用场景。 通过webrtc-qt-example的学习和实践,开发者可以了解并深入理解WebRTC技术的使用方法,以及在Qt框架中的应用。同时,借助webrtc-qt-example提供的示例代码和API文档,开发者可以进一步开发出更加复杂和功能丰富的实时通信应用,满足不同领域的需求。 总之,webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用,具备实时音视频通话、数据传输和屏幕共享等功能,适用于开发者学习、实践和开发基于WebRTC实时通信应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值