2020.11.12 屏幕共享功能 终于理解了“A发布命令让B主动做某事”的逻辑

11.12

增加屏幕共享功能。

面试官点击按钮,要求考生发布自己的屏幕共享。

发送屏幕共享,A和B方都要init融云,先初始化im,再使用RTClib,再两人都进入房间。然后B获取屏幕信息(先不指定窗口,让考生那边自己选择)然后发布到服务器,然后A订阅到B的媒体流,展示。

初始化:初始化+进入房间

A:发送命令,让B主动发布视频共享。然后展示屏幕共享的媒体流

B:收到某个指令,或某个指令帮B执行发布操作,B就选择屏幕共享弹窗中的窗口发布。

先考虑,A发送命令,让B主动做事情,这个事情怎么实现。

B收到很多消息,通过判断extra来判断这是命令还是文本消息。如果是命令,就执行对应的操作,比如获取屏幕并发布。

(现在要找到在哪里接收信息的……)√发送成功的信息都在message.js里面了,可以实时监听commands有没有变多。

message.js中,它会return一个messages,这个东西是通过append增加的。append被使用两次,第一次使用是将准备发送的消息发过来,此时extra是PENDING状态,这时候就setMessages(为什么,这时候还没有发送成功啊???),然后sendMessage成功后再append一次,此时extra为空,进入条件分支语句将之前append的那个message的PENDING状态去掉。

append(message) {
          const {type, content, extra} = message;
          if (type === SELF && extra !== PENDING) {
              for (const message of _messages) {
                  if (message.extra === PENDING && message.content === content) {
                      message.extra = extra;
                      return setMessages([..._messages]);
                  }
              }
          }
          _messages = [..._messages, message];
          setMessages(_messages);
      }

那对方发送的message会怎么加到这个messages里面?

rongyun.js里面,im.watch中,有一个message的方法,这个方法会在收到函数时触发。可以修改这里的逻辑,通过判断extra,如果是普通message,就append;如果是command,就将message发送到commands中。

message({message}) {
           const {content: {content}, messageUId: uid, senderUserId: id, sentTime, extra:{extra}} = message;
           if(extra === "screen")
               imModel.addCommand({content, extra})
           else
               imModel.append({uid, type: Message.OTHER, content, id, timestamp: sentTime});
      },

 

 

(useMessages好像是一个管理全局状态的东西,所有我觉得也可以把命令发到这里?)那在useMessages这里增加一个commands相关方法:

const [commands, setCommands] = useState([]);
let _commands = [];
addCommand(message) {
    const {type, command, extra} = message;
    _commands = [..._commands, message];
    setCommands(_commands);
}

假设A点击“查看屏幕共享”按钮,就会生成一条message,extra设置为"screenshare",然后addCommands。

B处(在哪)收到新的commands后,根据不同的extra做不同的事情。比如现在它收到extra = "screenshare",那就立即获取屏幕信息并发布。

我认为B是在面试过程中收到面试官要查看屏幕的要求的,所有这个”监听“写在interviewee/room中比较好?

(怎么监听?)发送成功的信息都在message.js里面了,可以实时监听commands有没有变多。

干脆先这样,反正现在也只有screen这一个command,那只要command里面有东西,就获取屏幕资源并发布。并且发布成功后就清空命令。

然后看看,怎么在这个组件中使用融云那个组件里的东西

初始化后,给出send sendCommand sendMessage到rong里面。

rong在useRongyunVideo里面。

那我再rong增加一个发布屏幕资源的方法,让考生那边用。然后面试官就用sendCommand。

const video = useRongyunVideoModel();
const {setRong, setInfo} = video;

发布屏幕资源的方法:

async function screenShare(){
      const mediaStream = await navigator.mediaDevices.getDisplayMedia();
      user.stream = {
          tag: 'ScreenShare',
          type: StreamType.VIDEO,
          mediaStream
      };
      stream.publish(user).then(()=>{
          console.log("screen share success");});
  }

但是这样使用同一个user的stream会不会覆盖?没有覆盖

在考生那里,导入commands和useEffect,当commands变化时,调用发布的方法。

修改了rongyun.js,在订阅资源那里,根据stream的tag来判断,是对方的摄像头资源,还是屏幕共享资源,分别放到不同的标签里去展示。

成功了!

 

优化一:

参考简历上传页面。如果没有屏幕资源,就显示<Result>,如果有屏幕资源,就显示屏幕资源。

优化二:

命令存放为数组,每发送一个指令就询问共享哪个窗口。√

如何更新新的窗口,现在是考生重新共享别的窗口后,也不更新视频信息

优化三:

监听考生离开,监听考生回来

优化四:

只允许考生选择整个窗口(防止作弊),若未选择整个窗口,就要求重新选择,直到选择整个窗口。(这个我做出来了,只要检测到不是选择整个窗口的行为,包括关掉、拒绝共享、未选择整个窗口等,就重复发起共享请求。不要忘记停止这个资源的获取,有个类似stop track的方法可以做这件事,不然很可能会捕获很多次屏幕资源,风扇嗡嗡响

 

   let screenStream;
   async function screenStart() {

       for (; ;) {
           try {
               screenStream = await utils.shareScreen();
          } catch {
               message.warning("请选择整个屏幕");
               continue;
          }
           if (screenStream.getVideoTracks()[0].label.startsWith('screen')) {
               break;
          }

           message.warning("请选择整个屏幕");
           screenStream.getVideoTracks()[0].stop();
      }

       const screenUser = {
           ...user,
           stream: {
               tag: TAG_SCREEN,
               type: StreamType.VIDEO,
               mediaStream: screenStream
          }
      };
       stream.publish(screenUser);
       screenStream.getVideoTracks()[0].onended = () => stream.unpublish(screenUser);
  }

   async function screenStop() {
       screenStream.getVideoTracks()[0].stop();
       const screenUser = {
           ...user,
           stream: {
               tag: TAG_SCREEN,
               type: StreamType.VIDEO,
               mediaStream: screenStream
          }
      }
       stream.unpublish(screenUser);
  }

 

上一篇:LaTeX编译参考文献“I found no \citation commands---while reading file”问题


下一篇:Head First 设计模式 —— 06. 命令 (Command) 模式