前端IM使用以及视频通讯记录分项

8 篇文章 0 订阅
1 篇文章 0 订阅

首先这次接触到IM,是因为项目里需要用到一个群聊视频,通讯录,消息发送等功能,但是Im提供的dom是 react框架写的,并没有vue框架的dom,于是自己根据IM的官方文档实现了在vue框架下的IM通讯。

这是最终效果图,因个人项目而异,弹窗或者不弹窗灵活改动,还有很多功能没写,待此功能重构后再继续更新,因信息数据保密所以打码
在这里插入图片描述
IM官方文档:https://doc.rentsoft.cn/#/integrate/web/integrate_login_web
–》web就看每一个功能下面web端文档说明
视频通讯文档 官方没有写,但是在IM项目的源码里面发现视频通讯功能使用的是LiveKit
LiveKit官方文档 :https://docs.livekit.io/guides/room/connect/
LiveKit GitHub: https://github.com/livekit/client-sdk-js
LiveKit JS Client SDK文档: https://docs.livekit.io/client-sdk-js/index.html
LiveKit其实只需要看 官方文档就OK,当然,除非你想深入了解它
下面贴实现过程代码
首先第一步 登录IM 我将它部分封装在一个JS里面

PS:音频功能需要在HTTPS 和 本地环境下浏览器才允许通过安全策略,否则调取本地摄像头你会报错,目前没找到浏览器关闭使用摄像头,目前找到的资料是挂断电话或者关闭通话弹窗时刷新此页面

import { OpenIMSDK } from 'open-im-sdk'
import axios from "axios";
import store from "@/store";
import { Message } from 'element-ui';

//       音视频邀请相关信息
// {
// 		"inviterUserID": "18666662412",  //邀请者UserID
// 		"inviteeUserIDList": ["18349115126"], //被邀请者UserID列表,如果是单聊只有一个元素
// 		"groupID": "f2e77b9ec33e92298675ad511fdfa6ab", //如果是单聊,为""
// 		"roomID": "room_id_111", //房间ID,必须唯一,可以不设置。
// 		"timeout": 1000, //邀请超时时间(秒)
// 		"mediaType": "video", //video 或者audio
// 		"sessionType": 2 //1为单聊,2为群聊
// 		"platformID":1 // 创建时写什么就是什么 1 2 皆可
// 	}
let user = '';
const openIM = new OpenIMSDK();
const onGetImData = (datas) => {
    user = JSON.parse(datas.serviceTag1)
    let urls = window.location.host;
    let obj = {
        platform: "5",
        userID: JSON.parse(datas.serviceTag1).openImUserId,
    };
    let urltype = 'https://****:端口/portaluserIm/im/user/obtainToken';
    let urlstype =
        document.location.protocol.indexOf("https") > -1 ? true : false;
    axios
        .post(urltype, obj)
        .then((data) => {
            console.log("openIMopenIM", data);
            sessionStorage.setItem("SET_IMTOKEN", JSON.stringify(data.data.data));
            var commonIp = ''
            commonIp = eval("(" + sessionStorage.getItem('commonIp') + ")")
            console.log('commonIp',commonIp);
            const config = {
                userID: data.data.data.userID,
                token: data.data.data.token,
                url: commonIp? commonIp.newImWss : '你的wss地址',
                platformID: 5,
            };
            openIM
                .login(config)
                .then((res) => {
                    console.log("login 结果...", res);
                    sessionStorage.setItem("SET_IMUSER", JSON.stringify(res));

                    openIM.on("OnRecvNewMessage", (dataMessage) => {
                        console.log("会话消息监听测试", JSON.parse(dataMessage.data));
                    });

                    openIM.on("OnReceiveNewInvitation", (dataInvitation) => {
                        let row = dataInvitation;
                        row.objData = JSON.parse(row.data);
                        row.objDataName = row.objData.participant.groupInfo.groupName
                        console.log('urlstype', urlstype);
                        
                        if (row.objData.invitation.inviterUserID != user.openImUserId) {
                            // 被邀请者需要点击接受后 方可进入视频房间
                            sessionStorage.removeItem("ImRow")
                            sessionStorage.setItem("ImRow", JSON.stringify(row))
                            store.commit("SET_IMTYPE", true);
                            console.log('this.$store.state.user.ImType', store.state.ImType);
                        } else {

                        }
                        console.log("被邀请者收到:音视频通话邀请", row,user);
                    });

                    openIM.on("OnInviteeAccepted", (dataAccepted) => {
                        console.log("邀请者收到:被邀请者同意音视频通话", dataAccepted);
                    });
                    openIM.on("OnInviteeRejected", (inviteeRejectedCallback) => {
                        console.log("邀请者收到:被邀请者拒绝音视频通话",inviteeRejectedCallback);
                    });
                    openIM.on("OnInvitationCancelled", (dataCancelled) => {
                        console.log("被邀请者收到:邀请者取消音视频通话", dataCancelled);
                    });
                    openIM.on("OnInvitationTimeout", (dataTimeout) => {
                        console.log("邀请者收到:被邀请者超时未接通", dataTimeout);
                    });
                    openIM.on("OnInviteeAcceptedByOtherDevice", (dataOtherDevice) => {
                        console.log(
                            "被邀请者(其他端)收到:比如被邀请者在手机接听,在pc上会收到此回调",
                            dataOtherDevice
                        );
                    });
                    openIM.on("OnInviteeRejectedByOtherDevice", (dataDevice) => {
                        console.log(
                            "被邀请者(其他端)收到:比如被邀请者在手机拒接,在pc上会收到此回调",
                            dataDevice
                        );
                    });
                })
                .catch((err) => {
                    console.log("login 报错结果...", err);
                });
        });
}
export default {
    onGetImData: onGetImData,
    openIM: openIM
};

上面是封装好的JS文件,既然封装了就得引用对吧,是的,你知道的

import openIMData from "@/components/ImToken";
const openIM = openIMData.openIM;

好了 接下来我们将一步一步来
1,创建群
https://doc.rentsoft.cn/#/integrate/web/integrate_group_web 官方文档有写
2,创建群组成功后 ,如果需要二次确认就获取下当前群组信息你能拿到当前群成员所有ID,如果不用那么就直接发起呼叫

//       音视频邀请相关信息
      // {
      // 		"inviterUserID": "18666662412",  //邀请者UserID
      // 		"inviteeUserIDList": ["18349115126"], //被邀请者UserID列表,如果是单聊只有一个元素
      // 		"groupID": "f2e77b9ec33e92298675ad511fdfa6ab", //如果是单聊,为""
      // 		"roomID": "room_id_111", //房间ID,必须唯一,可以不设置。
      // 		"timeout": 1000, //邀请超时时间(秒)
      // 		"mediaType": "video", //video 或者audio
      // 		"sessionType": 2 //1为单聊,2为群聊
      // 		"platformID":1
      // 	}
      let invitation = {
        inviterUserID: user.userID,
        inviteeUserIDList: this.qunUid.arrData,
        groupID: this.qunData.groupID,
        // groupID: "eaaf2f787bd59b5c8ac2c1039d8388ff", // 方便测试创建好一个群后你需要记录这个群的 ID
        roomID: this.oncode(),// 唯一ID 你可以随机生成15位数字+字母
        timeout: 10000,
        mediaType: "video",
        sessionType: 2,
      };
      openIM
          .signalingInviteInGroup(invitation)
          .then((res) => {
            console.log("res11", res);
            Message({
              showClose: true,
              message: "音频发起成功,如长时间未反应,请刷新页面后重新发起!",
              type: "success",
              duration: 5000,
            });
            this.qunUidView = res;
            this.qunUidView.obj = JSON.parse(res.data);
            let row = {
              objData: {
                invitation: invitation,
              },
            };

            sessionStorage.setItem("ImRow", JSON.stringify(row));
            store.commit("SET_CreateVideoUserType", true);
          })
          .catch((err) => {
            console.log("err", err);
          });

好的,上面这一块代码 你已经成功发起了 呼叫,现在回到封装好的JS文件,不管是创建群还是消息接受 OnRecvNewMessage 函数都会接收到反馈数据,比如别人创建了一个群 把你添加进去 此函数会返回数据,好了让我们继续往下走,或许你觉得我讲的废话很多,你可以关闭当前浏览器。

上面讲到发起了群聊视频,被邀请者 OnReceiveNewInvitation 方法会触发,此函数会返回 当前群聊视频房间所有信息,比如房间ID 房间成员,记住 这是视频群聊房间 不是上面创建的群。群不等于音频群聊房间。

你可以和我一样设置一个监听vuex -》 store.commit(“SET_IMTYPE”, true);当有人邀请我 需要弹出接受邀请按钮,下面我们来看看点击接受按钮后的事件

import { Room, RoomEvent, ParticipantEvent, Track } from "livekit-client";//记得引用
class VideoPreset {
  constructor(width, height, maxBitrate, maxFramerate) {
    this.width = width;
    this.height = height;
    this.encoding = {
      maxBitrate,
      maxFramerate,
    };
  }
  get resolution() {
    return {
      width: this.width,
      height: this.height,
      frameRate: this.encoding.maxFramerate,
      aspectRatio: this.width / this.height,
    };
  }
}

