fqh123

 首先我们要会引入TRTC Web SDK

 下载地址:https://cloud.tencent.com/document/product/647/16863#script-.E9.9B.86.E6.88.90

腾讯流播放器文档地址:https://cloud.tencent.com/document/product/881/20207

腾讯点播播放器与超级播放器:使用文档:https://cloud.tencent.com/document/product/266/14424

腾讯点播播放器:开发文档:https://cloud.tencent.com/document/product/266/14603

web端实时通讯IM:https://cloud.tencent.com/document/product/269/37411

 

 

 

 

 

 

 

 

 目前还不知道cdn地址

demo中的index.html中说的很明确,哪些是第三方库,哪些是脚本

 先看一下index.html的代码:

<!doctype html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!-- Material Design for Bootstrap fonts and icons -->
  <!-- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons"> -->

  <!-- Material Design for Bootstrap CSS -->
  <link rel="stylesheet" href="./css/bootstrap-material-design.min.css">
  <link rel="stylesheet" href="./css/common.css">
  <link rel="stylesheet" href="./css/toastify.min.css">
  
  <title>TRTC Web SDK Samples - 基础音视频通话</title>
</head>

<body>
  <nav class="navbar navbar-light fixed-top rtc-primary-bg">
    <h5>基础音视频通话</h5>
  </nav>
  <form id="form">
    <div class="custom-container container">
      <div class="row">
        <div class="custom-row-container">
          <div class="row">
            <div class="col-ms">
              <div class="card custom-card">
                <div class="form-group">
                  <label for="userId" class="bmd-label-floating">用户ID:</label>
                  <input type="text" class="form-control" name="userId" id="userId">
                </div>
                <div class="form-group bmd-form-group">
                  <label for="roomId" class="bmd-label-floating">房间号:</label>
                  <input type="text" class="form-control" name="roomId" id="roomId">
                </div>
                <div class="form-group bmd-form-group">
                  <button id="join" type="button" class="btn btn-raised btn-primary rtc-primary-bg">加入房间</button>
                  <button id="leave" type="button" class="btn btn-raised btn-primary rtc-primary-bg">离开房间</button>
                  <button id="publish" type="button" class="btn btn-raised btn-primary rtc-primary-bg">开始推流</button>
                  <button id="unpublish" type="button" class="btn btn-raised btn-primary rtc-primary-bg">停止推流</button>
                </div>
              </div>
              <div class="card">
                <button class="btn btn-raised rtc-expand-btn" id="settings" data-toggle="collapse"
                  data-target="#setting-collapse" aria-expanded="false" aria-controls="collapse">
                  设置
                </button>
                <div id="setting-collapse" class="collapse" aria-labelledby="setting-collapse">
                  <div class="card-body">
                    <div class="form-group">
                      <label for="cameraId" class="bmd-label-floating">摄像头</label>
                      <select class="form-control" id="cameraId" name="cameraId">
                      </select>
                    </div>
                    <div class="form-group">
                      <label for="microphoneId" class="bmd-label-floating">麦克风</label>
                      <select class="form-control" id="microphoneId" name="microphoneId">
                      </select>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </form>
  <div class="video-grid" id="video_grid">
    <div id="local_stream" class="video-placeholder">
      <div id="local_video_info" class="video-info"></div>
    </div>
  </div>

  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <!-- Demo 相关第三方库-->
  <script src="./js/jquery-3.2.1.min.js"></script>
  <script src="./js/popper.js"></script>
  <script src="./js/toastify.js"></script>
  <script src="./js/bootstrap-material-design.min.js"></script>
  <script>$(document).ready(function () { $(\'body\').bootstrapMaterialDesign(); });</script>
  <!-- 引入 TRTC WEB SDK 脚本 -->
  <script src="./js/trtc.js"></script>
  <!-- Demo 相关脚本 -->
  <script src="./js/lib-generate-test-usersig.min.js"></script>
  <script src="./js/debug/GenerateTestUserSig.js"></script>
  <script src="./js/utils.js"></script>
  <script src="./js/rtc-client.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>
View Code

 里面有:输入用户ID、房间号的输入框;还有进入房间、离开房间、开始推流、停止推流按钮

下面来看看逻辑代码index.js

/* eslint-disable require-jsdoc */

// initialize userId/roomId
$(\'#userId\').val(\'user_\' + parseInt(Math.random() * 100000000));
$(\'#roomId\').val(\'889988\');

let rtc = null;

$(\'#join\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'join\');
  if (rtc) return;
  const userId = $(\'#userId\').val();
  const roomId = $(\'#roomId\').val();
  const config = genTestUserSig(userId);
  rtc = new RtcClient({
    userId,
    roomId,
    sdkAppId: config.sdkAppId,
    userSig: config.userSig
  });
  rtc.join();
});

$(\'#publish\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'publish\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.publish();
});

$(\'#unpublish\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'unpublish\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.unpublish();
});

$(\'#leave\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'leave\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.leave();
  rtc = null;
});

$(\'#settings\').on(\'click\', function(e) {
  e.preventDefault();
  $(\'#settings\').toggleClass(\'btn-raised\');
  $(\'#setting-collapse\').collapse();
});
View Code

可以看出。先让给用户ID和房间号设置了值,

点击进入房间按钮的代码:

$(\'#join\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'join\');
  if (rtc) return;
  const userId = $(\'#userId\').val();
  const roomId = $(\'#roomId\').val();
  const config = genTestUserSig(userId);//根据userId生成一个对象含:sdkAppId、userSig,具体代码见GenerateTestUserSig.js
  rtc = new RtcClient({//创建一个rtc实例  传入userid、房间号、sdkAppId、userSig
    userId,
    roomId,
    sdkAppId: config.sdkAppId,
    userSig: config.userSig,//签名
  });
  rtc.join();//调用join方法
});
GenerateTestUserSig.js:根据SDKAPPIDSECRETKEY(秘钥), EXPIRETIME(签名过期时间 默认设置7天不要太短)生成:userSig(签名)
/* eslint-disable require-jsdoc */
/*
 * Module:   GenerateTestUserSig
 *
 * Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
 *           其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
 *
 * Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
 *
 *            本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
 *            这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
 *            一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
 *
 *            正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
 *            由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
 *
 * Reference:https://cloud.tencent.com/document/product/647/17275#Server
 */
function genTestUserSig(userID) {
  /**
   * 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
   *
   * 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
   * 它是腾讯云用于区分客户的唯一标识。
   */
  const SDKAPPID = 1400324973;

  /**
   * 签名过期时间,建议不要设置的过短
   * <p>
   * 时间单位:秒
   * 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
   */
  const EXPIRETIME = 604800;

  /**
   * 计算签名用的加密密钥,获取步骤如下:
   *
   * step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
   * step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
   * step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
   *
   * 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
   * 文档:https://cloud.tencent.com/document/product/647/17275#Server
   */
  const SECRETKEY = \'aaa3e88fffd77e4f3755a423151ea4008a4539aa64d41c0d16c3e1a844e2f3f5\';

  // a soft reminder to guide developer to configure sdkAppId/secretKey
  if (SDKAPPID === \'\' || SECRETKEY === \'\') {
    alert(
      \'请先配置好您的账号信息: SDKAPPID 及 SECRETKEY \' +
        \'\r\n\r\nPlease configure your SDKAPPID/SECRETKEY in js/debug/GenerateTestUserSig.js\'
    );
  }
  const generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
  const userSig = generator.genTestUserSig(userID);
  return {
    sdkAppId: SDKAPPID,
    userSig: userSig
  };
}
View Code

我们还发现加入房间的代码里 new RtcClient();这样一个代码,字面意思是创建一个rtc实例对象;

下面来看看rtc-client.js中的代码:

/* eslint-disable require-jsdoc */

class RtcClient {//声明一个RtcClient类
  constructor(options) {//实例属性
    this.sdkAppId_ = options.sdkAppId;//sdkAppId
    this.userId_ = options.userId;
    this.userSig_ = options.userSig;
    this.roomId_ = options.roomId;

    this.isJoined_ = false;
    this.isPublished_ = false;
    this.localStream_ = null;
    this.remoteStreams_ = [];

    // check if browser is compatible with TRTC
    TRTC.checkSystemRequirements().then(result => {//trtc.js中的TRTC对象
      if (!result) {
        alert(\'Your browser is not compatible with TRTC! Please download Chrome M72+\');
      }
    });
  }

  async join() {//实例方法
    if (this.isJoined_) {
      console.warn(\'duplicate RtcClient.join() observed\');
      return;
    }

    // create a client for RtcClient
    this.client_ = TRTC.createClient({
      mode: \'videoCall\', // 实时通话模式
      sdkAppId: this.sdkAppId_,
      userId: this.userId_,
      userSig: this.userSig_
    });

    // 处理 client 事件
    this.handleEvents();

    try {
      // join the room
      await this.client_.join({ roomId: this.roomId_ });
      console.log(\'join room success\');
      Toast.notify(\'进房成功!\');
      this.isJoined_ = true;
    } catch (error) {
      console.error(\'failed to join room because: \' + error);
      alert(
        \'进房失败原因:\' +
          error +
          \'\r\n\r\n请确保您的网络连接是正常的,您可以先体验一下我们的Demo以确保网络连接是正常的:\' +
          \'\r\n https://trtc-1252463788.file.myqcloud.com/web/demo/official-demo/index.html \' +
          \'\r\n\r\n另外,请确保您的账号信息是正确的。\' +
          \'\r\n请打开链接:https://cloud.tencent.com/document/product/647/34342 查询详细错误信息!\'
      );
      Toast.error(\'进房错误!\');
      return;
    }

    try {
      // 采集摄像头和麦克风视频流
      await this.createLocalStream({ audio: true, video: true });
      Toast.info(\'摄像头及麦克风采集成功!\');
      console.log(\'createLocalStream with audio/video success\');
    } catch (error) {
      console.error(\'createLocalStream with audio/video failed: \' + error);
      alert(
        \'请确认已连接摄像头和麦克风并授予其访问权限!\r\n\r\n 如果您没有连接摄像头或麦克风,您可以通过调整第60行代码来关闭未连接设备的采集请求!\'
      );
      try {
        // fallback to capture camera only
        await this.createLocalStream({ audio: false, video: true });
        Toast.info(\'采集摄像头成功!\');
      } catch (error) {
        console.error(\'createLocalStream with video failed: \' + error);
        return;
      }
    }

    this.localStream_.on(\'player-state-changed\', event => {
      console.log(`local stream ${event.type} player is ${event.state}`);
      if (event.type === \'video\' && event.state === \'PLAYING\') {
        // dismiss the remote user UI placeholder
      } else if (event.type === \'video\' && event.state === \'STOPPPED\') {
        // show the remote user UI placeholder
      }
    });

    // 在名为 ‘local_stream’ 的 div 容器上播放本地音视频
    this.localStream_.play(\'local_stream\');

    // publish local stream by default after join the room
    await this.publish();
    Toast.notify(\'发布本地流成功!\');
  }

  async leave() {
    if (!this.isJoined_) {
      console.warn(\'leave() - leave without join()d observed\');
      Toast.error(\'请先加入房间!\');
      return;
    }

    if (this.isPublished_) {
      // ensure the local stream has been unpublished before leaving.
      await this.unpublish(true);
    }

    try {
      // leave the room
      await this.client_.leave();
      Toast.notify(\'退房成功!\');
      this.isJoined_ = false;
    } catch (error) {
      console.error(\'failed to leave the room because \' + error);
      location.reload();
    } finally {
      // 停止本地流,关闭本地流内部的音视频播放器
      this.localStream_.stop();
      // 关闭本地流,释放摄像头和麦克风访问权限
      this.localStream_.close();
      this.localStream_ = null;
    }
  }

  async publish() {
    if (!this.isJoined_) {
      Toast.error(\'请先加入房间再点击开始推流!\');
      console.warn(\'publish() - please join() firstly\');
      return;
    }
    if (this.isPublished_) {
      console.warn(\'duplicate RtcClient.publish() observed\');
      Toast.error(\'当前正在推流!\');
      return;
    }
    try {
      // 发布本地流
      await this.client_.publish(this.localStream_);
      Toast.info(\'发布本地流成功!\');
      this.isPublished_ = true;
    } catch (error) {
      console.error(\'failed to publish local stream \' + error);
      Toast.error(\'发布本地流失败!\');
      this.isPublished_ = false;
    }
  }

  async unpublish(isLeaving) {
    if (!this.isJoined_) {
      console.warn(\'unpublish() - please join() firstly\');
      Toast.error(\'请先加入房间再停止推流!\');
      return;
    }
    if (!this.isPublished_) {
      console.warn(\'RtcClient.unpublish() called but not published yet\');
      Toast.error(\'当前尚未发布本地流!\');
      return;
    }

    try {
      // 停止发布本地流
      await this.client_.unpublish(this.localStream_);
      this.isPublished_ = false;
      Toast.info(\'停止发布本地流成功!\');
    } catch (error) {
      console.error(\'failed to unpublish local stream because \' + error);
      Toast.error(\'停止发布本地流失败!\');
      if (!isLeaving) {
        console.warn(\'leaving the room because unpublish failure observed\');
        Toast.error(\'停止发布本地流失败,退出房间!\');
        this.leave();
      }
    }
  }

  async createLocalStream(options) {
    this.localStream_ = TRTC.createStream({
      audio: options.audio, // 采集麦克风
      video: options.video, // 采集摄像头
      userId: this.userId_
      // cameraId: getCameraId(),
      // microphoneId: getMicrophoneId()
    });
    // 设置视频分辨率帧率和码率
    this.localStream_.setVideoProfile(\'480p\');

    await this.localStream_.initialize();
  }

  handleEvents() {
    // 处理 client 错误事件,错误均为不可恢复错误,建议提示用户后刷新页面
    this.client_.on(\'error\', err => {
      console.error(err);
      alert(err);
      Toast.error(\'客户端错误:\' + err);
      // location.reload();
    });

    // 处理用户被踢事件,通常是因为房间内有同名用户引起,这种问题一般是应用层逻辑错误引起的
    // 应用层请尽量使用不同用户ID进房
    this.client_.on(\'client-banned\', err => {
      console.error(\'client has been banned for \' + err);
      Toast.error(\'用户被踢出房间!\');
      // location.reload();
    });

    // 远端用户进房通知 - 仅限主动推流用户
    this.client_.on(\'peer-join\', evt => {
      const userId = evt.userId;
      console.log(\'peer-join \' + userId);
      Toast.notify(\'远端用户进房 - \' + userId);
    });
    // 远端用户退房通知 - 仅限主动推流用户
    this.client_.on(\'peer-leave\', evt => {
      const userId = evt.userId;
      console.log(\'peer-leave \' + userId);
      Toast.notify(\'远端用户退房 - \' + userId);
    });

    // 处理远端流增加事件
    this.client_.on(\'stream-added\', evt => {
      const remoteStream = evt.stream;
      const id = remoteStream.getId();
      const userId = remoteStream.getUserId();
      console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
      Toast.info(\'远端流增加 - \' + userId);
      console.log(\'subscribe to this remote stream\');
      // 远端流默认已订阅所有音视频,此处可指定只订阅音频或者音视频,不能仅订阅视频。
      // 如果不想观看该路远端流,可调用 this.client_.unsubscribe(remoteStream) 取消订阅
      this.client_.subscribe(remoteStream);
    });

    // 远端流订阅成功事件
    this.client_.on(\'stream-subscribed\', evt => {
      const remoteStream = evt.stream;
      const id = remoteStream.getId();
      this.remoteStreams_.push(remoteStream);
      addView(id);
      // 在指定的 div 容器上播放音视频
      remoteStream.play(id);
      console.log(\'stream-subscribed ID: \', id);
      Toast.info(\'远端流订阅成功 - \' + remoteStream.getUserId());
    });

    // 处理远端流被删除事件
    this.client_.on(\'stream-removed\', evt => {
      const remoteStream = evt.stream;
      const id = remoteStream.getId();
      // 关闭远端流内部的音视频播放器
      remoteStream.stop();
      this.remoteStreams_ = this.remoteStreams_.filter(stream => {
        return stream.getId() !== id;
      });
      removeView(id);
      console.log(`stream-removed ID: ${id}  type: ${remoteStream.getType()}`);
      Toast.info(\'远端流删除 - \' + remoteStream.getUserId());
    });

    // 处理远端流更新事件,在音视频通话过程中,远端流音频或视频可能会有更新
    this.client_.on(\'stream-updated\', evt => {
      const remoteStream = evt.stream;
      console.log(
        \'type: \' +
          remoteStream.getType() +
          \' stream-updated hasAudio: \' +
          remoteStream.hasAudio() +
          \' hasVideo: \' +
          remoteStream.hasVideo()
      );
      Toast.info(\'远端流更新!\');
    });

    // 远端流音频或视频mute状态通知
    this.client_.on(\'mute-audio\', evt => {
      console.log(evt.userId + \' mute audio\');
    });
    this.client_.on(\'unmute-audio\', evt => {
      console.log(evt.userId + \' unmute audio\');
    });
    this.client_.on(\'mute-video\', evt => {
      console.log(evt.userId + \' mute video\');
    });
    this.client_.on(\'unmute-video\', evt => {
      console.log(evt.userId + \' unmute video\');
    });

    // 信令通道连接状态通知
    this.client_.on(\'connection-state-changed\', evt => {
      console.log(`RtcClient state changed to ${evt.state} from ${evt.prevState}`);
    });
  }
}
View Code

看到这里,我在本地写了demo,有个报错,先到这里,推荐大家看文档:

文档地址:https://cloud.tencent.com/document/product/647/16863

 

 

 可以现充基础音视频通话来做,慢慢的我那个下看文档

 也可以看一下最佳实践里的小例子,很简短,可以照着实现一个web端的音视频通话;

文档地址:https://cloud.tencent.com/document/product/647/32225

 

 

下面是基于demo中的一些js文件自己实现的一个实时音视频的,进入房间、退出房间、开始推流、停止推流的小例子,就提的代码还没研究;先贴上代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实时音视频demo</title>
  <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  <link rel="stylesheet" href="./css/toastify.min.css">
  <style>
    #local_stream{
      height:500px;
    }
  </style>
</head>
<body>
  <div class="row">
    <div class="col col-xs-3">
      <button class="btn btn-primary" id="join">加入房间</button>
    </div>
    <div class="col col-xs-3">
      <button class="btn btn-primary" id="leave">退出房间</button>
    </div>
    <div class="col col-xs-3">
      <button class="btn btn-primary" id="publish">开始推流</button>
    </div>
    <div class="col col-xs-3">
      <button class="btn btn-primary" id="unpublish">停止推流</button>
    </div>
  </div>
  <div class="row">
    <div class="col col-xs-12">
      <div id="local_stream" class="video-placeholder">
        <div id="local_video_info" class="video-info"></div>
      </div>
    </div>
  </div>



  <!-- jquery -->
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  <!-- 提示框插件 -->
  <script src="./js/toastify.js"></script>
  <!-- bootstrap -->
  <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
  <!-- 引入 TRTC WEB SDK 脚本 -->
  <script src="./js/trtc.js"></script>
  <!-- 下面是demo相关脚本 -->
  <!-- 生成签名 -->
  <script src="./js/lib-generate-test-usersig.min.js"></script>
  <script src="./js/GenerateTestUserSig.js"></script>
  <script src="./js/utils.js"></script>
  <script src="./js/rtc-client.js"></script>
  

<script>
  let rtc = null;
  // 加入房间
$(\'#join\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'join\');
  if (rtc) return;
  let userId = \'user_\' + parseInt(Math.random() * 100000000);
  const config = genTestUserSig(userId);//根据userId生成一个对象含:sdkAppId、userSig,具体代码见GenerateTestUserSig.js
  rtc = new RtcClient({//创建一个rtc实例  传入userid、房间号、sdkAppId、userSig
    userId,
    roomId:\'889988\',
    sdkAppId: config.sdkAppId,
    userSig: config.userSig
  });
  rtc.join();//调用join方法
});
// 退出房间
$(\'#leave\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'leave\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.leave();
  rtc = null;
});
// 开始推流
$(\'#publish\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'publish\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.publish();
});
// 停止推流
$(\'#unpublish\').on(\'click\', function(e) {
  e.preventDefault();
  console.log(\'unpublish\');
  if (!rtc) {
    Toast.error(\'请先加入房间!\');
    return;
  }
  rtc.unpublish();
});

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

效果:

 

 

 

 

 

 

 

分类:

技术点:

相关文章: