百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

WebRTC实现一对一音视频和类IM即时通讯

ccwgpt 2024-10-30 01:34 81 浏览 0 评论

后续pc代表PeerConnection, callerAcalleeB举例。

一对一音视频

再看看这个流程,回顾一下基本的通信思路

呼叫方A

localRtcPc为本地实例化后的PeerConnection实例,与前面整体流程有差异的地方是,现在在初始化 pc 后,直接同步获取本地摄像头和音频输入并添加到 pc 中。初始获取媒体流需要一定时间响应,如果在乎创建连接时间的,这一步可异步完成。

// 初始化呼叫者信息
const initCallerInfo = async (userId, targetUserId) => {
  mapSender.value = [];

  // 初始化 PC
  localRtcPc.value = new PeerConnection();

  // 初始化本地媒体信息
  const localStream = await getLocalUserMedia({
    video: true,
    audio: true,
  });

  // 添加轨道
  for (const track of localStream.getTracks()) {
    mapSender.value.push(localRtcPc.value.addTrack(track));
  }

  // 本地dom渲染  必须渲染完成后才能设置
  await setDomVideoStream("video", localStream);

  // 触发监听
  onPcEvent(localRtcPc.value, userId, targetUserId);

  // 创建offer
  const offer = await localRtcPc.value.createOffer({ iceRestart: true });

  // 设置offer为本地描述
  await localRtcPc.value.setLocalDescription(offer);

  // 发送offer给被呼叫者
  const params = {
    userId,
    targetUserId,
    offer,
  };
  linkSocket.value.emit("offer", params);
};
  • A呼叫B后双方同意建立通信,A首先初始化PC,即代码中的localRtcPc
  • 然后 A 初始化本地mediaStream,并添加到 pc 对象中,同时渲染在本地预览 DOM 元素。
  • 初始化回调信息,比如 ontrack(监听B端媒体),onicecandidate(双方 ICE 候选信息)事件等
  • // 创建消息通道
    const onPcEvent = (pc, userId, targetUserId) => {
      // 创建消息通道,建立webRTC通信之后,就可以直接 p2p 的直接发送消息, 无需中转服务器
      channel.value = pc.createDataChannel("chat");
    
      // 监听远程媒体轨道即远端音视频信息
      pc.ontrack = (e) => {
        setRemoteDomVideoStream("remoteVideo", e.track);
      };
    
      // 需要协商新的连接时会被触发  例如在添加新的数据流或者移除数据流时
      pc.onnegotiationneeded = (e) => {
        console.log("需要协商新的连接", e);
      };
    
      // 用于处理当数据通道被添加到连接时触发
      pc.ondatachannel = (e) => {
        e.channel.onopen = () => {
          console.log("通道打开");
        };
        e.channel.onclose = () => {
          console.log("通道关闭");
        };
        e.channel.onmessage = (data) => {
          console.log("收到消息", data);
          remoteMsg.value = data.data; // 接收到远端消息
        };
      };
    
      // 当 ICE 框架收集到新的 ICE 候选时触发,用于处理 ICE 候选的事件
      pc.onicecandidate = (e) => {
        if (e.candidate) {
          const params = {
            userId,
            targetUserId,
            candidate: e.candidate,
          };
          linkSocket.value.emit("candidate", params);
        } else {
          console.log("在此次协商中,没有收集到新的 ICE 候选");
        }
      };
    };
    
    1. 创建offer信令设置为本地描述后发送给 B 。
    2. 等 B 创建应答信令之后,信令服务器会将其转发到 A 这边。
    3. A 接受 B 的 answer信令后,将其设置为 remoteDesc
    // 处理远端answer
    const onRemoteAnswer = async (fromUserId, answer) => {
      await localRtcPc.value.setRemoteDescription(answer);
    };
    

    粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~

    被呼叫方B

    被呼叫端的过程和呼叫端类似,大体代码如下:

    // 初始化被呼叫者的信息
    const initCalleeInfo = async (targetUserId, userId) => {
      // 初始化 PC
      localRtcPc.value = new PeerConnection();
    
      // 初始化本地媒体信息
      const localStream = await getLocalUserMedia({
        video: true,
        audio: true,
      });
    
      // 添加轨道
      for (const track of localStream.getTracks()) {
        localRtcPc.value.addTrack(track);
      }
    
      // dom渲染
      await setDomVideoStream("video", localStream);
    
      // 触发监听
      onPcEvent(localRtcPc.value, targetUserId, userId);
    };
    

    B 接听后同时初始化 pc。

  • B 创建本地mediaStream,并添加到 pc 对象中,同时渲染在本地预览 Dom 元素。
  • 同 A一样初始化回调监听。
  • 当然此时 A 发送的offer信令通过信令服务器转发到 B 这边,B 将其设置为remoteDesc后,同时创建answer信令。
  • // 处理远端offer
    const onRemoteOffer = async (fromUserId, offer) => {
      localRtcPc.value.setRemoteDescription(offer); // 保存远端发送给自己的信令
    
      const answer = await localRtcPc.value.createAnswer(); // 创建应答
    
      await localRtcPc.value.setLocalDescription(answer); // 保存应答
      const params = {
        userId: props.userId,
        targetUserId: fromUserId,
        answer,
      };
      linkSocket.value.emit("answer", params);
    };
    

    至此,所有的会话建立完成,在双方监听的 pc 核心方法ontrack中,就能拿到双方的音频和视频信息了,在文末有仓库地址,大家可自取。

    注意:在真正的复杂网络环境中,需要考虑的还有很多,如果之前了解过WebRTC相关的知识,一定对stun 和 turn这几个词不陌生,但是这里暂时不考虑这个,从最简单的网络环境中开始,完成我们的目标。

    通话过程中媒体流的变更

    完成以上视频通话,再考虑一下,如何实现类似微信视频中,视频和音频之间随意切换,或摄像头前置后置切换呢?

    这里就需要再学习一个知识点:RTCRtpSender对象。这个对象的接口支持变更你发送到对方的媒体,通过这个对象接口,你可以编辑更改流属性,从而达到控制远端媒体流的目的。

    通过实例化后的PeerConnection对象调用getSenders方法,可获取每个媒体轨道对应RTCRtpSender对象。这里再解释下这个媒体轨道,我们在获取到媒体信息的时候,一般包含两部分,一部分音频信息(audiotrack),一部分视频信息(videotrack),因此这里的媒体轨道指的就是媒体信息。

    • 音频视频模式切换。
    //获取发送到远端的具体媒体信息的发送方信息
    const senders = this.localRtcPc.getSenders(); 
    console.log(senders)
    const send = senders.find((s) => s.track.kind === 'video') //找到视频发送方信息
    send.track.enabled = !send.track.enabled //控制视频显示与否 即仅音频模式
    
    • 摄像头切换。
    //这里web端因此只获取屏幕分享流 APP端则获取前置后置摄像头流即可
    let stream = await this.getShareMedia()
    const [videoTrack] = stream.getVideoTracks(); 
    const send = senders.find((s) => s.track.kind === 'video')//找到视频类型发送方信息
    send.replaceTrack(videoTrack) //替换视频媒体信息
    

    类IM通讯实现

    前面初始化回调流程中有个监听方法 onPcEvent(),内部你会发现有个函数createDataChannel,看名字就是创建了一个通道。是的,这就是的WebRTC中的datachannel可以实现无服务端 P2P 文本等富文本信息双向传输,只要完成WebRTC会话,即使视频通话过程中你的云服务器宕机了也没关系,P2P 的即时通讯还是可以正常进行的。

    回看一下上面那两张图片,里面有相互发送消息。这就是类IM即时通讯。

    官方描述

    RTCPeerConnection 的 createDataChannel() 方法可以创建一个可以发送任意数据的数据通道, 常用于后台传输内容,例如:图像、文件传输、聊天文字等其他数据,当然除了后台,最常用的就是 P2P 中客户端的双向通信了。

    基础语法和使用

    下面的创建 datachannel 的前提是双方已经完成WebRTC的基础信令交换,pc变量为初始化后的RTCPeerConnection。

    let dataChannel = RTCPeerConnection.createDataChannel(label[, options]);

    创建一个datachannel,发送并监听消息。

    const channel =  pc.createDataChannel("channel", {
           protocol: "json",
           ordered: true,
    });
    
    -----------------监听消息------------------------------
    pc.ondatachannel = function(ev) {
          console.log('Data channel is created!');
          ev.channel.onopen = function() {
            console.log('Data channel ------------open----------------');
          };
          ev.channel.onmessage = function(data) {
            console.log('Data channel ------------msg----------------',data);
          };
          ev.channel.onclose = function() {
            console.log('Data channel ------------close----------------');
          };
        };
        
     -------------发送消息--------------------------------------   
     channel.send(this.rtcmessage)
    

    通过这种方式发送消息,你在浏览器的 NetWork 是看不到的哦,因此按照常规抓包逻辑直接抓HTTP或者WS协议包的话,也是抓不到的。想要深入了解原理,可以去看看SCTP协议

    项目使用指北

    1. 启动项目后,点击一对一视频
    2. 进去后,在url上带上参数,如下:
    // 用户一
    https://xx/#/demo3?userId=12345&roomId=xxx&nickName=Jack
    
    // 用户二
    https://xx/#/demo3?userId=1234&roomId=xxx&nickName=Rose
    
    1. 点击通话/切换,以及发送消息

    相关推荐

    谷歌正在为Play商店进行Material Design改造

    谷歌最近一直忙于在其应用程序中完成MaterialDesign风格的改造,而Play商店似乎是接下来的一个。9to5Google网站报道,有用户在Play商店的最新版本中发现了新界面,暗示该应用和网...

    企业网站免费搭建,定制化建站CMS系统

    科腾软件企业网站CMS管理系统已完成开发工作,首次开源(全部源码)发布。开发工具:VisualStudioEnterprise2022数据库:SQLite(零配置,跨平台,嵌入式)开发...

    您需要的 11 个免费 Chrome 扩展程序

    来源:SEO_SEM营销顾问大师Chrome扩展程序是SEO的无名英雄,他们在幕后默默工作,使您的策略脱颖而出并提高您的努力效率。从竞争对手研究到审核您的网站,速度比您说“元描述”还快,这些小工具发...

    户外便携设备抗干扰困境如何破局?CMS-160925-078S-67给出答案

      在户外复杂的电磁环境中,便携式设备中的扬声器需具备出色抗干扰能力,CUID的CMS-160925-078S-67在这方面表现突出。  从其结构设计来看,矩形框架虽主要为适配紧凑空...

    一个基于NetCore开发的前后端分离CMS系统

    今天给大家推荐一个开源的前后端分离架构的CMS建站系统。项目简介这是一个基于.Net3构建的简单、跨平台、模块化建站系统。系统业务简单、代码清晰、层级分明、全新架构便于二次扩展开发。支持多种数据库,...

    本地Docker部署ZFile网盘打造个人云存储

    前言本文主要介绍如何在LinuxUbuntu系统使用Docker本地部署ZFile文件管理系统,并结合cpolar内网穿透工具实现远程访问本地服务器上的ZFile传输与备份文件,轻松搭建个人网盘,无...

    pcfcms企业建站系统 免费+开源的企业内容管理系统

    项目介绍pcfcms是基于TP6.0框架为核心开发的免费+开源的企业内容管理系统,专注企业建站用户需求提供海量各行业模板,降低中小企业网站建设、网络营销成本,致力于打造用户舒适的建站体验。演示站...

    【推荐】一个高颜值且功能强大的 Vue3 后台管理系统框架

    如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!项目介绍SnowAdmin是一款基于Vue3、TypeScript、Vite5、Pinia、Arco-Desi...

    java开源cms管理系统框架PublicCMS后台管理系统

    一款使用Java语言开发的CMS,提供文章发布,图片展示,文件下载,用户权限、站点模块,内容管理、分类等功能。可免费用于商业用途maven工程数据库脚本在工程中database文件夹下代码结构:效果...

    一定要大量读书:当我问Deepseek,它给出的高效阅读方法厉害了!

    一年一度的世界读书日,总该写点什么。于是,我去问Deepseek给我推荐人生破局必读的10本书,结果它给了我回复,竟然10本推荐的书籍里,我都曾经浏览过,同时还给出破局关键。而说浏览过,不是读过,是因...

    《搜神札记》:不应磨灭的惊奇(小说《搜神记》)

    □黄勃志怪传说的书写一直是文人墨客的后花园,晚近尤盛,从张岱到袁枚到纪昀,收集那些或阴森或吊诡的行状故事,遂成一类,到民国年间,周作人挟此遗传,捋袖子拿希腊神话动刀,乃兄鲁迅不甘其后,《故事新编》虎...

    《如何构建金字塔》之第三章总结(构建金字塔结构的方法有)

    “没有什么比一套好理论更有用了。”——库尔特.勒温这篇读后感依然引用了这句库尔特.勒温名言,这句话也是我读芭芭拉.明托这本书的初衷。今天就“如何构建金字塔”,我来谈谈我的读后心得。我热爱写作,但是写...

    《助人技术》第一章助人引论内容框架

    第一章内容基本呈现如何成为助人者(心理咨询师)以及一些相关基础知识,对于进入这个行业有兴趣以及希望通过心理咨询寻求帮助但存有疑虑的当事人,都值得一读。心理咨询的三个阶段(不是说严格的三个阶段,而是广义...

    AI助手重构读后感写作流程:从提纲到完整性思考的转换

    大家好!你有没有遇到过读完一本书,想要写读后感,却不知道从何下手的情况呢?今天我们要来探讨一下如何利用稿见AI助手来重构读后感写作流程,从提纲到完整性思考的转换。让我们一起来看看这个全新而又实用的方法...

    图解用思维导图做读书笔记技巧(图解用思维导图做读书笔记技巧视频)

    做阅读笔记非常有利于读后进行有效的深入思考,而思维导图这一强大的工具其最大的特点就是架构清晰,在阅读过程中对文章的分析、总结、分类起着很大的辅助作用。思维导图读书笔记步骤:1、阅读大纲。首先要快速浏览...

    取消回复欢迎 发表评论: