集成融云RongCloud视频通话功能PC端

本文详细介绍了如何在Vue项目中集成融云RongCloud的视频通话功能,包括引入JS库、设置API请求头、连接流程和关键代码片段。适合开发者快速上手融云视频通话功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记录下接入融云视频通话的 好日子

集成融云RongCloud视频通话功能PC端,vue2.0接入融云视频通话和语音通话
vue2.0和vue3.0接入的方式几乎一样,改变下写法就好

js引入,在index.html引入对应js文件,注意官方的版本更新

    <script src="https://cdn.ronghub.com/RongIMLib-4.5.latest.js"></script>
    <script src="https://cdn.ronghub.com/RCRTC-5.2.latest.js"></script>
    <script src="https://cdn.ronghub.com/RCCall-5.0.latest.js"></script>

连接融云,需要调用官方API接口,接口请求request封装进行拦截,记得引入import sha1 from ‘./SHA1.js’,

 if (config.url.includes('RongYunsAPI')) {
        let NonceV = Math.floor(Math.random() * 10)
        let TimestampV = +new Date()
        let SignatureV = 你的融云密钥 + NonceV + TimestampV
        config.headers["App-Key"] = '你的融云key'
        config.headers["Nonce"] = NonceV
        config.headers["Timestamp"] = TimestampV
        config.headers["Signature"] = sha1(SignatureV)
    }
// 每个项目的封装请求方式不同,请求的方式随意贴一个作为参考
export const rongyunToken = (params) => {
    return request({
        url: `https://api-cn.ronghub.com/user/getToken.json`,
        method: "post",
        data: params
    });
};

// 在需要连接的界面进行方法请求
//phone  为需要登录的用户ID,电话号码最好
 rongyunToken (`userId=${phone}&name=${name}`).then((res) => {
        console.log(res);
        在这里再进行融云的连接方法
        this.tokens = res.data.token;
        connectIM()
      });

vue页面代码,这里是自动连接,如果不需要自动连接融云就去掉this.GetuserInfo();方法

<template>
  <div id="RongYun">
    <div class="rong-container">
      <div id="rongUser" class="rong-user" >
        <span>用户 ID</span>
        <span id="rongUserId"></span>
      </div>

      <div class="rong-im" id="rongIM" >
        <p>请先进行 IM 连接</p>
        <!--appkey-->
        <div class="im-item">
          <label>App Key</label>
          <!-- value="pwe86ga5pjno6" -->

          <input
            type="text"
            id="appkey"
            :value="AppKey"
            placeholder="请输入 App Key"
          />
          <p>
            必填;可通过
            <a target="_blank" href="https://developer.rongcloud.cn/"
              >[开发者后台] -> [服务管理] -> [基本信息]</a
            >
            获取
          </p>
        </div>
        <!--token-->
        <div class="im-item">
          <label>Token</label>
          <input
            type="text"
            id="token"
            :value="tokens"
            placeholder="请输入 Token"
          />
          <p>
            必填;可通过
            <a target="_blank" href="https://developer.rongcloud.cn/"
              >[开发者后台] -> [服务管理] -> [API 调用] -> [用户服务] -> [获取
              token]</a
            >
            获取
          </p>
        </div>
        <!--navi-->
        <div class="im-item">
          <label>Navi</label>
          <input type="text" id="navi" placeholder="请输入 Navi 地址" />
          <p>非必填;私有云环境必填</p>
        </div>
        <!--mediaServer-->
        <div class="im-item">
          <label>MediaServer</label>
          <input
            type="text"
            id="mediaServer"
            placeholder="请输入 MediaServer 地址"
          />
          <p>非必填;音视频服务地址</p>
        </div>
        <!--连接-->
        <div class="im-item">
          <button id="DengLuBTN" ref="DengLuBTN" @click="connectIM()">
            连接
          </button>
        </div>
      </div>

      <div class="rong-call" id="rongCall" style="display: none">
        <!--呼叫选项-->
        <div id="callParam" class="call-param">
          <!--选择通话类型-->
          <div class="param-item" >
            <label>通话类型</label>
            <select name="" id="callType" @change="callTypeChange()">
              <option value="1">单聊</option>
              <option value="3">群聊</option>
            </select>
            <p>必选</p>
          </div>
          <!--mediaType-->
          <div class="param-item" >
            <label>媒体类型</label>
            <select name="" id="callMediaType" @change="callMediaTypeChange()">
              <option value="1">音频</option>
              <option value="2">音视频</option>
            </select>
            <p>必选</p>
          </div>
          <!--targetId-->
          <div id="paramPrivate" class="param-item">
            <label>对方 ID</label>
            <input
              id="targetId"
              type="text"
              placeholder="对方 userId"
              v-model="targetIdvalue"
            />
            <p>
              必填;对方的 userId,可通过<a
                target="_blank"
                href="https://developer.rongcloud.cn/"
                >[开发者后台] -> [服务管理] -> [API 调用] -> [用户服务] -> [获取
                token]</a
              >获取,且登录成功
            </p>
          </div>
          <!--targetId-->
          <div id="paramGroupId" class="param-item" style="display: none">
            <label>群组 ID</label>
            <input id="groupId" type="text" placeholder="群组 ID" />
            <p>
              必填;可通过
              <a target="_blank" href="https://developer.rongcloud.cn/"
                >[开发者后台] -> [服务管理] -> [API 调用] -> [群组服务] ->
                [加入群组]</a
              >
              加入群组后获取
            </p>
          </div>
          <!--userIds 只有群显示-->
          <div id="paramInvitedIds" class="param-item" style="display: none">
            <label>被邀请者 ID</label>
            <input
              id="userIds"
              type="text"
              placeholder="多个 userId 用英文半角逗号分开"
              :value="UserList"
            />
            <p>
              必填;需加入群后,方可收到邀请。多个 userId 用英文半角逗号分开
            </p>
          </div>
        </div>
        <!--通话视图展示-->
        <div id="videoView" class="video-view">
          
        </div>
        <!--通话操作按钮-->
        <div class="opt-btn" id="btn-div">
          <!-- <button id="callBtn"
                  class="btn-call"
                  @click="call()">呼叫</button> -->
          <div id="callBtn"  >
          <!-- <div id="">通话已结束,点击可再次呼起</div> -->
            <div class="btn-call" @click="call()"></div>
          </div>

          <!-- <button id="acceptBtn"
                  class="btn-accept"
                  @click="accept()">
            接听 -->
          <div id="acceptBtn" class="btn-accept" @click="accept()"></div>

          <div id="hungupBtn" class="btn-hungup" @click="hungup()"></div>
        </div>
      </div>
    </div>
    <div class="toast-wrap">
      <span class="toast-msg"></span>
    </div>
  </div>
</template>

<script>
import { userGetToken } from "@/api/paishuiSys/patrol/patrolMange/wayManage";
import { getUserInfo } from "@/api/system/user";
/**
 * call 主要相关逻辑
 */
const { ConversationType } = RongIMLib;
const { RCCallMediaType, RCCallErrorCode } = RCCall;
// CallSession 实例
let callSession;
// Call 呼叫类型
let callType = ConversationType.PRIVATE;
// Call 媒体类型
let mediaType = RCCallMediaType.AUDIO;

// RTC 实例
let rtcClient;
// CallLib 实例
// let callClient;

