user-controller.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import Event from "../utils/event";
  2. import User from "../model/user";
  3. import Stream from "../model/stream";
  4. import { EVENT } from "../common/constants";
  5. const TAG_NAME = 'UserController';
  6. /**
  7. * 通讯成员管理
  8. */
  9. class UserController {
  10. constructor(componentContext) {
  11. // userMap 用于存储完整的数据结构
  12. this.userMap = new Map(); // userList 用于存储简化的用户数据 Object,包括 {userID hasMainAudio hasMainVideo hasAuxAudio hasAuxVideo}
  13. this.userList = []; // streamList 存储steam 对象列表,用于trtc-room 渲染 player
  14. this.streamList = [];
  15. this._emitter = new Event();
  16. this.componentContext = componentContext;
  17. this.isNewVersion = componentContext.isNewVersion;
  18. }
  19. userEventHandler(event) {
  20. const code = event.detail.code;
  21. let data;
  22. if (event.detail.message && typeof event.detail.message === 'string') {
  23. try {
  24. data = JSON.parse(event.detail.message);
  25. } catch (exception) {
  26. console.warn(TAG_NAME, 'userEventHandler 数据格式错误', exception);
  27. return false;
  28. }
  29. } else {
  30. console.warn(TAG_NAME, 'userEventHandler 数据格式错误');
  31. return false;
  32. }
  33. switch (code) {
  34. case 1020:
  35. // console.log(TAG_NAME, '远端用户全量列表更新:', code)
  36. if (!this.isNewVersion) {// TODO 旧版SDK处理逻辑,返回全量的用户列表,需要对userList 进行前后对比,筛选出新增用户,暂不实现
  37. }
  38. break;
  39. case 1031:
  40. // console.log(TAG_NAME, '远端用户进房通知:', code)
  41. // 1031 有新用户
  42. // {
  43. // "userlist":[
  44. // {
  45. // "userid":"webrtc11"
  46. // }
  47. // ]
  48. // }
  49. this.addUser(data);
  50. break;
  51. case 1032:
  52. // console.log(TAG_NAME, '远端用户退房通知:', code)
  53. // 1032 有用户退出
  54. this.removeUser(data);
  55. break;
  56. case 1033:
  57. // console.log(TAG_NAME, '远端用户视频状态位变化通知:', code)
  58. // 1033 用户视频状态变化,新增stream或者更新stream 状态
  59. // {
  60. // "userlist":[
  61. // {
  62. // "userid":"webrtc11",
  63. // "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main",
  64. // "streamtype":"main",
  65. // "hasvideo":true
  66. // }
  67. // ]
  68. // }
  69. this.updateUserVideo(data);
  70. break;
  71. case 1034:
  72. // console.log(TAG_NAME, '远端用户音频状态位变化通知:', code)
  73. // 1034 用户音频状态变化
  74. // {
  75. // "userlist":[
  76. // {
  77. // "userid":"webrtc11",
  78. // "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main",
  79. // "hasaudio":false
  80. // }
  81. // ]
  82. // }
  83. this.updateUserAudio(data);
  84. break;
  85. }
  86. }
  87. /**
  88. * 处理用户进房事件
  89. * @param {Object} data pusher 下发的数据
  90. */
  91. addUser(data) {
  92. // console.log(TAG_NAME, 'addUser', data)
  93. const incomingUserList = data.userlist;
  94. const userMap = this.userMap;
  95. if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
  96. incomingUserList.forEach(item => {
  97. const userID = item.userid; // 已经在 map 中的用户
  98. let user = this.getUser(userID);
  99. if (!user) {
  100. // 新增用户
  101. user = new User({
  102. userID: userID
  103. });
  104. this.userList.push({
  105. userID: userID
  106. });
  107. }
  108. userMap.set(userID, user);
  109. this._emitter.emit(EVENT.REMOTE_USER_JOIN, {
  110. userID: userID,
  111. userList: this.userList
  112. }); // console.log(TAG_NAME, 'addUser', item, userMap.get(userID), this.userMap)
  113. });
  114. }
  115. }
  116. /**
  117. * 处理用户退房事件
  118. * @param {Object} data pusher 下发的数据 {userlist}
  119. */
  120. removeUser(data) {
  121. // console.log(TAG_NAME, 'removeUser', data)
  122. const incomingUserList = data.userlist;
  123. if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
  124. incomingUserList.forEach(item => {
  125. const userID = item.userid;
  126. let user = this.getUser(userID); // 从userList 里删除指定的用户和 stream
  127. this._removeUserAndStream(userID); // 重置
  128. user.streams['main'] && user.streams['main'].reset();
  129. user.streams['aux'] && user.streams['aux'].reset(); // 用户退出,释放引用,外部调用该 user 所有stream 的 playerContext.stop() 方法停止播放
  130. // TODO 触发时机提前了,方便外部用户做出处理,时机仍需进一步验证
  131. this._emitter.emit(EVENT.REMOTE_USER_LEAVE, {
  132. userID: userID,
  133. userList: this.userList,
  134. streamList: this.streamList
  135. });
  136. user = undefined;
  137. this.userMap.delete(userID); // console.log(TAG_NAME, 'removeUser', this.userMap)
  138. });
  139. }
  140. }
  141. /**
  142. * 处理用户视频通知事件
  143. * @param {Object} data pusher 下发的数据 {userlist}
  144. */
  145. updateUserVideo(data) {
  146. // console.log(TAG_NAME, 'updateUserVideo', data)
  147. const incomingUserList = data.userlist;
  148. if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
  149. incomingUserList.forEach(item => {
  150. // 修改现有用户属性
  151. const userID = item.userid;
  152. const streamType = item.streamtype;
  153. const hasVideo = item.hasvideo;
  154. const src = item.playurl;
  155. const user = this.getUser(userID); // 如果找到该用户,则更新用户的信息和相关的stream 信息
  156. if (user) {
  157. let stream = user.streams[streamType]; // console.log(TAG_NAME, 'updateUserVideo', user, streamType, stream)
  158. // 更新指定的stream
  159. if (!stream) {
  160. user.streams[streamType] = stream = new Stream({
  161. streamType: streamType
  162. }); // 更新streamList
  163. this.streamList.push(stream);
  164. }
  165. if (hasVideo && streamType === 'aux') {
  166. stream.objectFit = 'contain';
  167. }
  168. if (!hasVideo && streamType === 'aux') {
  169. // TODO 如果是辅流可能要移除该 stream
  170. this._removeStream(stream);
  171. } else {
  172. stream.userID = userID;
  173. stream.streamID = userID + '_' + streamType;
  174. stream.hasVideo = hasVideo;
  175. stream.src = src;
  176. } // 更新所属user 的 hasXxx 值
  177. this.userList.find(item => {
  178. if (item.userID === userID) {
  179. item[`has${streamType.replace(/^\S/, s => s.toUpperCase())}Video`] = hasVideo;
  180. return true;
  181. }
  182. });
  183. const eventName = hasVideo ? EVENT.REMOTE_VIDEO_ADD : EVENT.REMOTE_VIDEO_REMOVE;
  184. this._emitter.emit(eventName, {
  185. stream: stream,
  186. streamList: this.streamList,
  187. userList: this.userList
  188. }); // console.log(TAG_NAME, 'updateUserVideo', user, stream, this.userMap)
  189. }
  190. });
  191. }
  192. }
  193. /**
  194. * 处理用户音频通知事件
  195. * @param {Object} data pusher 下发的数据 {userlist}
  196. */
  197. updateUserAudio(data) {
  198. // console.log(TAG_NAME, 'updateUserAudio', data)
  199. const incomingUserList = data.userlist;
  200. if (Array.isArray(incomingUserList) && incomingUserList.length > 0) {
  201. incomingUserList.forEach(item => {
  202. const userID = item.userid; // 音频只跟着 stream main ,这里只修改 main
  203. const streamType = 'main';
  204. const hasAudio = item.hasaudio;
  205. const src = item.playurl;
  206. const user = this.getUser(userID);
  207. if (user) {
  208. let stream = user.streams[streamType];
  209. if (!stream) {
  210. user.streams[streamType] = stream = new Stream({
  211. streamType: streamType
  212. }); // 更新streamList
  213. this.streamList.push(stream);
  214. }
  215. stream.userID = userID;
  216. stream.streamID = userID + '_' + streamType;
  217. stream.hasAudio = hasAudio;
  218. stream.src = src; // 更新所属 user 的 hasXxx 值
  219. this.userList.find(item => {
  220. if (item.userID === userID) {
  221. item[`has${streamType.replace(/^\S/, s => s.toUpperCase())}Audio`] = hasAudio;
  222. return true;
  223. }
  224. });
  225. const eventName = hasAudio ? EVENT.REMOTE_AUDIO_ADD : EVENT.REMOTE_AUDIO_REMOVE;
  226. this._emitter.emit(eventName, {
  227. stream: stream,
  228. streamList: this.streamList,
  229. userList: this.userList
  230. }); // console.log(TAG_NAME, 'updateUserAudio', user, stream, this.userMap)
  231. }
  232. });
  233. }
  234. }
  235. /**
  236. *
  237. * @param {String} userID 用户ID
  238. * @returns {Object}
  239. */
  240. getUser(userID) {
  241. return this.userMap.get(userID);
  242. }
  243. getStream({
  244. userID,
  245. streamType
  246. }) {
  247. const user = this.userMap.get(userID);
  248. if (user) {
  249. return user.streams[streamType];
  250. }
  251. return undefined;
  252. }
  253. getUserList() {
  254. return this.userList;
  255. }
  256. getStreamList() {
  257. return this.streamList;
  258. }
  259. /**
  260. * 重置所有user 和 steam
  261. * @returns {Object}
  262. */
  263. reset() {
  264. this.streamList.forEach(item => {
  265. item.reset();
  266. });
  267. this.streamList = [];
  268. this.userList = [];
  269. this.userMap.clear();
  270. return {
  271. userList: this.userList,
  272. streamList: this.streamList
  273. };
  274. }
  275. on(eventCode, handler, context) {
  276. this._emitter.on(eventCode, handler, context);
  277. }
  278. off(eventCode, handler) {
  279. this._emitter.off(eventCode, handler);
  280. }
  281. /**
  282. * 删除用户和所有的 stream
  283. * @param {String} userID 用户ID
  284. */
  285. _removeUserAndStream(userID) {
  286. this.streamList = this.streamList.filter(item => {
  287. return item.userID !== userID && item.userID !== '';
  288. });
  289. this.userList = this.userList.filter(item => {
  290. return item.userID !== userID;
  291. });
  292. }
  293. _removeStream(stream) {
  294. this.streamList = this.streamList.filter(item => {
  295. if (item.userID === stream.userID && item.streamType === stream.streamType) {
  296. return false;
  297. }
  298. return true;
  299. });
  300. }
  301. }
  302. export default UserController;