const VideoPresets = {
  h90: new VideoPreset(160, 90, "60_000", 15),
  h180: new VideoPreset(320, 180, "120_000", 15),
  h216: new VideoPreset(384, 216, "180_000", 15),
  h360: new VideoPreset(640, 360, "300_000", 20),
  h540: new VideoPreset(960, 540, "600_000", 25),
  h720: new VideoPreset(1280, 720, "2_000_000", 30),
  h1080: new VideoPreset(1920, 1080, "3_000_000", 30),
  h1440: new VideoPreset(2560, 1440, "5_000_000", 30),
  h2160: new VideoPreset(3840, 2160, "8_000_000", 30),
  /** @deprecated */
  qvga: new VideoPreset(320, 180, "120_000", 10),
  /** @deprecated */
  vga: new VideoPreset(640, 360, "300_000", 20),
  /** @deprecated */
  qhd: new VideoPreset(960, 540, "600_000", 25),
  /** @deprecated */
  hd: new VideoPreset(1280, 720, "2_000_000", 30),
};
// created 里面 需要 this.room = new Room(); 初始化
let obj = {
        opUserID: SET_IMTOKEN.userID,
        invitation: {
          inviterUserID: this.sessData.objData.invitation.inviterUserID, // 邀请者
          inviteeUserIDList: [SET_IMTOKEN.userID], // 参与者ID
          groupID: this.sessData.objData.invitation.groupID, // 群ID
          roomID: this.sessData.objData.invitation.roomID, //房间ID,必须唯一,可以不设置。
          timeout: 60, //邀请超时时间(秒)
          mediaType: "video",
          sessionType: 2,
        },
      };
      console.log("接受视频邀请参数", obj);
      openIM
        .signalingAccept(obj)
        .then(async ({ data }) => {
          this.signalAcceptReqData = JSON.parse(data);
          console.log(
            "接受某人邀请返回房间进入token,和 ws 地址",
            this.signalAcceptReqData
          );
          await this.room
            .connect(
              this.signalAcceptReqData.liveURL, // rtc 地址
              this.signalAcceptReqData.token, // rtc 链接当前登录用户token
              {
                // to publish camera and microphone immediately after joining 加入后立即发布相机和麦克风
                audio: true,
                video: true,
                // don't subscribe to other participants automatically 不要自动订阅其他参与者
                autoSubscribe: true,
                // automatically manage video quality 自动管理视频质量
                autoManageVideo: true,
                // default publish settings 默认发布
                publishDefaults: {
                  simulcast: true,
                },
                // default capture settings 默认捕获设置
                captureDefaults: {
                  videoResolution: VideoPresets.hd.resolution,
                },
              }
            )
            .then(async (room) => {
            // 此时你已经连接进入到了群聊视频房间
              console.log("livekit-client->connect 回调", room);
              this.roomData = room;
              room.on(
                RoomEvent.TrackSubscribed,
                (track, publication, participant) => {
                  let ele = document.getElementById("videodata");
                  var videoDomDiv = document.createElement("div"); // 创建文字标签

                  if (track.kind == "video") {
                    console.log("接受媒体", track, publication, participant);
                    videoDomDiv.setAttribute(
                      "style",
                      "height:150px;width:200px;display: flex;flex-direction: column-reverse;align-items: center;margin: 0px 5px;"
                    );
                    videoDomDiv.setAttribute("id", participant.identity);
                    let nameData = JSON.parse(participant.metadata);
                    videoDomDiv.innerHTML = nameData.userInfo.nickname
                      ? nameData.userInfo.nickname
                      : nameData.userInfo.userID;
                    console.log(
                      nameData.userInfo.nickname
                        ? nameData.userInfo.nickname
                        : nameData.userInfo.userID
                    );

                    ele.appendChild(videoDomDiv);
                  }
                  const element = track.attach();
                  element.className = participant.identity;
                  videoDomDiv.appendChild(element);
                }
              );

              room.on(
                RoomEvent.TrackPublished,
                (track, publication, participant) => {
                  console.log("查看房间活动", track, publication, participant);
                }
              );
              room.on(RoomEvent.LocalTrackPublished, (track, publication) => {
                console.log("本地音轨已发布", new Track(), track, publication);
              });
              room.on(RoomEvent.RoomMetadataChanged, (track) => {
                console.log("房间元数据已更改", track);
              });
              room.on(
                RoomEvent.TrackStreamStateChanged,
                (track, publication, participant) => {
                  console.log(
                    "跟踪流状态已更改",
                    track,
                    publication,
                    participant
                  );
                  if (document.getElementById(participant.identity)) {
                    console.log("当前ID,DOM存在", participant.identity);
                    document.getElementById(participant.identity).remove();
                    setTimeout(() => {
                      let ele = document.getElementById("videodata");
                      var videoDomDiv = document.createElement("div"); // 创建文字标签

                      if (track.kind == "video") {
                        console.log(
                          "跟踪流状态已更改后的媒体流数据",
                          track,
                          publication,
                          participant
                        );
                        videoDomDiv.setAttribute(
                          "style",
                          "height:150px;width:200px;display: flex;flex-direction: column-reverse;align-items: center;margin: 0px 5px;"
                        );
                        videoDomDiv.setAttribute("id", participant.identity);
                        let nameData = JSON.parse(participant.metadata);
                        videoDomDiv.innerHTML = nameData.userInfo.nickname
                          ? nameData.userInfo.nickname
                          : nameData.userInfo.userID;
                        console.log(
                          nameData.userInfo.nickname
                            ? nameData.userInfo.nickname
                            : nameData.userInfo.userID
                        );

                        ele.appendChild(videoDomDiv);
                      }
                      const element = track.track.attach();
                      element.className = participant.identity;
                      videoDomDiv.appendChild(element);
                    }, 200);
                  }
                }
              );
              room.on(
                RoomEvent.TrackUnsubscribed,
                (track, publication, participant) => {
                  console.log(
                    "从所有附加元素中删除轨道",
                    track,
                    publication,
                    participant
                  );
                  if (track.detach) {
                    track.detach();
                  }
                  if (document.getElementById(participant.identity)) {
                    document.getElementById(participant.identity).remove();
                  }
                }
              );

              room.on(RoomEvent.ConnectionQualityChanged, (participant) => {
                // 目前这里输出 good  excellent
                console.log("模仿源码未知函数", participant);
              });
              room.on(
                RoomEvent.TrackSubscriptionFailed,
                (track, publication, participant) => {
                  console.log("跟踪订阅失败", track, publication, participant);
                }
              );
              room.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
                // show UI indicators when participant is speaking
                // 当参与者发言时显示用户界面指示器
              });
              room.on(RoomEvent.Disconnected, () => {
                console.log("参与者离开时");
              });
              room.on(RoomEvent.LocalTrackUnpublished, (track, participant) => {
                console.log(
                  "当本地轨迹结束时,更新UI以将其从渲染中删除",
                  track,
                  participant
                );
                if (track.detach) {
                  track.detach();
                  document.getElementById(participant.identity).remove();
                }
              });
              room.on(RoomEvent.ParticipantConnected, (participant) => {
                // set up any per-participant callbacks 设置每个参与者的回调
                participant.on(ParticipantEvent.TrackMuted, (publication) => {
                  console.log("设置每个参与者的回调", publication);
                });
              });
              room.on(RoomEvent.ParticipantDisconnected, (track) => {
                console.log("参与者 分离", track);
                let elements = document.getElementsByClassName(track.identity);

                for (var i = elements.length - 1; i >= 0; i--) {
                  elements[i].parentNode.removeChild(elements[i]);
                }
              });
              room.on(RoomEvent.DataReceived, (payload, participant, kind) => {
                // receive data from other participants
                // 接收其他参与者的数据 RemoteTrackPublication
              });
              // publish local camera and mic tracks  发布本地摄像头和麦克风轨道
              await room.localParticipant.enableCameraAndMicrophone();
              const p = room.localParticipant;
              await p
                .setCameraEnabled(true)
                .then((data) => {
                  console.log("有摄像头", data);
                  navigator.mediaDevices
                    .getUserMedia({ audio: false, video: true })
                    .then((stream) => {
                      console.log("stream", stream, stream.getTracks());
                      this.loading = false;
                      this.videoStream =
                        typeof stream.stop === "function"
                          ? stream
                          : stream.getTracks();
                      let ele = document.getElementById("videodata"); // 获取父级标签
                      var videoDomDiv = document.createElement("div"); // 创建多媒体承载盒子标签
                      videoDomDiv.setAttribute(
                        "style",
                        "height:150px;width:200px;display: flex;flex-direction: column-reverse;align-items: center;"
                      );
                      videoDomDiv.innerHTML = store.state.userInfo.nickName;
                      var videoDom = document.createElement("video"); // 创建多媒体标签
                      videoDom.setAttribute("id", "newRTC");
                      videoDom.autoplay = true;
                      videoDom.setAttribute(
                        "style",
                        "height:125px;width:200px;"
                      );

                      videoDom.srcObject = stream;

                      videoDomDiv.appendChild(videoDom);
                      ele.appendChild(videoDomDiv);
                    })
                    .catch((err) => {
                      this.loading = false;
                      console.log(err);
                    });
                })
                .catch((err) => {
                  console.log("没有摄像头");
                }); // 调取摄像头 没有会报错
              await p
                .setMicrophoneEnabled(true)
                .then((data) => {
                  console.log("有麦克风", data);
                })
                .catch((err) => {
                  console.log("没有麦克风");
                }); // 调取声音 没有会报错
              // 屏幕 true 会分享屏幕
              await p
                .setScreenShareEnabled(false)
                .then((data) => {
                  console.log("屏幕", data);
                })
                .catch((err) => {
                  console.log("屏幕", err);
                });

              room.on(RoomEvent.AudioPlaybackStatusChanged, () => {
                if (!room.canPlayAudio) {
                  // UI is necessary.
                  console.log("333333");
                  button.onclick = () => {
                    room.startAudio().then(() => {
                      button.remove();
                    });
                  };
                }
              });
            })
            .catch((err) => {
              console.log("livekit-client->connect 回调 报错", err);
            });
        })
        .catch((err) => {
          this.loading = false;
          console.log("接受视频邀请 报错信息", err);
        });

3,关闭房间记得退群,因项目而异。这里说的群 是上面创建的群

喜欢就关注吧,后续将持续输出一些接触到的新东西

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值