use-websocket.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. import { copyToClipBoard, getRandomString } from 'billd-utils';
  2. import { NButton } from 'naive-ui';
  3. import { computed, h, onUnmounted, ref, watch } from 'vue';
  4. import { useRoute } from 'vue-router';
  5. import { fetchVerifyPkKey } from '@/api/liveRoom';
  6. import { THEME_COLOR, WEBSOCKET_URL } from '@/constant';
  7. import { useRTCParams } from '@/hooks/use-rtcParams';
  8. import { useTip } from '@/hooks/use-tip';
  9. import { useWebRtcLive } from '@/hooks/webrtc/live';
  10. import { useWebRtcMeetingOne } from '@/hooks/webrtc/meetingOne';
  11. import { useWebRtcSrs } from '@/hooks/webrtc/srs';
  12. import {
  13. DanmuMsgTypeEnum,
  14. ILiveUser,
  15. WsMessageMsgIsFileEnum,
  16. } from '@/interface';
  17. import router, { routerName } from '@/router';
  18. import { useAppStore } from '@/store/app';
  19. import { useNetworkStore } from '@/store/network';
  20. import { useUserStore } from '@/store/user';
  21. import { LiveRoomTypeEnum } from '@/types/ILiveRoom';
  22. import { IUser } from '@/types/IUser';
  23. import {
  24. IDanmu,
  25. WSGetRoomAllUserType,
  26. WSLivePkKeyType,
  27. WsAnswerType,
  28. WsCandidateType,
  29. WsConnectStatusEnum,
  30. WsDisableSpeakingType,
  31. WsGetLiveUserType,
  32. WsHeartbeatType,
  33. WsJoinType,
  34. WsLeavedType,
  35. WsMessageType,
  36. WsMsgTypeEnum,
  37. WsOfferType,
  38. WsOtherJoinType,
  39. WsRoomLivingType,
  40. WsStartLiveType,
  41. WsUpdateJoinInfoType,
  42. } from '@/types/websocket';
  43. import { createNullVideo, handleUserMedia } from '@/utils';
  44. import {
  45. WebSocketClass,
  46. prettierReceiveWsMsg,
  47. } from '@/utils/network/webSocket';
  48. import { useWebRtcMeetingPk } from './webrtc/meetingPk';
  49. export const useWebsocket = () => {
  50. const route = useRoute();
  51. const appStore = useAppStore();
  52. const userStore = useUserStore();
  53. const networkStore = useNetworkStore();
  54. const { maxBitrate, maxFramerate, resolutionRatio } = useRTCParams();
  55. const { updateWebRtcMeetingPkConfig, webRtcMeetingPk } = useWebRtcMeetingPk();
  56. const { updateWebRtcSrsConfig, webRtcSrs } = useWebRtcSrs();
  57. const { updateWebRtcLiveConfig, webRtcLive } = useWebRtcLive();
  58. const { updateWebRtcMeetingOneConfig, webRtcMeetingOne } =
  59. useWebRtcMeetingOne();
  60. const connectStatus = ref();
  61. const loopHeartbeatTimer = ref();
  62. const loopGetLiveUserTimer = ref();
  63. const liveUserList = ref<ILiveUser[]>([]);
  64. const roomId = ref('');
  65. const roomLiving = ref(false);
  66. const isAnchor = ref(false);
  67. const anchorInfo = ref<IUser>();
  68. const anchorSocketId = ref('');
  69. const canvasVideoStream = ref<MediaStream>();
  70. const userStream = ref<MediaStream>();
  71. const lastCoverImg = ref('');
  72. const currentMaxBitrate = ref(maxBitrate.value[3].value);
  73. const currentMaxFramerate = ref(maxFramerate.value[2].value);
  74. const currentResolutionRatio = ref(resolutionRatio.value[3].value);
  75. const timerObj = ref({});
  76. const damuList = ref<IDanmu[]>([]);
  77. onUnmounted(() => {
  78. clearInterval(loopHeartbeatTimer.value);
  79. clearInterval(loopGetLiveUserTimer.value);
  80. });
  81. watch(
  82. [() => userStore.userInfo?.id, () => connectStatus.value],
  83. ([userInfo, status]) => {
  84. if (userInfo && status === WsConnectStatusEnum.connect) {
  85. const ws = networkStore.wsMap.get(roomId.value);
  86. if (!ws) return;
  87. ws.send<WsUpdateJoinInfoType['data']>({
  88. requestId: getRandomString(8),
  89. msgType: WsMsgTypeEnum.updateJoinInfo,
  90. data: {
  91. live_room_id: Number(roomId.value),
  92. },
  93. });
  94. }
  95. },
  96. { immediate: true }
  97. );
  98. const mySocketId = computed(() => {
  99. return networkStore.wsMap.get(roomId.value)?.socketIo?.id || '-1';
  100. });
  101. function handleHeartbeat(socketId: string) {
  102. loopHeartbeatTimer.value = setInterval(() => {
  103. const ws = networkStore.wsMap.get(roomId.value);
  104. if (!ws) return;
  105. ws.send<WsHeartbeatType['data']>({
  106. requestId: getRandomString(8),
  107. msgType: WsMsgTypeEnum.heartbeat,
  108. data: {
  109. socket_id: socketId,
  110. live_room_id: Number(roomId.value),
  111. roomLiving: isAnchor.value && roomLiving.value,
  112. },
  113. });
  114. }, 1000 * 5);
  115. }
  116. function handleSendGetLiveUser(liveRoomId: number) {
  117. loopGetLiveUserTimer.value = setInterval(() => {
  118. const ws = networkStore.wsMap.get(roomId.value);
  119. if (!ws) return;
  120. ws.send<WsGetLiveUserType['data']>({
  121. requestId: getRandomString(8),
  122. msgType: WsMsgTypeEnum.getLiveUser,
  123. data: {
  124. live_room_id: liveRoomId,
  125. },
  126. });
  127. }, 1000 * 5);
  128. }
  129. function handleStartLive({
  130. coverImg,
  131. name,
  132. type,
  133. msrDelay,
  134. msrMaxDelay,
  135. }: {
  136. coverImg?: string;
  137. name?: string;
  138. type: LiveRoomTypeEnum;
  139. videoEl?: HTMLVideoElement;
  140. msrDelay: number;
  141. msrMaxDelay: number;
  142. }) {
  143. if (appStore.liveRoomInfo) {
  144. appStore.liveRoomInfo.type = type;
  145. }
  146. networkStore.wsMap.get(roomId.value)?.send<WsStartLiveType['data']>({
  147. requestId: getRandomString(8),
  148. msgType: WsMsgTypeEnum.startLive,
  149. data: {
  150. cover_img: coverImg!,
  151. name: name!,
  152. type,
  153. msrDelay,
  154. msrMaxDelay,
  155. },
  156. });
  157. if (type === LiveRoomTypeEnum.srs) {
  158. updateWebRtcSrsConfig({
  159. isPk: false,
  160. roomId: roomId.value,
  161. canvasVideoStream: canvasVideoStream.value,
  162. });
  163. webRtcSrs.newWebRtc({
  164. sender: mySocketId.value,
  165. receiver: 'srs',
  166. videoEl: createNullVideo(),
  167. });
  168. webRtcSrs.sendOffer({
  169. sender: mySocketId.value,
  170. receiver: 'srs',
  171. });
  172. } else if (type === LiveRoomTypeEnum.pk) {
  173. updateWebRtcSrsConfig({
  174. isPk: true,
  175. roomId: roomId.value,
  176. canvasVideoStream: canvasVideoStream.value,
  177. });
  178. webRtcSrs.newWebRtc({
  179. sender: mySocketId.value,
  180. receiver: 'srs',
  181. videoEl: createNullVideo(),
  182. });
  183. webRtcSrs.sendOffer({
  184. sender: mySocketId.value,
  185. receiver: 'srs',
  186. });
  187. }
  188. }
  189. function sendJoin() {
  190. const instance = networkStore.wsMap.get(roomId.value);
  191. if (!instance) return;
  192. instance.send<WsJoinType['data']>({
  193. requestId: getRandomString(8),
  194. msgType: WsMsgTypeEnum.join,
  195. data: {
  196. socket_id: mySocketId.value,
  197. live_room_id: Number(roomId.value),
  198. user_info: userStore.userInfo,
  199. },
  200. });
  201. }
  202. function initReceive() {
  203. const ws = networkStore.wsMap.get(roomId.value);
  204. if (!ws?.socketIo) return;
  205. // websocket连接成功
  206. ws.socketIo.on(WsConnectStatusEnum.connect, () => {
  207. prettierReceiveWsMsg(WsConnectStatusEnum.connect, ws.socketIo);
  208. handleHeartbeat(ws.socketIo!.id);
  209. if (!ws) return;
  210. connectStatus.value = WsConnectStatusEnum.connect;
  211. ws.status = WsConnectStatusEnum.connect;
  212. ws.update();
  213. sendJoin();
  214. });
  215. // websocket连接断开
  216. ws.socketIo.on(WsConnectStatusEnum.disconnect, (err) => {
  217. prettierReceiveWsMsg(WsConnectStatusEnum.disconnect, ws);
  218. console.log('websocket连接断开', err);
  219. if (!ws) return;
  220. ws.status = WsConnectStatusEnum.disconnect;
  221. ws.update();
  222. });
  223. // 收到livePkKey
  224. ws.socketIo.on(WsMsgTypeEnum.livePkKey, (data: WSLivePkKeyType['data']) => {
  225. console.log('收到livePkKey', data);
  226. const url = router.resolve({
  227. name: routerName.pull,
  228. params: { roomId: data.live_room_id },
  229. query: {
  230. pkKey: data.key,
  231. },
  232. });
  233. const pkurl = `${window.location.origin}${url.href}`;
  234. useTip({
  235. title: '邀请主播加入PK',
  236. width: '360px',
  237. hiddenCancel: true,
  238. content: h('div', [
  239. h('div', { style: { marginBottom: '5px' } }, `${pkurl}`),
  240. h(
  241. NButton,
  242. {
  243. size: 'small',
  244. type: 'primary',
  245. color: THEME_COLOR,
  246. onClick: () => {
  247. copyToClipBoard(pkurl);
  248. window.$message.success('复制成功!');
  249. },
  250. },
  251. () => '复制链接' // 用箭头函数返回性能更好。
  252. ),
  253. h('div', { style: { marginTop: '5px' } }, '注意,有效期:5分钟'),
  254. ]),
  255. }).catch(() => {});
  256. });
  257. // 收到srsOffer
  258. ws.socketIo.on(WsMsgTypeEnum.srsOffer, (data: WsOfferType['data']) => {
  259. console.log('收到srsOffer', data);
  260. });
  261. // 收到srsAnswer
  262. ws.socketIo.on(WsMsgTypeEnum.srsAnswer, (data: WsAnswerType['data']) => {
  263. console.log('收到srsAnswer', data);
  264. });
  265. // 收到srsCandidate
  266. ws.socketIo.on(
  267. WsMsgTypeEnum.srsCandidate,
  268. (data: WsCandidateType['data']) => {
  269. console.log('收到srsCandidate', data);
  270. if (data.receiver === mySocketId.value) {
  271. console.warn('是发给我的srsCandidate');
  272. const rtc = networkStore.rtcMap.get(data.sender);
  273. rtc?.addIceCandidate(data.candidate);
  274. } else {
  275. console.error('不是发给我的srsCandidate');
  276. }
  277. }
  278. );
  279. // 收到nativeWebRtcOffer
  280. ws.socketIo.on(
  281. WsMsgTypeEnum.nativeWebRtcOffer,
  282. async (data: WsOfferType['data']) => {
  283. console.log('收到nativeWebRtcOffer', data);
  284. if (data.live_room.type === LiveRoomTypeEnum.pk) {
  285. if (!route.query.pkKey) {
  286. return;
  287. }
  288. if (data.receiver === mySocketId.value) {
  289. console.warn('是发给我的nativeWebRtcOffer');
  290. const res = await fetchVerifyPkKey({
  291. liveRoomId: Number(roomId.value),
  292. key: route.query.pkKey,
  293. });
  294. if (res.code === 200 && res.data === true) {
  295. await useTip({
  296. content: '是否加入PK?',
  297. });
  298. const stream = await handleUserMedia({
  299. video: true,
  300. audio: true,
  301. });
  302. userStream.value = stream;
  303. updateWebRtcMeetingPkConfig({
  304. roomId: roomId.value,
  305. anchorStream: canvasVideoStream.value,
  306. userStream: userStream.value,
  307. });
  308. webRtcMeetingPk.newWebRtc({
  309. // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
  310. // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
  311. sender: mySocketId.value,
  312. receiver: data.sender,
  313. videoEl: createNullVideo(),
  314. });
  315. await webRtcMeetingPk.sendAnswer({
  316. sender: mySocketId.value,
  317. // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
  318. receiver: data.sender,
  319. sdp: data.sdp,
  320. });
  321. } else {
  322. await useTip({
  323. content: '加入PK失败,验证pkKey错误!',
  324. hiddenCancel: true,
  325. hiddenClose: true,
  326. });
  327. }
  328. } else {
  329. console.error('不是发给我的nativeWebRtcOffer');
  330. }
  331. } else if (data.live_room.type === LiveRoomTypeEnum.wertc_live) {
  332. if (data.receiver === mySocketId.value) {
  333. console.warn('是发给我的nativeWebRtcOffer');
  334. if (networkStore.rtcMap.get(data.sender)) {
  335. return;
  336. }
  337. updateWebRtcLiveConfig({
  338. roomId: roomId.value,
  339. canvasVideoStream: canvasVideoStream.value,
  340. });
  341. webRtcLive.newWebRtc({
  342. // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
  343. // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
  344. sender: mySocketId.value,
  345. receiver: data.sender,
  346. videoEl: createNullVideo(),
  347. });
  348. await webRtcLive.sendAnswer({
  349. sender: mySocketId.value,
  350. // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
  351. receiver: data.sender,
  352. sdp: data.sdp,
  353. });
  354. } else {
  355. console.error('不是发给我的nativeWebRtcOffer');
  356. }
  357. } else if (data.live_room.type === LiveRoomTypeEnum.wertc_meeting_one) {
  358. if (data.receiver === mySocketId.value) {
  359. console.warn('是发给我的nativeWebRtcOffer');
  360. await useTip({
  361. content: '是否加入会议?',
  362. });
  363. const stream = await handleUserMedia({
  364. video: true,
  365. audio: true,
  366. });
  367. userStream.value = stream;
  368. updateWebRtcMeetingOneConfig({
  369. roomId: roomId.value,
  370. userStream: userStream.value,
  371. anchorStream: canvasVideoStream.value,
  372. });
  373. if (networkStore.rtcMap.get(data.sender)) {
  374. return;
  375. }
  376. webRtcMeetingOne.newWebRtc({
  377. // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
  378. // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
  379. sender: mySocketId.value,
  380. receiver: data.sender,
  381. videoEl: createNullVideo(),
  382. });
  383. await webRtcMeetingOne.sendAnswer({
  384. sender: mySocketId.value,
  385. // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
  386. receiver: data.sender,
  387. sdp: data.sdp,
  388. });
  389. } else {
  390. console.error('不是发给我的nativeWebRtcOffer');
  391. }
  392. }
  393. }
  394. );
  395. // 收到nativeWebRtcAnswer
  396. ws.socketIo.on(
  397. WsMsgTypeEnum.nativeWebRtcAnswer,
  398. async (data: WsAnswerType['data']) => {
  399. console.log('收到nativeWebRtcAnswer', data);
  400. if (data.receiver === mySocketId.value) {
  401. console.warn('是发给我的nativeWebRtcAnswer');
  402. const rtc = networkStore.rtcMap.get(data.sender);
  403. if (rtc) {
  404. await rtc.setRemoteDescription(data.sdp);
  405. }
  406. } else {
  407. console.error('不是发给我的nativeWebRtcAnswer');
  408. }
  409. }
  410. );
  411. // 收到nativeWebRtcCandidate
  412. ws.socketIo.on(
  413. WsMsgTypeEnum.nativeWebRtcCandidate,
  414. (data: WsCandidateType['data']) => {
  415. console.log('收到nativeWebRtcCandidate', data);
  416. if (data.receiver === mySocketId.value) {
  417. console.warn('是发给我的nativeWebRtcCandidate');
  418. const rtc = networkStore.rtcMap.get(data.sender);
  419. rtc?.addIceCandidate(data.candidate);
  420. } else {
  421. console.error('不是发给我的nativeWebRtcCandidate');
  422. }
  423. }
  424. );
  425. // 主播正在直播
  426. ws.socketIo.on(
  427. WsMsgTypeEnum.roomLiving,
  428. (data: WsRoomLivingType['data']) => {
  429. prettierReceiveWsMsg(WsMsgTypeEnum.roomLiving, data);
  430. roomLiving.value = true;
  431. if (data.anchor_socket_id) {
  432. anchorSocketId.value = data.anchor_socket_id;
  433. }
  434. if (route.name === routerName.pull) {
  435. // 当前是拉流页面
  436. } else if (route.name === routerName.push) {
  437. // 当前是推流页面
  438. }
  439. }
  440. );
  441. // 主播不在直播
  442. ws.socketIo.on(WsMsgTypeEnum.roomNoLive, (data) => {
  443. prettierReceiveWsMsg(WsMsgTypeEnum.roomNoLive, data);
  444. roomLiving.value = false;
  445. });
  446. // 当前所有在线用户
  447. ws.socketIo.on(
  448. WsMsgTypeEnum.liveUser,
  449. (data: WSGetRoomAllUserType['data']) => {
  450. prettierReceiveWsMsg(WsMsgTypeEnum.liveUser, data);
  451. liveUserList.value = data.liveUser;
  452. }
  453. );
  454. // 收到用户发送消息
  455. ws.socketIo.on(WsMsgTypeEnum.message, (data: WsMessageType) => {
  456. prettierReceiveWsMsg(WsMsgTypeEnum.message, data);
  457. damuList.value.push({
  458. user_agent: data.data.user_agent,
  459. live_room_id: data.data.live_room_id,
  460. request_id: data.request_id,
  461. socket_id: data.socket_id,
  462. msgType: data.data.msgType,
  463. msg: data.data.msg,
  464. userInfo: data.user_info,
  465. msgIsFile: data.data.msgIsFile,
  466. send_msg_time: data.data.send_msg_time,
  467. });
  468. });
  469. // 收到disableSpeaking
  470. ws.socketIo.on(
  471. WsMsgTypeEnum.disableSpeaking,
  472. (data: WsDisableSpeakingType['data']) => {
  473. prettierReceiveWsMsg(WsMsgTypeEnum.disableSpeaking, data);
  474. // if (data.is_disable_speaking) {
  475. // window.$message.error('你已被禁言!');
  476. // appStore.disableSpeaking.set(data.live_room_id, {
  477. // exp: data.disable_expired_at,
  478. // label: formatDownTime({
  479. // startTime: +new Date(),
  480. // endTime: data.disable_expired_at,
  481. // }),
  482. // });
  483. // clearTimeout(timerObj.value[data.live_room_id]);
  484. // timerObj.value[data.live_room_id] = setInterval(() => {
  485. // if (
  486. // data.disable_expired_at &&
  487. // +new Date() > data.disable_expired_at
  488. // ) {
  489. // clearTimeout(timerObj.value[data.live_room_id]);
  490. // }
  491. // appStore.disableSpeaking.set(data.live_room_id, {
  492. // exp: data.disable_expired_at!,
  493. // label: formatDownTime({
  494. // startTime: +new Date(),
  495. // endTime: data.disable_expired_at!,
  496. // }),
  497. // });
  498. // }, 1000);
  499. // damuList.value = damuList.value.filter(
  500. // (v) => v.request_id !== data.request_id
  501. // );
  502. // }
  503. if (data.user_id !== userStore.userInfo?.id && data.disable_ok) {
  504. window.$message.success('禁言成功!');
  505. }
  506. if (
  507. data.user_id !== userStore.userInfo?.id &&
  508. data.restore_disable_ok
  509. ) {
  510. window.$message.success('解除禁言成功!');
  511. }
  512. if (
  513. data.user_id === userStore.userInfo?.id &&
  514. data.restore_disable_ok
  515. ) {
  516. window.$message.success('禁言接触了!');
  517. clearTimeout(timerObj.value[data.live_room_id]);
  518. appStore.disableSpeaking.delete(data.live_room_id);
  519. }
  520. }
  521. );
  522. // 用户加入房间完成
  523. ws.socketIo.on(WsMsgTypeEnum.joined, (data: WsJoinType['data']) => {
  524. prettierReceiveWsMsg(WsMsgTypeEnum.joined, data);
  525. appStore.setLiveRoomInfo(data.live_room);
  526. anchorInfo.value = data.anchor_info;
  527. });
  528. // 其他用户加入房间
  529. ws.socketIo.on(WsMsgTypeEnum.otherJoin, (data: WsOtherJoinType['data']) => {
  530. prettierReceiveWsMsg(WsMsgTypeEnum.otherJoin, data);
  531. const requestId = getRandomString(8);
  532. const danmu: IDanmu = {
  533. live_room_id: data.live_room.id!,
  534. request_id: requestId,
  535. msgType: DanmuMsgTypeEnum.otherJoin,
  536. socket_id: data.join_socket_id,
  537. userInfo: data.join_user_info,
  538. msgIsFile: WsMessageMsgIsFileEnum.no,
  539. msg: '',
  540. send_msg_time: +new Date(),
  541. };
  542. damuList.value.push(danmu);
  543. if (route.name === routerName.push) {
  544. // 当前是推流页面
  545. if (!isAnchor.value) {
  546. console.error('不是主播');
  547. return;
  548. }
  549. if (!roomLiving.value) {
  550. console.error('主播没点开始直播');
  551. return;
  552. }
  553. if (userStore.userInfo?.id === data.join_user_info?.id) {
  554. console.error('自己进入直播间,退出');
  555. return;
  556. }
  557. if (
  558. [
  559. LiveRoomTypeEnum.system,
  560. LiveRoomTypeEnum.srs,
  561. LiveRoomTypeEnum.obs,
  562. ].includes(data.live_room.type!)
  563. ) {
  564. return;
  565. }
  566. if (data.live_room.type === LiveRoomTypeEnum.wertc_live) {
  567. updateWebRtcLiveConfig({
  568. roomId: roomId.value,
  569. canvasVideoStream: canvasVideoStream.value,
  570. });
  571. data.socket_list.forEach((item) => {
  572. if (item !== mySocketId.value) {
  573. if (networkStore.rtcMap.get(item)) {
  574. return;
  575. }
  576. webRtcLive.newWebRtc({
  577. sender: mySocketId.value,
  578. receiver: item,
  579. videoEl: createNullVideo(),
  580. });
  581. webRtcLive.sendOffer({
  582. sender: mySocketId.value,
  583. receiver: item,
  584. });
  585. }
  586. });
  587. } else if (data.live_room.type === LiveRoomTypeEnum.wertc_meeting_one) {
  588. updateWebRtcMeetingOneConfig({
  589. roomId: roomId.value,
  590. anchorStream: canvasVideoStream.value,
  591. });
  592. data.socket_list?.forEach((item) => {
  593. if (item !== mySocketId.value) {
  594. if (networkStore.rtcMap.get(item)) {
  595. return;
  596. }
  597. webRtcMeetingOne.newWebRtc({
  598. sender: mySocketId.value,
  599. receiver: item,
  600. videoEl: createNullVideo(),
  601. });
  602. webRtcMeetingOne.sendOffer({
  603. sender: mySocketId.value,
  604. receiver: item,
  605. });
  606. }
  607. });
  608. } else if (data.live_room.type === LiveRoomTypeEnum.pk) {
  609. updateWebRtcMeetingPkConfig({
  610. roomId: roomId.value,
  611. anchorStream: canvasVideoStream.value,
  612. });
  613. data.socket_list?.forEach((item) => {
  614. if (item !== mySocketId.value) {
  615. if (networkStore.rtcMap.get(item)) {
  616. return;
  617. }
  618. webRtcMeetingPk.newWebRtc({
  619. sender: mySocketId.value,
  620. receiver: item,
  621. videoEl: createNullVideo(),
  622. });
  623. webRtcMeetingPk.sendOffer({
  624. sender: mySocketId.value,
  625. receiver: item,
  626. });
  627. }
  628. });
  629. }
  630. } else {
  631. // 当前不是推流页面
  632. }
  633. });
  634. // 用户离开房间
  635. ws.socketIo.on(WsMsgTypeEnum.leave, (data) => {
  636. prettierReceiveWsMsg(WsMsgTypeEnum.leave, data);
  637. });
  638. // 用户离开房间完成
  639. ws.socketIo.on(WsMsgTypeEnum.leaved, (data: WsLeavedType['data']) => {
  640. prettierReceiveWsMsg(WsMsgTypeEnum.leaved, data);
  641. if (anchorSocketId.value === data.socket_id) {
  642. roomLiving.value = false;
  643. }
  644. networkStore.removeRtc(data.socket_id);
  645. damuList.value.push({
  646. live_room_id: Number(roomId.value),
  647. socket_id: data.socket_id,
  648. msgType: DanmuMsgTypeEnum.userLeaved,
  649. msgIsFile: WsMessageMsgIsFileEnum.no,
  650. userInfo: data.user_info,
  651. msg: '',
  652. send_msg_time: +new Date(),
  653. });
  654. });
  655. }
  656. function initWs(data: {
  657. isAnchor: boolean;
  658. roomId: string;
  659. currentResolutionRatio?: number;
  660. currentMaxFramerate?: number;
  661. currentMaxBitrate?: number;
  662. }) {
  663. roomId.value = data.roomId;
  664. isAnchor.value = data.isAnchor;
  665. if (data.currentMaxBitrate) {
  666. currentMaxBitrate.value = data.currentMaxBitrate;
  667. }
  668. if (data.currentMaxFramerate) {
  669. currentMaxFramerate.value = data.currentMaxFramerate;
  670. }
  671. if (data.currentResolutionRatio) {
  672. currentResolutionRatio.value = data.currentResolutionRatio;
  673. }
  674. new WebSocketClass({
  675. roomId: roomId.value,
  676. url: WEBSOCKET_URL,
  677. isAnchor: data.isAnchor,
  678. });
  679. initReceive();
  680. }
  681. return {
  682. initWs,
  683. handleStartLive,
  684. handleSendGetLiveUser,
  685. mySocketId,
  686. canvasVideoStream,
  687. lastCoverImg,
  688. roomLiving,
  689. anchorInfo,
  690. liveUserList,
  691. damuList,
  692. currentMaxFramerate,
  693. currentMaxBitrate,
  694. currentResolutionRatio,
  695. };
  696. };