let RCDom = {
  get: (id) => {
    return document.getElementById(id);
  },
  show: (id) => {
    var rongIMDom = document.getElementById(id);
    rongIMDom.setAttribute("style", "display:block;");
  },
  showBlock: (id) => {
    var rongIMDom = document.getElementById(id);
    rongIMDom.setAttribute("style", "display:block;");
  },
  hide: (id) => {
    var rongIMDom = document.getElementById(id);
    rongIMDom.setAttribute("style", "display:none;");
  },
};
let RCCallView = {
  connectedIM: () => {
    // RCDom.show("rongUser");
  },
  readyToCall: () => {
    RCDom.hide("rongIM");
    RCDom.show("rongCall");
  },
  outgoing: () => {
    RCDom.hide("callParam");
    RCDom.hide("callBtn");
    RCDom.show("hungupBtn");

  },
  incomming: () => {
    RCDom.hide("callParam");
    RCDom.hide("callBtn");
    RCDom.show("acceptBtn");
    RCDom.show("hungupBtn");

  },
  inTheCall: () => {
    RCDom.hide("acceptBtn");
    RCDom.hide("callParam");
    RCDom.show("hungupBtn");
    
  },
  end: () => {
    RCDom.show("callParam");
    RCDom.show("callBtn");
    RCDom.hide("acceptBtn");
    RCDom.hide("hungupBtn");
  },
};
export default {
  components: {},
  props: {
    Iphone: {  //呼叫号码
      type: String,
      default: () => "",
    },
    getmediaType: {  //类型
      type: Number,
      default: () => 2,
    },
  },
  data() {
    return {
      imClient: null,
      callClient: null,
      targetIdvalue: "123",
      AppKey: "你的融云key",
      UserList: "11,22",
      tokens: "",
      DengLuBTN: null,
    };
  },
  watch: {
    Iphone: {
      handler(val) {
        console.log(val);
        if (val) {
          this.targetIdvalue = val;
        }
      },
      // immediate: true
    },
  },
  computed: {},
  created() {},
  mounted() {
   // this.GetuserInfo();
  },
  methods: {
    // GetuserInfo 获取用户信息
    GetuserInfo() {
      getUserInfo().then((res) => {
        console.log(res);
        this.userinfo = res.data.data;
        console.log(this.userinfo);

        this.numbs = res.data.data.phone;
        this.names = res.data.data.name;

        this.$nextTick(() => {
          this.Dengl();
        });
      });
    },

    // 默认登录,获取当前电脑用户PCUser的通话token
    Dengl() {
      userGetToken(`userId=${this.numbs}&name=${this.names}`).then((res) => {
        console.log(res);
        if (res.data.code == 200) {
          console.log("res.data.token", res.data.token);
          this.tokens = res.data.token;

          this.$nextTick(() => {
            this.connectIM();
          });
        }
      });
    },
    RCToast(msg) {
      setTimeout(function () {
        document
          .getElementsByClassName("toast-wrap")[0]
          .getElementsByClassName("toast-msg")[0].innerHTML = msg;
        var toastTag = document.getElementsByClassName("toast-wrap")[0];
        toastTag.className = toastTag.className.replace("toastAnimate", "");
        setTimeout(function () {
          toastTag.className = toastTag.className + " toastAnimate";
        }, 10);
      }, 10);
    },

    // im
    /**
     * 初始化、链接 IM 相关逻辑
     */

    // IM 实例

    connectIM() {
      const appkey = RCDom.get("appkey").value;
      const token = RCDom.get("token").value;
      const navi = RCDom.get("navi").value;

      if (!appkey) {
        this.RCToast("请输入 App Key");
        return;
      }
      if (!token) {
        this.RCToast("请输入 Token");
        return;
      }

      // IM 客户端初始化
      this.imClient = RongIMLib.init({
        appkey,
        navigators: navi ? [navi] : undefined,
        logLevel: 1,
      });
      // 初始化 RTC CallLib
      this.initRTC();
      this.initCall();

      this.imClient.watch({
        // 监听 IM 连接状态变化
        status(evt) {
          console.log("connection status change:", evt.status);
        },
      });

      this.RCToast("正在链接 IM ... ☕️");
      this.imClient
        .connect({ token })
        .then((user) => {
          RCCallView.connectedIM();
          RCCallView.readyToCall();
          RCDom.get("rongUserId").innerText = user.id;
          this.RCToast(`用户 ${user.id} IM 链接成功 ✌🏻`);

          this.callTypeChange();
          this.callMediaTypeChange();
          setTimeout(() => {
            this.call();
          }, 0);
        })
        .catch((error) => {
          console.log(error);
          this.RCToast("IM 链接失败,请检查网络后再试");
        });
    },
    // call

    /**
     * RTC 初始化
     * 在 IM 初始化后进行初始化 (具体位置:im.js)
     */
    initRTC() {
      const mediaServer = RCDom.get("mediaServer").value;
      rtcClient = this.imClient.install(window.RCRTC.installer, {
        mediaServer: mediaServer || undefined,
        timeout: 30 * 1000,
        logLevel: window.RCEngine.LogLevel.DEBUG,
      });
    },

    /**
     * CallLib 初始化
     * 在 IM 初始化后进行初始化 (具体位置:im.js)
     */
    initCall() {
      this.callClient = this.imClient.install(window.RCCall.installer, {
        rtcClient: rtcClient,
        onSession: (session) => {
          callSession = session;
          mediaType = session.getMediaType();
          this.registerCallSessionEvent(callSession);
          this.RCToast(`收到 ${session.getCallerId()} 的通话邀请`);
          RCCallView.incomming();
        },
        onSessionClose: (session, summary) => {
          this.RCToast("通话已结束");
          RCCallView.end();
          this.removeVideoEl();
        },
      });
      console.log(this.callClient);
    },

    /**
     * 通话类型监听
     */
    callTypeChange() {
      const callTypeDom = RCDom.get("callType");
      callType = Number(callTypeDom.value);
      console.log(callType);
      if (callType === ConversationType.GROUP) {
        RCDom.showBlock("paramGroupId");
        RCDom.showBlock("paramInvitedIds");
        RCDom.hide("paramPrivate");
      } else {
        RCDom.hide("paramGroupId");
        RCDom.hide("paramInvitedIds");
        // RCDom.showBlock("paramPrivate");
      }
    },

    /**
     * 媒体类型监听
     */
    callMediaTypeChange() {
      const mediaTypeDom = RCDom.get("callMediaType");
      mediaType = Number(mediaTypeDom.value);
      console.log(mediaType);
    },

    /**
     * CallSession 事件
     */
    getCallSessionEvent() {
      return {
        onRinging: (sender) => {
          this.RCToast(`收到 ${sender.userId} 振铃`);
        },
        onAccept: (sender) => {
          this.RCToast(`${sender.userId} 已接听`);
        },
        onHungup: (sender) => {
          this.RCToast(`${sender.userId} 已挂断`);
          // 群组中移除相应节点
          const videoViewDom = RCDom.get("videoView");
          const videoDom = RCDom.get(`video-${sender.userId}`);
          videoDom && videoViewDom.removeChild(videoDom);
        },
        onTrackReady: (track) => {
          this.appendVideoEl(track);
          if (!track.isLocalTrack()) {
            this.RCToast("通话已建立");
            RCCallView.inTheCall();
          }
        },
        onMemberModify: (sender) => {
          console.log(sender);
        },
        onMediaModify: (sender) => {
          console.log(sender);
        },
        onAudioMuteChange: (muteUser) => {
          console.log(muteUser);
        },
        onVideoMuteChange: (muteUser) => {
          console.log(muteUser);
        },
      };
    },

    /**
     * callSession 事件注册
     */
    registerCallSessionEvent(session) {
      const events = this.getCallSessionEvent();
      session.registerSessionListener(events);
    },

    /**
     * callSession 呼叫
     */
    call() {
      mediaType = this.getmediaType;
      const events = this.getCallSessionEvent();
      const isPrivateCall = callType === ConversationType.PRIVATE;
      const params = {
        targetId: RCDom.get(`${isPrivateCall ? "targetId" : "groupId"}`).value,
        mediaType: mediaType,
        listener: events,
      };
      if (isPrivateCall) {
        if (!RCDom.get("targetId").value) {
          this.RCToast("请输入对方 ID");
          return;
        }
        this.privateCall(params);
      } else {
        if (!RCDom.get("groupId").value) {
          this.RCToast("请输入群组 ID");
          return;
        }
        if (!RCDom.get("userIds").value) {
          this.RCToast("请输入被邀请者 ID");
          return;
        }
        this.groupCall(params);
      }
    },

    /**
     * 单呼
     */
    privateCall(params) {
      this.callClient.call(params).then(({ code, session }) => {
        if (code === RCCallErrorCode.SUCCESS) {
          this.registerCallSessionEvent(session);
          callSession = session;
          RCCallView.outgoing();
        } else {
          this.RCToast(`呼叫失败,错误原因:${code}`);
        }
      });
    },

    /**
     * 群呼
     */
    groupCall(params) {
      params.userIds = (RCDom.get("userIds").value || []).split(",");
      this.callClient.callInGroup(params).then(({ code, session }) => {
        if (code === RCCallErrorCode.SUCCESS) {
          this.registerCallSessionEvent(session);
          callSession = session;
          RCCallView.outgoing();
        } else {
          const reason =
            code === RCCallErrorCode.NOT_IN_GROUP ? "当前用户未加入群组" : code;
          this.RCToast(`呼叫失败,错误原因:${reason}`);
          this.removeVideoEl();
        }
      });
    },

    /**
     * 接听当前 callSession
     */
    accept() {
      callSession.accept().then(({ code }) => {
        if (code === RCCallErrorCode.SUCCESS) {
          this.RCToast("接听成功");
        } else {
          this.RCToast(`接听失败,错误原因:${code}`);
        }
      });
    },

    /**
     * 挂断当前 callSession
     */
    hungup() {
      if (callSession) {
        callSession.hungup().then(({ code }) => {
          if (code === RCCall.RCCallErrorCode.SUCCESS) {
            this.RCToast("挂断成功");
          } else {
            this.RCToast(`挂断失败,错误原因:${code}`);
          }
        });
      }
    },

    /**
     * video 视图渲染
     */
    appendVideoEl(track) {
      console.log(track);
      const container = RCDom.get("videoView");
      if (track.isAudioTrack()) {
        const uid = track.getUserId();
        const node = document.createElement("div");
        node.setAttribute("id", `video-${uid}`);
        const videoTpl = `<span class="video-user-id">ID: ${uid}</span>
      <span class="video-media-type">${mediaType === 1 ? "音频" : ""}</span>
      <video id="${uid}" 	controls 
			preload="auto"></video>`;
        node.innerHTML = videoTpl;
        node.classList = "video-item";

        const rongCall = document.getElementById("rongCall");
        const btndiv = document.getElementById("btn-div");
        container.appendChild(node);
        track.play();
      } else {
        const videoEl = RCDom.get(track.getUserId());
        track.play(videoEl);
      }
    },

    /**
     * 通话结束后,清除所有 video 标签
     */
    removeVideoEl() {
      RCDom.get("videoView").innerHTML = "";
    },
  },
  beforeDestroy() {
    this.hungup();
  },
};
</script>
<style lang="scss" scoped>
@import "./rongyun.css";
#RongYun {
  width: 100%;
  height: 100%;
}

