如何在HTML5使用WebRTC(内含可测试地址可用TRUN服务器)

简介: 如何在HTML5使用WebRTC(内含可测试地址可用TRUN服务器)
// 从地址栏获取参数
const url=location.href;
let name = getParamFromUrl(url, "name");
let role = getParamFromUrl(url, "role");
let remote = getParamFromUrl(url, "remote"); // answer none remote
var localOpened = false;

// 信令服务器
const signaling = new SignalingChannel("wss://www.zhaosonghan.com:9001", name, role, onmessage);
signaling.remote = remote;

// TURN 参数
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{
                                      urls:'turn:43.138.235.180:9002',
                                      username: name,
                                      credential: 'mypwd'
                                    }]};

// 最重要的对象
const pc = new RTCPeerConnection(configuration);

// 没太大用
pc.getStats = (status) => {
  console.log("getStats ");
}

// 没太大用,状态改变会触发
pc.oniceconnectionstatechange = async (ev) => {
  console.log("oniceconnectionstatechange " + pc.iceConnectionState);

  // 如果是 answer 并且不是 local,打开自己本地的视频
  if (ROLE_ANSWER == role && !localOpened) {
    localOpened = true;
    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) => pc.addTrack(track, stream));

    // now, trigger onnegotiationneeded / onicecandidate
    document.getElementById('local').srcObject = stream;   
  }
}

// ICE 失败会有调用
pc.onicecandidateerror = (event) => {
  console.log("onicecandidateerror");
}

// 触发产生 candidate
// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => {
  if (null == candidate)
    return;

  log("onicecandidate=" + JSON.stringify(candidate));
  
  signaling.sendToPeer(signaling.remote, {id:ID_WEBRTC_CANDI, candi:candidate});
}

