user-controller.js 11 KB

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