.rong-call input {
  color: #000 !important;
}
input:-internal-autofill-selected {
  -webkit-text-fill-color: #000 !important;
}
</style>

SHA1.js 加密

//SHA1 加密
function encodeUTF8(s) {
    var i, r = [],
        c, x;
    for (i = 0; i < s.length; i++)
        if ((c = s.charCodeAt(i)) < 0x80) r.push(c);
        else if (c < 0x800) r.push(0xC0 + (c >> 6 & 0x1F), 0x80 + (c & 0x3F));
    else {
        if ((x = c ^ 0xD800) >> 10 == 0) //对四字节UTF-16转换为Unicode
            c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000,
            r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
        else r.push(0xE0 + (c >> 12 & 0xF));
        r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
    };
    return r;
};

// 字符串加密成 hex 字符串
function sha1(s) {
    var data = new Uint8Array(encodeUTF8(s))
    var i, j, t;
    var l = ((data.length + 8) >>> 6 << 4) + 16,
        s = new Uint8Array(l << 2);
    s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
    for (t = new DataView(s.buffer), i = 0; i < l; i++) s[i] = t.getUint32(i << 2);
    s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
    s[l - 1] = data.length << 3;
    var w = [],
        f = [
            function () {
                return m[1] & m[2] | ~m[1] & m[3];
            },
            function () {
                return m[1] ^ m[2] ^ m[3];
            },
            function () {
                return m[1] & m[2] | m[1] & m[3] | m[2] & m[3];
            },
            function () {
                return m[1] ^ m[2] ^ m[3];
            }
        ],
        rol = function (n, c) {
            return n << c | n >>> (32 - c);
        },
        k = [1518500249, 1859775393, -1894007588, -899497514],
        m = [1732584193, -271733879, null, null, -1009589776];
    m[2] = ~m[0], m[3] = ~m[1];
    for (i = 0; i < s.length; i += 16) {
        var o = m.slice(0);
        for (j = 0; j < 80; j++)
            w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1),
            t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0,
            m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
        for (j = 0; j < 5; j++) m[j] = m[j] + o[j] | 0;
    };
    t = new DataView(new Uint32Array(m).buffer);
    for (var i = 0; i < 5; i++) m[i] = t.getUint32(i << 2);

    var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function (e) {
        return (e < 16 ? "0" : "") + e.toString(16);
    }).join("");

    return hex;
};
export default sha1

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值