user-controller.js 13 KB

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