基于声网 Web SDK 实现一对一视频通话 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cookiezmq
V2EX    分享发现

基于声网 Web SDK 实现一对一视频通话

  •  
  •   cookiezmq 2022-10-09 18:41:27 +08:00 1641 次点击
    这是一个创建于 1097 天前的主题,其中的信息可能已经有所发展或是发生改变。

    视频互动直播是当前比较热门的玩法,我们经常见到有 PK 连麦、直播答题、一起 KTV 、电商直播、互动大班课、视频相亲等。本文将演示如何通过声网 视频 SDK 在 Web 端实现一个视频直播应用。话不多说,我们开始动手实操。

    前提准备

    在声网开发者控制台 Console https://console.agora.io 注册声网开发者账号后,需要获取项目 AppID 。另外,开发者每个月可获得 10000 分钟的免费使用额度,可实现各类实时音视频场景。

    先来体验下 Demo

    我们在 GitHub 上提供一个开源的基础视频通话示例项目,在开始开发之前你可以通过该示例项目体验音视频通话效果。

    动手实践

    从 Web 前端页面引入声网 SDK ,发起视频通话。

    开发环境

    声网 SDK 的兼容性良好,对硬件设备和软件系统的要求不高,开发环境和测试环境满足以下条件即可,以下是本文的开发环境和测试环境:

    • 浏览器:Chrome 、Firefox 、Safari 及 Edge
    • 开发环境
      • MacBook Pro (13-inch, M1, 2020)
      • Visual Studio Code (1.67.1)
      • AgoraWebSDK (4.12.2)
    • 测试环境
      • Chrome (101.0.4951.64)

    手动集成设置

    文件组织结构

    实现视频通话之前,参考如下步骤设置你的项目: 如需创建新项目,可以在 Visual Studio Code 里 File > New Window ,创建 Web 项目。完整的目录结构如下,根据个人经验会有所变化。

    . ├── index.css # 用于设计 Web 应用的用户界面样式 ├── index.html # 用于设计 Web 应用的用户界面 ├── index.js # 通过 AgoraRTCClient 实现具体应用逻辑的代码。 └── vendor # 第三方前端插件,辅助页面布局和交互,本教程中是下载到本地使用,你也可以使用 CDN 的方式 ├── bootstrap.bundle.min.js ├── bootstrap.min.css └── jquery-3.4.1.min.js 

    前端页面集成声网 SDK

    声网可以下载到本地使用,也可以直接使用声网的 CDN 引入, 本文推荐使用 CDN 方式集成 Agora SDK 。

    在 index.html 中添加以下代码

     <!DOCTYPE html> ... <link rel="stylesheet" href="./vendor/bootstrap.min.css"> <link rel="stylesheet" href="./index.css"> ... <script src="./vendor/jquery-3.4.1.min.js"></script> <script src="./vendor/bootstrap.bundle.min.js"></script> <script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script> <script src="./index.js"></script> ... 

    最终完整代码为

    可以直接复制运行。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" cOntent="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" cOntent="ie=edge"> <title>Basic Video Call -- Agora</title> <link rel="stylesheet" href="./vendor/bootstrap.min.css"> <link rel="stylesheet" href="./index.css"> </head> <body> <div class="container-fluid banner"> <p class="banner-text">Basic Video Call</p> <a style="color: rgb(255, 255, 255);fill: rgb(255, 255, 255);fill-rule: evenodd; position: absolute; right: 10px; top: 4px;" class="Header-link " href="https://github.com/AgoraIO-Community/AgoraWebSDK-NG/tree/master/Demo"> <svg class="octicon octicon-mark-github v-align-middle" height="32" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg> </a> </div> <div id="success-alert" class="alert alert-success alert-dismissible fade show" role="alert"> <strong>Congratulations!</strong><span> You can invite others join this channel by click </span><a href="" target="_blank">here</a> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div id="success-alert-with-token" class="alert alert-success alert-dismissible fade show" role="alert"> <strong>Congratulations!</strong><span> Joined room successfully. </span> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div id="success-alert-with-token" class="alert alert-success alert-dismissible fade show" role="alert"> <strong>Congratulations!</strong><span> Joined room successfully. </span> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div class="container"> <form id="join-form"> <div class="row join-info-group"> <div class="col-sm"> <p class="join-info-text">AppID</p> <input id="appid" type="text" placeholder="enter appid" required> <p class="tips">If you don`t know what is your appid, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#a-nameappidaapp-id">this</a></p> </div> <div class="col-sm"> <p class="join-info-text">Token(optional)</p> <input id="token" type="text" placeholder="enter token"> <p class="tips">If you don`t know what is your token, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#a-namekeyadynamic-key">this</a></p> </div> <div class="col-sm"> <p class="join-info-text">Channel</p> <input id="channel" type="text" placeholder="enter channel name" required> <p class="tips">If you don`t know what is your channel, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#channel">this</a></p> </div> </div> <div class="button-group"> <button id="join" type="submit" class="btn btn-primary btn-sm">Join</button> <button id="leave" type="button" class="btn btn-primary btn-sm" disabled>Leave</button> </div> </form> <div class="row video-group"> <div class="col"> <p id="local-player-name" class="player-name"></p> <div id="local-player" class="player"></div> </div> <div class="w-100"></div> <div class="col"> <div id="remote-playerlist"></div> </div> </div> </div> <script src="./vendor/jquery-3.4.1.min.js"></script> <script src="./vendor/bootstrap.bundle.min.js"></script> <script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script> <script src="./index.js"></script> </body> </html> 

    视频通话逻辑

    注:以下代码都将在 index.js 中添加

    1 )初始化 Client
    var client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }); 
    2 )加入 RTC 频道并创建本地音频轨道
    // Join a channel and create local tracks. Best practice is to use Promise.all and run them concurrently. [ options.uid, localTracks.audioTrack, localTracks.videoTrack ] = await Promise.all([ // Join the channel. client.join(options.appid, options.channel, options.token || null, options.uid || null), // Create tracks to the local microphone and camera. AgoraRTC.createMicrophoneAudioTrack(), AgoraRTC.createCameraVideoTrack() ]); 
    3 )播放本地视频
    // Play the local video track to the local browser and update the UI with the user ID. localTracks.videoTrack.play("local-player"); 
    4 )发布本地音视频到频道中
    // Publish the local video and audio tracks to the channel. await client.publish(Object.values(localTracks)); 
    5 )监听远端用户音视频
    // Add an event listener to play remote tracks when remote user publishes. client.on("user-published", handleUserPublished); client.on("user-unpublished", handleUserUnpublished); function handleUserPublished(user, mediaType) { const id = user.uid; remoteUsers[id] = user; subscribe(user, mediaType); } function handleUserUnpublished(user, mediaType) { if (mediaType === 'video') { const id = user.uid; delete remoteUsers[id]; $(`#player-wrapper-${id}`).remove(); } } async function subscribe(user, mediaType) { const uid = user.uid; // subscribe to a remote user await client.subscribe(user, mediaType); console.log("subscribe success"); if (mediaType === 'video') { const player = $(` <div id="player-wrapper-${uid}"> <p class="player-name">remoteUser(${uid})</p> <div id="player-${uid}" class="player"></div> </div> `); $("#remote-playerlist").append(player); user.videoTrack.play(`player-${uid}`); } if (mediaType === 'audio') { user.audioTrack.play(); } } 
    6 )离开频道
    async function leave() { for (trackName in localTracks) { var track = localTracks[trackName]; if(track) { track.stop(); track.close(); localTracks[trackName] = undefined; } } // Remove remote users and player views. remoteUsers = {}; $("#remote-playerlist").html(""); // leave the channel await client.leave(); } 

    最终完整的代码

    // create Agora client var client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }); var localTracks = { videoTrack: null, audioTrack: null }; var remoteUsers = {}; // Agora client options var optiOns= { appid: null, channel: null, uid: null, token: null }; // the demo can auto join channel with params in url $(() => { var urlParams = new URL(location.href).searchParams; options.appid = urlParams.get("appid"); options.channel = urlParams.get("channel"); options.token = urlParams.get("token"); if (options.appid && options.channel) { $("#appid").val(options.appid); $("#token").val(options.token); $("#channel").val(options.channel); $("#join-form").submit(); } }) $("#join-form").submit(async function (e) { e.preventDefault(); $("#join").attr("disabled", true); try { options.appid = $("#appid").val(); options.token = $("#token").val(); options.channel = $("#channel").val(); await join(); if(options.token) { $("#success-alert-with-token").css("display", "block"); } else { $("#success-alert a").attr("href", `index.html?appid=${options.appid}&channel=${options.channel}&token=${options.token}`); $("#success-alert").css("display", "block"); } } catch (error) { console.error(error); } finally { $("#leave").attr("disabled", false); } }) $("#leave").click(function (e) { leave(); }) async function join() { // add event listener to play remote tracks when remote user publishs. client.on("user-published", handleUserPublished); client.on("user-unpublished", handleUserUnpublished); // join a channel and create local tracks, we can use Promise.all to run them concurrently [ options.uid, localTracks.audioTrack, localTracks.videoTrack ] = await Promise.all([ // join the channel client.join(options.appid, options.channel, options.token || null), // create local tracks, using microphone and camera AgoraRTC.createMicrophoneAudioTrack(), AgoraRTC.createCameraVideoTrack() ]); // play local video track localTracks.videoTrack.play("local-player"); $("#local-player-name").text(`localVideo(${options.uid})`); // publish local tracks to channel await client.publish(Object.values(localTracks)); console.log("publish success"); } async function leave() { for (trackName in localTracks) { var track = localTracks[trackName]; if(track) { track.stop(); track.close(); localTracks[trackName] = undefined; } } // remove remote users and player views remoteUsers = {}; $("#remote-playerlist").html(""); // leave the channel await client.leave(); $("#local-player-name").text(""); $("#join").attr("disabled", false); $("#leave").attr("disabled", true); console.log("client leaves channel success"); } async function subscribe(user, mediaType) { const uid = user.uid; // subscribe to a remote user await client.subscribe(user, mediaType); console.log("subscribe success"); if (mediaType === 'video') { const player = $(` <div id="player-wrapper-${uid}"> <p class="player-name">remoteUser(${uid})</p> <div id="player-${uid}" class="player"></div> </div> `); $("#remote-playerlist").append(player); user.videoTrack.play(`player-${uid}`); } if (mediaType === 'audio') { user.audioTrack.play(); } } function handleUserPublished(user, mediaType) { const id = user.uid; remoteUsers[id] = user; subscribe(user, mediaType); } function handleUserUnpublished(user) { const id = user.uid; delete remoteUsers[id]; $(`#player-wrapper-${id}`).remove(); } 

    运行效果

    在浏览器开两个 tab 运行网页,使用两个用户加入同一个频道,如果能看见两个自己,说明你成功了。 在这里插入图片描述

    完整代码工程下载

    https://download.agora.io/sdk/release/Agora_Web_SDK_v4_14_0_FULL.zip


    以上就是基于声网 Web SDK 实现一对一视频通话的全部内容。有问题欢迎到「 RTE 开发者社区」https://www.agora.io/cn/community/ 交流吐槽。

    2 条回复    2022-10-17 17:13:56 +08:00
    fuxinya
        1
    fuxinya  
       2022-10-09 23:17:37 +08:00 via Android
    请移动至推广节点
    cookiezmq
        2
    cookiezmq  
    OP
       2022-10-17 17:13:56 +08:00
    收到。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5534 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 09:00 PVG 17:00 LAX 02:00 JFK 05:00
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86