// getUserMedia 调用后,会触发
// Let the "negotiationneeded" event trigger offer generation.
// offer trigger
pc.onnegotiationneeded = async () => {
  try {
    
    log("onnegotiationneeded");
    await pc.setLocalDescription(await pc.createOffer());

    // Send the offer to the other peer.
    signaling.sendToPeer(signaling.remote, {id:ID_WEBRTC_DESC, desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// Once remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  let remoteView = document.getElementById('remote');
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// 最开始的地方
// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in 'local'
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
      stream.getTracks().forEach((track) => pc.addTrack(track, stream));

      // now, trigger onnegotiationneeded / onicecandidate
      document.getElementById('local').srcObject = stream;

  } catch (err) {
    log("start error=" + err);
  }
}

// 信令服务器交互
function onmessage(data) {
  //console.log("onmsg=" + data);
  
  let jsonRoot = eval('(' + data + ')');
  let cmd = parseInt(jsonRoot.head.cmd);
  //console.log("onmessage cmd=" + cmd);

  switch (cmd) {
    case CMD_REQ_SEND_PEER:
      handleCmdFromPeer(data);
      break;
    case CMD_RSP_SEND_PEER:
      handleSendPeerResp(data);
      break;
    case CMD_RSP_LOGIN:
      handleLogin(data);
      break;
    default:
      break;
  }
}

function handleLogin(data) {
  let jsonRoot = eval('(' + data + ')');

  log("login rst=" + jsonRoot.data.ret);
}

function handleSendPeerResp(data) {
  let jsonRoot = eval('(' + data + ')');
  log("handleSendPeerResp=" + jsonRoot.data.ret);
}

function handleCmdFromPeer(data) {
  let jsonRoot = eval('(' + data + ')');
  signaling.remote = jsonRoot.head.name;
  log("cmd from peer " + signaling.remote);

  let cmdid = parseInt(jsonRoot.data.msg.id);
  switch (cmdid) {
    case ID_WEBRTC_DESC:
      setRemoteDesc(jsonRoot.data.msg.desc);
      break;
    case ID_WEBRTC_CANDI:
      setCandidate(jsonRoot.data.msg.candi);
      break;
    default:
      break;
  }
}

// 添加 candidate
async function setCandidate(candi) {
  log("setCandidate " + JSON.stringify(candi));
  await pc.addIceCandidate(candi);
}

// 设置远端desc
async function setRemoteDesc(json) {
    if ('offer' == json.type) {

      await pc.setRemoteDescription(json);
      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      stream.getTracks().forEach((track) => pc.addTrack(track, stream));
      await pc.setLocalDescription(await pc.createAnswer());
      log("offer setremotedesc local desc=" + pc.localDescription);

      signaling.sendToPeer(signaling.remote, {id:ID_WEBRTC_DESC, desc: pc.localDescription});  
    } else if ('answer' == json.type) {
      await pc.setRemoteDescription(json);
      log("answer setremotedesc local desc=" + pc.localDescription);

    } else {
      log('Unsupported SDP type.');
    }
      
}


function getParamFromUrl(url, param)
{
  let vars = url.split("&");
  for (let i = 0; i < vars.length; i++)
  {
    let pair = vars[i].split("=");
    if (param == pair[0])
      return pair[1];
  }

  return "";
}

以上是 html5 使用 webrtc 的核心代码,其实代码并不复杂,可以运行示例例观察调用流程;但是自己需要实现一个信令服务器。
如果没有信令服务器,可以使用如下链接直接看效果

pc端

offer https://www.zhaosonghan.com/h5/webrtc_pc.html?&name=123456&remote=abcdef&role=1
answer https://www.zhaosonghan.com/h5/webrtc_pc.html?&name=abcdef&role=2
移动端

offer https://www.zhaosonghan.com/h5/webrtc_mobile.html?&name=123456&remote=abcdef&role=1
answer https://www.zhaosonghan.com/h5/webrtc_mobile.html?&name=abcdef&role=2

相关文章
|
5月前
|
JavaScript 数据可视化 Docker
简易制作MCP服务器并测试
本文介绍了如何简易制作并测试MCP服务器,包括环境搭建、代码实现及Docker部署。首先通过uv包创建项目,在main.py中定义MCP服务器及其工具和资源函数。接着详细说明了在Windows上安装uv、配置Docker镜像加速、生成requirements.txt文件以及编写Dockerfile的过程。最后,通过构建和运行Docker容器部署MCP服务器,并使用Node.js工具测试其功能,确保服务器正常工作。此教程适合初学者快速上手MCP服务器的开发与部署。
2391 63
|
10月前
|
运维 Prometheus 监控
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
|
11月前
|
存储 监控 网络协议
服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
【10月更文挑战第11天】服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
552 32
|
11月前
|
缓存 监控 测试技术
服务器压力测试
【10月更文挑战第11天】服务器压力测试
528 31
|
10月前
|
缓存 Ubuntu Linux
Linux环境下测试服务器的DDR5内存性能
通过使用 `memtester`和 `sysbench`等工具,可以有效地测试Linux环境下服务器的DDR5内存性能。这些工具不仅可以评估内存的读写速度,还可以检测内存中的潜在问题,帮助确保系统的稳定性和性能。通过合理配置和使用这些工具,系统管理员可以深入了解服务器内存的性能状况,为系统优化提供数据支持。
646 4
|
11月前
|
SQL 分布式计算 NoSQL
大数据-170 Elasticsearch 云服务器三节点集群搭建 测试运行
大数据-170 Elasticsearch 云服务器三节点集群搭建 测试运行
227 4
|
11月前
|
弹性计算 网络协议 Linux
云服务器评估迁移时间与测试传输速度
云服务器评估迁移时间与测试传输速度
|
25天前
|
存储 缓存 数据挖掘
阿里云目前最便宜云服务器介绍:38元、99元、199元性能,选购攻略参考
轻量应用服务器2核2G峰值200M带宽38元1年;云服务器经济型e实例2核2G3M带宽99元1年;云服务器通用算力型u1实例2核4G5M带宽199元1年。对于还未使用过阿里云服务器的用户来说,大家也不免有些疑虑,这些云服务器性能究竟如何?它们适用于哪些场景?能否满足自己的使用需求呢?接下来,本文将为您全方位介绍这几款云服务器,以供您了解及选择参考。
|
29天前
|
网络安全 云计算
如何设置阿里云轻量应用服务器镜像?
本文介绍了在阿里云轻量应用服务器上创建与配置镜像的详细步骤。镜像是一种特殊的文件系统映射,可用于快速克隆服务器配置。内容涵盖准备条件、登录控制台、创建实例、生成镜像、下载与设置镜像,以及如何使用镜像启动新实例。适合希望提升服务器部署效率的用户参考。
|
1月前
|
存储 弹性计算 安全
阿里云轻量服务器通用型、CPU优化型、多公网IP型、国际型、容量型不同实例区别与选择参考
阿里云轻量应用服务器实例类型分为通用型、CPU优化型、多公网IP型、国际型、容量型,不同规格族的适用场景和特点不同,收费标准也不一样。本文为大家介绍轻量应用服务器通用型、多公网IP型、容量型有何区别?以及选择参考。