use-pull.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. import { getRandomString, judgeDevice } from 'billd-utils';
  2. import { Ref, nextTick, onUnmounted, reactive, ref, watch } from 'vue';
  3. import { useRoute } from 'vue-router';
  4. import { fetchRtcV1Play } from '@/api/srs';
  5. import { useFlvPlay, useHlsPlay } from '@/hooks/use-play';
  6. import {
  7. DanmuMsgTypeEnum,
  8. IAnswer,
  9. ICandidate,
  10. IDanmu,
  11. IJoin,
  12. ILive,
  13. ILiveUser,
  14. IMessage,
  15. IOffer,
  16. IOtherJoin,
  17. IUpdateJoinInfo,
  18. LiveRoomTypeEnum,
  19. MediaTypeEnum,
  20. liveTypeEnum,
  21. } from '@/interface';
  22. import { SRSWebRTCClass } from '@/network/srsWebRtc';
  23. import { WebRTCClass } from '@/network/webRtc';
  24. import {
  25. WebSocketClass,
  26. WsConnectStatusEnum,
  27. WsMsgTypeEnum,
  28. prettierReceiveWebsocket,
  29. } from '@/network/webSocket';
  30. import { useNetworkStore } from '@/store/network';
  31. import { useUserStore } from '@/store/user';
  32. import { videoToCanvas } from '@/utils';
  33. export function usePull({
  34. localVideoRef,
  35. canvasRef,
  36. isSRS,
  37. isFlv,
  38. }: {
  39. localVideoRef: Ref<HTMLVideoElement[]>;
  40. canvasRef: Ref<HTMLDivElement | undefined>;
  41. isSRS?: boolean;
  42. isFlv?: boolean;
  43. }) {
  44. const route = useRoute();
  45. const userStore = useUserStore();
  46. const networkStore = useNetworkStore();
  47. const videoEl = document.createElement('video');
  48. videoEl.muted = true;
  49. videoEl.playsInline = true;
  50. videoEl.setAttribute('webkit-playsinline', 'true');
  51. const remoteVideoRef = ref(videoEl);
  52. const heartbeatTimer = ref();
  53. const roomId = ref(route.params.roomId as string);
  54. const roomName = ref('');
  55. const userName = ref('');
  56. const userAvatar = ref('');
  57. const currentLiveRoom = ref<ILive>();
  58. const streamurl = ref('');
  59. const flvurl = ref('');
  60. const hlsurl = ref('');
  61. const coverImg = ref('');
  62. const danmuStr = ref('');
  63. const balance = ref('0.00');
  64. const damuList = ref<IDanmu[]>([]);
  65. const liveUserList = ref<ILiveUser[]>([]);
  66. const autoplayVal = ref(false);
  67. const videoLoading = ref(false);
  68. const isDone = ref(false);
  69. const roomNoLive = ref(false);
  70. const localStream = ref();
  71. const sidebarList = ref<
  72. {
  73. socketId: string;
  74. }[]
  75. >([]);
  76. const { flvPlayer, startFlvPlay } = useFlvPlay();
  77. const { startHlsPlay } = useHlsPlay();
  78. const track = reactive({
  79. audio: 1,
  80. video: 1,
  81. });
  82. const giftList = ref([
  83. { name: '鲜花', ico: '', price: '免费' },
  84. { name: '肥宅水', ico: '', price: '2元' },
  85. { name: '小鸡腿', ico: '', price: '3元' },
  86. { name: '大鸡腿', ico: '', price: '5元' },
  87. { name: '一杯咖啡', ico: '', price: '10元' },
  88. ]);
  89. const offerSended = ref(new Set());
  90. const hooksRtcMap = ref(new Set());
  91. const sender = ref();
  92. const allMediaTypeList = {
  93. [MediaTypeEnum.camera]: {
  94. type: MediaTypeEnum.camera,
  95. txt: '摄像头',
  96. },
  97. [MediaTypeEnum.screen]: {
  98. type: MediaTypeEnum.screen,
  99. txt: '窗口',
  100. },
  101. };
  102. const currMediaTypeList = ref<
  103. {
  104. type: MediaTypeEnum;
  105. txt: string;
  106. }[]
  107. >([]);
  108. const currMediaType = ref<{
  109. type: MediaTypeEnum;
  110. txt: string;
  111. }>();
  112. onUnmounted(() => {
  113. clearInterval(heartbeatTimer.value);
  114. });
  115. /** 摄像头 */
  116. async function startGetUserMedia() {
  117. if (!localStream.value) {
  118. // WARN navigator.mediaDevices在localhost和https才能用,http://192.168.1.103:8000局域网用不了
  119. const event = await navigator.mediaDevices.getUserMedia({
  120. video: true,
  121. audio: true,
  122. });
  123. const audio = event.getAudioTracks();
  124. const video = event.getVideoTracks();
  125. track.audio = audio.length ? 1 : 2;
  126. track.video = video.length ? 1 : 2;
  127. console.log('getUserMedia成功', event);
  128. currMediaType.value = allMediaTypeList[MediaTypeEnum.camera];
  129. currMediaTypeList.value.push(allMediaTypeList[MediaTypeEnum.camera]);
  130. localStream.value = event;
  131. }
  132. }
  133. /** 窗口 */
  134. async function startGetDisplayMedia() {
  135. if (!localStream.value) {
  136. // WARN navigator.mediaDevices.getDisplayMedia在localhost和https才能用,http://192.168.1.103:8000局域网用不了
  137. const event = await navigator.mediaDevices.getDisplayMedia({
  138. video: true,
  139. audio: true,
  140. });
  141. const audio = event.getAudioTracks();
  142. const video = event.getVideoTracks();
  143. track.audio = audio.length ? 1 : 2;
  144. track.video = video.length ? 1 : 2;
  145. console.log('getDisplayMedia成功', event);
  146. currMediaType.value = allMediaTypeList[MediaTypeEnum.screen];
  147. currMediaTypeList.value.push(allMediaTypeList[MediaTypeEnum.screen]);
  148. localStream.value = event;
  149. }
  150. }
  151. watch(
  152. [
  153. () => userStore.userInfo,
  154. () => networkStore.wsMap.get(roomId.value)?.socketIo?.connected,
  155. ],
  156. ([userInfo, connected]) => {
  157. if (userInfo) {
  158. balance.value = userInfo.wallet?.balance || '0.00';
  159. }
  160. if (userInfo && connected) {
  161. const instance = networkStore.wsMap.get(roomId.value);
  162. if (!instance) return;
  163. const data: IUpdateJoinInfo['data'] = {
  164. live_room_id: Number(roomId.value),
  165. };
  166. instance.send({
  167. msgType: WsMsgTypeEnum.updateJoinInfo,
  168. data,
  169. });
  170. }
  171. }
  172. );
  173. function initPull(autolay = true) {
  174. autoplayVal.value = autolay;
  175. if (autoplayVal.value) {
  176. videoLoading.value = true;
  177. }
  178. console.warn('开始new WebSocketClass');
  179. const ws = new WebSocketClass({
  180. roomId: roomId.value,
  181. url:
  182. process.env.NODE_ENV === 'development'
  183. ? 'ws://localhost:4300'
  184. : 'wss://live.hsslive.cn',
  185. isAnchor: false,
  186. });
  187. ws.update();
  188. initReceive();
  189. remoteVideoRef.value?.addEventListener('loadstart', () => {
  190. console.warn('视频流-loadstart');
  191. const rtc = networkStore.getRtcMap(roomId.value);
  192. if (!rtc) return;
  193. rtc.rtcStatus.loadstart = true;
  194. rtc.update();
  195. });
  196. remoteVideoRef.value?.addEventListener('loadedmetadata', () => {
  197. console.warn('视频流-loadedmetadata');
  198. videoLoading.value = false;
  199. const rtc = networkStore.getRtcMap(roomId.value);
  200. if (!rtc) return;
  201. rtc.rtcStatus.loadedmetadata = true;
  202. rtc.update();
  203. });
  204. }
  205. function handleHeartbeat() {
  206. heartbeatTimer.value = setInterval(() => {
  207. const instance = networkStore.wsMap.get(roomId.value);
  208. if (!instance) return;
  209. instance.send({
  210. msgType: WsMsgTypeEnum.heartbeat,
  211. });
  212. }, 1000 * 5);
  213. }
  214. function closeWs() {
  215. const instance = networkStore.wsMap.get(roomId.value);
  216. instance?.close();
  217. }
  218. function closeRtc() {
  219. networkStore.rtcMap.forEach((rtc) => {
  220. rtc.close();
  221. });
  222. }
  223. function getSocketId() {
  224. return networkStore.wsMap.get(roomId.value!)?.socketIo?.id || '-1';
  225. }
  226. function sendJoin() {
  227. const instance = networkStore.wsMap.get(roomId.value);
  228. if (!instance) return;
  229. const joinData: IJoin['data'] = {
  230. live_room: {
  231. id: Number(roomId.value),
  232. name: roomName.value,
  233. type: isSRS ? LiveRoomTypeEnum.user_srs : LiveRoomTypeEnum.user_wertc,
  234. },
  235. track,
  236. };
  237. instance.send({
  238. msgType: WsMsgTypeEnum.join,
  239. data: joinData,
  240. });
  241. }
  242. function addTransceiver(socketId: string) {
  243. if (!localStream.value) return;
  244. if (socketId !== getSocketId()) {
  245. localStream.value.getTracks().forEach((track) => {
  246. const rtc = networkStore.getRtcMap(`${roomId.value}___${socketId}`);
  247. rtc?.addTransceiver(track, localStream.value);
  248. });
  249. }
  250. }
  251. function addTrack() {
  252. if (!localStream.value) return;
  253. liveUserList.value.forEach((item) => {
  254. if (item.id !== getSocketId()) {
  255. localStream.value.getTracks().forEach((track) => {
  256. const rtc = networkStore.getRtcMap(`${roomId.value}___${item.id}`);
  257. rtc?.addTrack(track, localStream.value);
  258. });
  259. }
  260. });
  261. }
  262. async function sendOffer({
  263. sender,
  264. receiver,
  265. }: {
  266. sender: string;
  267. receiver: string;
  268. }) {
  269. if (isDone.value) return;
  270. const instance = networkStore.wsMap.get(roomId.value);
  271. if (!instance) return;
  272. const rtc = networkStore.getRtcMap(`${roomId.value}___${receiver}`);
  273. if (!rtc) return;
  274. const sdp = await rtc.createOffer();
  275. await rtc.setLocalDescription(sdp);
  276. const offerData = {
  277. sdp,
  278. sender,
  279. receiver,
  280. live_room_id: roomId.value,
  281. };
  282. instance.send({
  283. msgType: WsMsgTypeEnum.offer,
  284. data: offerData,
  285. });
  286. }
  287. async function batchSendOffer(socketId: string) {
  288. console.log('batchSendOffer', socketId);
  289. await nextTick(async () => {
  290. if (!offerSended.value.has(socketId) && socketId !== getSocketId()) {
  291. hooksRtcMap.value.add(await startNewWebRtc({ receiver: socketId }));
  292. await addTransceiver(socketId);
  293. console.log('执行sendOffer', {
  294. sender: getSocketId(),
  295. receiver: socketId,
  296. });
  297. sendOffer({ sender: getSocketId(), receiver: socketId });
  298. offerSended.value.add(socketId);
  299. }
  300. });
  301. }
  302. function addVideo() {
  303. sidebarList.value.push({ socketId: getSocketId() });
  304. nextTick(() => {
  305. liveUserList.value.forEach(async (item) => {
  306. const socketId = item.id;
  307. if (socketId === getSocketId()) {
  308. localVideoRef.value[getSocketId()].srcObject = localStream.value;
  309. }
  310. if (!offerSended.value.has(socketId)) {
  311. hooksRtcMap.value.add(
  312. await startNewWebRtc({
  313. receiver: socketId,
  314. videoEl: localVideoRef.value[socketId],
  315. // videoEl: localVideoRef.value[sender.value],
  316. })
  317. );
  318. await addTransceiver(socketId);
  319. console.log('执行sendOffer', {
  320. sender: getSocketId(),
  321. receiver: socketId,
  322. });
  323. sendOffer({ sender: getSocketId(), receiver: socketId });
  324. offerSended.value.add(socketId);
  325. }
  326. });
  327. });
  328. }
  329. /** 原生的webrtc时,receiver必传 */
  330. async function startNewWebRtc({
  331. receiver,
  332. videoEl = remoteVideoRef.value!,
  333. }: {
  334. receiver?: string;
  335. videoEl?: HTMLVideoElement;
  336. }) {
  337. if (isSRS) {
  338. if (!autoplayVal.value) return;
  339. console.warn('开始new SRSWebRTCClass', getSocketId());
  340. const rtc = new SRSWebRTCClass({
  341. roomId: `${roomId.value}___${getSocketId()}`,
  342. videoEl,
  343. });
  344. rtc.rtcStatus.joined = true;
  345. rtc.update();
  346. if (track.video === 1) {
  347. rtc.peerConnection?.addTransceiver('video', { direction: 'recvonly' });
  348. }
  349. if (track.audio === 1) {
  350. rtc.peerConnection?.addTransceiver('audio', { direction: 'recvonly' });
  351. }
  352. try {
  353. const offer = await rtc.createOffer();
  354. if (!offer) return;
  355. await rtc.setLocalDescription(offer);
  356. const res: any = await fetchRtcV1Play({
  357. api: `${
  358. process.env.NODE_ENV === 'development'
  359. ? 'http://localhost:1985'
  360. : 'https://live.hsslive.cn/srs'
  361. }/rtc/v1/play/`,
  362. clientip: null,
  363. sdp: offer.sdp!,
  364. streamurl: streamurl.value,
  365. tid: getRandomString(10),
  366. });
  367. await rtc.setRemoteDescription(
  368. new RTCSessionDescription({ type: 'answer', sdp: res.sdp })
  369. );
  370. } catch (error) {
  371. console.log(error);
  372. }
  373. } else {
  374. if (!autoplayVal.value) return;
  375. console.warn('开始new WebRTCClass');
  376. const rtc = new WebRTCClass({
  377. roomId: `${roomId.value}___${receiver!}`,
  378. videoEl,
  379. });
  380. return rtc;
  381. }
  382. }
  383. function keydownDanmu(event: KeyboardEvent) {
  384. const key = event.key.toLowerCase();
  385. if (key === 'enter') {
  386. event.preventDefault();
  387. sendDanmu();
  388. }
  389. }
  390. function sendDanmu() {
  391. if (!danmuStr.value.trim().length) {
  392. window.$message.warning('请输入弹幕内容!');
  393. return;
  394. }
  395. const instance = networkStore.wsMap.get(roomId.value);
  396. if (!instance) return;
  397. const danmu: IDanmu = {
  398. socket_id: getSocketId(),
  399. userInfo: userStore.userInfo,
  400. msgType: DanmuMsgTypeEnum.danmu,
  401. msg: danmuStr.value,
  402. };
  403. const messageData: IMessage['data'] = {
  404. msg: danmuStr.value,
  405. msgType: DanmuMsgTypeEnum.danmu,
  406. live_room_id: Number(roomId.value),
  407. };
  408. instance.send({
  409. msgType: WsMsgTypeEnum.message,
  410. data: messageData,
  411. });
  412. damuList.value.push(danmu);
  413. danmuStr.value = '';
  414. }
  415. function initReceive() {
  416. const instance = networkStore.wsMap.get(roomId.value);
  417. if (!instance?.socketIo) return;
  418. // websocket连接成功
  419. instance.socketIo.on(WsConnectStatusEnum.connect, () => {
  420. prettierReceiveWebsocket(WsConnectStatusEnum.connect);
  421. handleHeartbeat();
  422. if (!instance) return;
  423. instance.status = WsConnectStatusEnum.connect;
  424. instance.update();
  425. sendJoin();
  426. });
  427. // websocket连接断开
  428. instance.socketIo.on(WsConnectStatusEnum.disconnect, () => {
  429. prettierReceiveWebsocket(WsConnectStatusEnum.disconnect);
  430. if (!instance) return;
  431. instance.status = WsConnectStatusEnum.disconnect;
  432. instance.update();
  433. });
  434. // 用户加入房间
  435. instance.socketIo.on(
  436. WsMsgTypeEnum.joined,
  437. async (data: { data: ILive }) => {
  438. prettierReceiveWebsocket(WsMsgTypeEnum.joined, data);
  439. roomName.value = data.data.live_room?.name!;
  440. userName.value = data.data.user?.username!;
  441. userAvatar.value = data.data.user?.avatar!;
  442. track.audio = data.data.track_audio!;
  443. track.video = data.data.track_video!;
  444. coverImg.value = data.data.live_room?.cover_img!;
  445. flvurl.value = data.data.live_room?.flv_url!;
  446. hlsurl.value = data.data.live_room?.hls_url!;
  447. streamurl.value = data.data.live_room!.rtmp_url!.replace(
  448. 'rtmp',
  449. 'webrtc'
  450. );
  451. currentLiveRoom.value = data.data;
  452. if (route.query.liveType === liveTypeEnum.srsWebrtcPull) {
  453. instance.send({ msgType: WsMsgTypeEnum.getLiveUser });
  454. } else if (route.query.liveType === liveTypeEnum.srsFlvPull) {
  455. if (!autoplayVal.value) return;
  456. await startFlvPlay({
  457. flvurl: flvurl.value,
  458. videoEl: remoteVideoRef.value!,
  459. });
  460. videoToCanvas({
  461. videoEl: remoteVideoRef.value!,
  462. targetEl: canvasRef.value!,
  463. width: flvPlayer.value?.mediaInfo.width!,
  464. height: flvPlayer.value?.mediaInfo.height!,
  465. });
  466. } else if (route.query.liveType === liveTypeEnum.srsHlsPull) {
  467. if (!autoplayVal.value) return;
  468. await startHlsPlay({
  469. hlsurl: hlsurl.value,
  470. });
  471. videoToCanvas({
  472. videoEl: remoteVideoRef.value!,
  473. targetEl: canvasRef.value!,
  474. width: flvPlayer.value?.mediaInfo.width!,
  475. height: flvPlayer.value?.mediaInfo.height!,
  476. });
  477. videoLoading.value = false;
  478. } else if (
  479. data.data.live_room?.type === LiveRoomTypeEnum.user_obs ||
  480. data.data.live_room?.type === LiveRoomTypeEnum.system
  481. ) {
  482. if (!autoplayVal.value) return;
  483. if (judgeDevice().isIphone) {
  484. await startHlsPlay({
  485. hlsurl: flvurl.value,
  486. });
  487. videoToCanvas({
  488. videoEl: remoteVideoRef.value!,
  489. targetEl: canvasRef.value!,
  490. width: flvPlayer.value?.mediaInfo.width!,
  491. height: flvPlayer.value?.mediaInfo.height!,
  492. });
  493. videoLoading.value = false;
  494. } else {
  495. await startFlvPlay({
  496. flvurl: flvurl.value,
  497. videoEl: remoteVideoRef.value!,
  498. });
  499. videoToCanvas({
  500. videoEl: remoteVideoRef.value!,
  501. targetEl: canvasRef.value!,
  502. width: flvPlayer.value?.mediaInfo.width!,
  503. height: flvPlayer.value?.mediaInfo.height!,
  504. });
  505. }
  506. }
  507. }
  508. );
  509. // 收到offer
  510. instance.socketIo.on(WsMsgTypeEnum.offer, async (data: IOffer) => {
  511. prettierReceiveWebsocket(
  512. WsMsgTypeEnum.offer,
  513. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  514. data
  515. );
  516. if (isSRS) return;
  517. if (!instance) return;
  518. if (data.data.receiver === getSocketId()) {
  519. if (!data.is_anchor) {
  520. sidebarList.value.push({ socketId: data.data.sender });
  521. }
  522. await nextTick(async () => {
  523. console.log('收到offer,这个offer是发给我的');
  524. sender.value = data.data.sender;
  525. const rtc = await startNewWebRtc({
  526. receiver: data.data.sender,
  527. videoEl: data.is_anchor
  528. ? remoteVideoRef.value
  529. : localVideoRef.value[data.data.sender],
  530. });
  531. if (rtc) {
  532. await rtc.setRemoteDescription(data.data.sdp);
  533. const sdp = await rtc.createAnswer();
  534. await rtc.setLocalDescription(sdp);
  535. const answerData: IAnswer = {
  536. sdp,
  537. sender: getSocketId(),
  538. receiver: data.data.sender,
  539. live_room_id: data.data.live_room_id,
  540. };
  541. instance.send({
  542. msgType: WsMsgTypeEnum.answer,
  543. data: answerData,
  544. });
  545. }
  546. });
  547. } else {
  548. console.log('收到offer,但是这个offer不是发给我的');
  549. }
  550. });
  551. // 收到answer
  552. instance.socketIo.on(WsMsgTypeEnum.answer, async (data: IOffer) => {
  553. prettierReceiveWebsocket(
  554. WsMsgTypeEnum.answer,
  555. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  556. data
  557. );
  558. if (isSRS) return;
  559. if (!instance) return;
  560. const rtc = networkStore.getRtcMap(`${roomId.value}___${data.socket_id}`);
  561. if (!rtc) return;
  562. rtc.rtcStatus.answer = true;
  563. rtc.update();
  564. if (data.data.receiver === getSocketId()) {
  565. console.log('收到answer,这个answer是发给我的');
  566. await rtc.setRemoteDescription(data.data.sdp);
  567. } else {
  568. console.log('收到answer,但这个answer不是发给我的');
  569. }
  570. });
  571. // 收到candidate
  572. instance.socketIo.on(WsMsgTypeEnum.candidate, (data: ICandidate) => {
  573. prettierReceiveWebsocket(
  574. WsMsgTypeEnum.candidate,
  575. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  576. data
  577. );
  578. if (isSRS) return;
  579. if (!instance) return;
  580. const rtc = networkStore.getRtcMap(`${roomId.value}___${data.socket_id}`);
  581. if (!rtc) return;
  582. if (data.data.receiver === getSocketId()) {
  583. console.log('是发给我的candidate');
  584. const candidate = new RTCIceCandidate({
  585. sdpMid: data.data.sdpMid,
  586. sdpMLineIndex: data.data.sdpMLineIndex,
  587. candidate: data.data.candidate,
  588. });
  589. rtc.peerConnection
  590. ?.addIceCandidate(candidate)
  591. .then(() => {
  592. console.log('candidate成功');
  593. })
  594. .catch((err) => {
  595. console.error('candidate失败', err);
  596. });
  597. } else {
  598. console.log('不是发给我的candidate');
  599. }
  600. });
  601. // 管理员正在直播
  602. instance.socketIo.on(WsMsgTypeEnum.roomLiveing, (data) => {
  603. prettierReceiveWebsocket(WsMsgTypeEnum.roomLiveing, data);
  604. if (isSRS && !isFlv) {
  605. startNewWebRtc({});
  606. }
  607. });
  608. // 管理员不在直播
  609. instance.socketIo.on(WsMsgTypeEnum.roomNoLive, (data) => {
  610. prettierReceiveWebsocket(WsMsgTypeEnum.roomNoLive, data);
  611. roomNoLive.value = true;
  612. closeRtc();
  613. });
  614. // 当前所有在线用户
  615. instance.socketIo.on(WsMsgTypeEnum.liveUser, (data: ILiveUser[]) => {
  616. prettierReceiveWebsocket(WsMsgTypeEnum.liveUser, data);
  617. if (!instance) return;
  618. liveUserList.value = data;
  619. // batchSendOffer();
  620. });
  621. // 收到用户发送消息
  622. instance.socketIo.on(WsMsgTypeEnum.message, (data: IMessage) => {
  623. prettierReceiveWebsocket(WsMsgTypeEnum.message, data);
  624. if (!instance) return;
  625. const danmu: IDanmu = {
  626. msgType: DanmuMsgTypeEnum.danmu,
  627. socket_id: data.socket_id,
  628. userInfo: data.user_info,
  629. msg: data.data.msg,
  630. };
  631. damuList.value.push(danmu);
  632. });
  633. // 其他用户加入房间
  634. instance.socketIo.on(WsMsgTypeEnum.otherJoin, (data: IOtherJoin) => {
  635. prettierReceiveWebsocket(WsMsgTypeEnum.otherJoin, data);
  636. const danmu: IDanmu = {
  637. msgType: DanmuMsgTypeEnum.otherJoin,
  638. socket_id: data.data.join_socket_id,
  639. msg: '',
  640. };
  641. damuList.value.push(danmu);
  642. batchSendOffer(data.data.join_socket_id);
  643. });
  644. // 用户离开房间
  645. instance.socketIo.on(WsMsgTypeEnum.leave, (data) => {
  646. prettierReceiveWebsocket(WsMsgTypeEnum.leave, data);
  647. if (!instance) return;
  648. instance.send({
  649. msgType: WsMsgTypeEnum.leave,
  650. data: { roomId: instance.roomId },
  651. });
  652. });
  653. // 用户离开房间完成
  654. instance.socketIo.on(WsMsgTypeEnum.leaved, (data) => {
  655. prettierReceiveWebsocket(WsMsgTypeEnum.leaved, data);
  656. if (!instance) return;
  657. const res = liveUserList.value.filter(
  658. (item) => item.id !== data.socketId
  659. );
  660. liveUserList.value = res;
  661. const danmu: IDanmu = {
  662. msgType: DanmuMsgTypeEnum.userLeaved,
  663. socket_id: data.socketId,
  664. userInfo: data.data.userInfo,
  665. msg: '',
  666. };
  667. damuList.value.push(danmu);
  668. });
  669. }
  670. return {
  671. initPull,
  672. closeWs,
  673. closeRtc,
  674. getSocketId,
  675. keydownDanmu,
  676. sendDanmu,
  677. batchSendOffer,
  678. startGetUserMedia,
  679. startGetDisplayMedia,
  680. addTrack,
  681. addVideo,
  682. flvPlayer,
  683. videoLoading,
  684. balance,
  685. roomName,
  686. userName,
  687. userAvatar,
  688. currentLiveRoom,
  689. hlsurl,
  690. coverImg,
  691. roomNoLive,
  692. damuList,
  693. giftList,
  694. liveUserList,
  695. danmuStr,
  696. localStream,
  697. sender,
  698. sidebarList,
  699. };
  700. }