use-ws.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. import { getRandomString } from 'billd-utils';
  2. import { reactive, ref, watch } from 'vue';
  3. import { fetchRtcV1Play, fetchRtcV1Publish } from '@/api/srs';
  4. import { WEBSOCKET_URL } from '@/constant';
  5. import {
  6. DanmuMsgTypeEnum,
  7. IAnswer,
  8. ICandidate,
  9. IDanmu,
  10. IHeartbeat,
  11. IJoin,
  12. ILive,
  13. ILiveUser,
  14. IMessage,
  15. IOffer,
  16. IOtherJoin,
  17. IUpdateJoinInfo,
  18. LiveRoomTypeEnum,
  19. liveTypeEnum,
  20. } from '@/interface';
  21. import { WebRTCClass } from '@/network/webRTC';
  22. import {
  23. WebSocketClass,
  24. WsConnectStatusEnum,
  25. WsMsgTypeEnum,
  26. prettierReceiveWebsocket,
  27. } from '@/network/webSocket';
  28. import { AppRootState, useAppStore } from '@/store/app';
  29. import { useNetworkStore } from '@/store/network';
  30. import { useUserStore } from '@/store/user';
  31. export const useWs = () => {
  32. const appStore = useAppStore();
  33. const userStore = useUserStore();
  34. const networkStore = useNetworkStore();
  35. const heartbeatTimer = ref();
  36. const liveUserList = ref<ILiveUser[]>([]);
  37. const roomId = ref('');
  38. const roomName = ref('');
  39. const roomNoLive = ref(false);
  40. const roomLiveing = ref<IJoin['data']>();
  41. const liveRoomInfo = ref<ILive>();
  42. const isAnchor = ref(false);
  43. const roomLiveType = ref(liveTypeEnum.srsFlvPull);
  44. const joined = ref(false);
  45. const isSRS = ref(false);
  46. const isPull = ref(false);
  47. const trackInfo = reactive({ track_audio: 1, track_video: 1 });
  48. const localVideo = ref<HTMLVideoElement>(document.createElement('video'));
  49. const localStream = ref<MediaStream>();
  50. const lastCoverImg = ref('');
  51. const maxBitrate = ref([
  52. {
  53. label: '1',
  54. value: 1,
  55. },
  56. {
  57. label: '10',
  58. value: 10,
  59. },
  60. {
  61. label: '50',
  62. value: 50,
  63. },
  64. {
  65. label: '1000',
  66. value: 1000,
  67. },
  68. {
  69. label: '2000',
  70. value: 2000,
  71. },
  72. {
  73. label: '3000',
  74. value: 3000,
  75. },
  76. {
  77. label: '4000',
  78. value: 4000,
  79. },
  80. {
  81. label: '5000',
  82. value: 5000,
  83. },
  84. {
  85. label: '6000',
  86. value: 6000,
  87. },
  88. {
  89. label: '7000',
  90. value: 7000,
  91. },
  92. {
  93. label: '8000',
  94. value: 8000,
  95. },
  96. {
  97. label: '9000',
  98. value: 9000,
  99. },
  100. {
  101. label: '10000',
  102. value: 10000,
  103. },
  104. ]);
  105. const maxFramerate = ref([
  106. {
  107. label: '1帧',
  108. value: 1,
  109. },
  110. {
  111. label: '10帧',
  112. value: 10,
  113. },
  114. {
  115. label: '20帧',
  116. value: 20,
  117. },
  118. {
  119. label: '24帧',
  120. value: 24,
  121. },
  122. {
  123. label: '30帧',
  124. value: 30,
  125. },
  126. {
  127. label: '60帧',
  128. value: 60,
  129. },
  130. ]);
  131. const resolutionRatio = ref([
  132. {
  133. label: '160P',
  134. value: 160,
  135. },
  136. {
  137. label: '240P',
  138. value: 240,
  139. },
  140. {
  141. label: '360P',
  142. value: 360,
  143. },
  144. {
  145. label: '720P',
  146. value: 720,
  147. },
  148. {
  149. label: '1080P',
  150. value: 1080,
  151. },
  152. {
  153. label: '1440P',
  154. value: 1440,
  155. },
  156. {
  157. label: '1920P',
  158. value: 1920,
  159. },
  160. {
  161. label: '2400P',
  162. value: 2400,
  163. },
  164. ]);
  165. const currentMaxBitrate = ref(maxBitrate.value[3].value);
  166. const currentResolutionRatio = ref(resolutionRatio.value[3].value);
  167. const currentMaxFramerate = ref(maxFramerate.value[2].value);
  168. const damuList = ref<IDanmu[]>([]);
  169. watch(
  170. () => appStore.allTrack,
  171. (newTrack, oldTrack) => {
  172. console.log('appStore.allTrack变了');
  173. const mixedStream = new MediaStream();
  174. newTrack.forEach((item) => {
  175. mixedStream.addTrack(item.track);
  176. });
  177. console.log('新的allTrack音频轨', mixedStream.getAudioTracks());
  178. console.log('新的allTrack视频轨', mixedStream.getVideoTracks());
  179. console.log('旧的allTrack音频轨', localStream.value?.getAudioTracks());
  180. console.log('旧的allTrack视频轨', localStream.value?.getVideoTracks());
  181. localStream.value = mixedStream;
  182. if (isSRS.value) {
  183. if (!isPull.value) {
  184. networkStore.rtcMap.forEach((rtc) => {
  185. rtc.close();
  186. });
  187. startNewWebRtc({
  188. receiver: 'srs',
  189. videoEl: localVideo.value,
  190. });
  191. }
  192. }
  193. },
  194. { deep: true }
  195. );
  196. watch(
  197. () => currentResolutionRatio.value,
  198. (newVal) => {
  199. appStore.allTrack.forEach((info) => {
  200. info.track.applyConstraints({
  201. frameRate: { max: currentMaxFramerate.value },
  202. height: newVal,
  203. });
  204. });
  205. networkStore.rtcMap.forEach(async (rtc) => {
  206. const res = await rtc.setResolutionRatio(newVal);
  207. if (res === 1) {
  208. window.$message.success('切换分辨率成功!');
  209. } else {
  210. window.$message.success('切换分辨率失败!');
  211. }
  212. });
  213. }
  214. );
  215. watch(
  216. () => currentMaxFramerate.value,
  217. (newVal) => {
  218. console.log(currentMaxFramerate.value, 'currentMaxFramerate.value');
  219. appStore.allTrack.forEach((info) => {
  220. info.track.applyConstraints({
  221. frameRate: { max: newVal },
  222. height: currentResolutionRatio.value,
  223. });
  224. });
  225. networkStore.rtcMap.forEach(async (rtc) => {
  226. const res = await rtc.setMaxFramerate(newVal);
  227. if (res === 1) {
  228. window.$message.success('切换帧率成功!');
  229. } else {
  230. window.$message.success('切换帧率失败!');
  231. }
  232. });
  233. }
  234. );
  235. watch(
  236. () => currentMaxBitrate.value,
  237. (newVal) => {
  238. networkStore.rtcMap.forEach(async (rtc) => {
  239. const res = await rtc.setMaxBitrate(newVal);
  240. if (res === 1) {
  241. window.$message.success('切换码率成功!');
  242. } else {
  243. window.$message.success('切换码率失败!');
  244. }
  245. });
  246. }
  247. );
  248. function addTrack(addTrackInfo: AppRootState['allTrack'][0]) {
  249. if (isAnchor.value) {
  250. networkStore.rtcMap.forEach((rtc) => {
  251. const sender = rtc.peerConnection
  252. ?.getSenders()
  253. .find((sender) => sender.track?.id === addTrackInfo.track.id);
  254. if (!sender) {
  255. console.log('ffff', sender);
  256. rtc.peerConnection?.addTrack(addTrackInfo.track, addTrackInfo.stream);
  257. }
  258. });
  259. }
  260. const mixedStream = new MediaStream();
  261. appStore.allTrack.forEach((item) => {
  262. mixedStream.addTrack(item.track);
  263. });
  264. console.log('addTrack后结果的音频轨', mixedStream.getAudioTracks());
  265. console.log('addTrack后结果的视频轨', mixedStream.getVideoTracks());
  266. localStream.value = mixedStream;
  267. // srs不需要更新,因为更新了之后,跟着就关闭当前rtc然后重新new一个新的rtc了
  268. if (!isSRS.value) {
  269. let resUrl = '';
  270. const rtmpUrl = userStore.userInfo?.live_rooms?.[0].rtmp_url!;
  271. if (rtmpUrl.indexOf('type=') === -1) {
  272. resUrl += `${rtmpUrl}&type=${
  273. isSRS.value ? LiveRoomTypeEnum.user_srs : LiveRoomTypeEnum.user_wertc
  274. }`;
  275. } else {
  276. resUrl = rtmpUrl.replace(
  277. /type=([0-9]+)/,
  278. `type=${
  279. isSRS.value
  280. ? LiveRoomTypeEnum.user_srs
  281. : LiveRoomTypeEnum.user_wertc
  282. }`
  283. );
  284. }
  285. const data: IUpdateJoinInfo['data'] = {
  286. live_room_id: Number(roomId.value),
  287. track: {
  288. audio: appStore.getTrackInfo().audio > 0 ? 1 : 2,
  289. video: appStore.getTrackInfo().video > 0 ? 1 : 2,
  290. },
  291. rtmp_url: resUrl,
  292. };
  293. networkStore.wsMap.get(roomId.value)?.send({
  294. msgType: WsMsgTypeEnum.updateJoinInfo,
  295. data,
  296. });
  297. }
  298. }
  299. function delTrack(delTrackInfo: AppRootState['allTrack'][0]) {
  300. if (isAnchor.value) {
  301. networkStore.rtcMap.forEach((rtc) => {
  302. const sender = rtc.peerConnection
  303. ?.getSenders()
  304. .find((sender) => sender.track?.id === delTrackInfo.track.id);
  305. if (sender) {
  306. console.log('删除track', delTrackInfo, sender);
  307. rtc.peerConnection?.removeTrack(sender);
  308. }
  309. });
  310. }
  311. const mixedStream = new MediaStream();
  312. appStore.allTrack.forEach((item) => {
  313. console.log('xxxx', item.track);
  314. mixedStream.addTrack(item.track);
  315. });
  316. console.log('delTrack后结果的音频轨', mixedStream.getAudioTracks());
  317. console.log('delTrack后结果的视频轨', mixedStream.getVideoTracks());
  318. localStream.value = mixedStream;
  319. if (!isSRS.value) {
  320. let resUrl = '';
  321. const rtmpUrl = userStore.userInfo?.live_rooms?.[0].rtmp_url!;
  322. if (rtmpUrl.indexOf('type=') === -1) {
  323. resUrl += `${rtmpUrl}&type=${
  324. isSRS.value ? LiveRoomTypeEnum.user_srs : LiveRoomTypeEnum.user_wertc
  325. }`;
  326. } else {
  327. resUrl = rtmpUrl.replace(
  328. /type=([0-9]+)/,
  329. `type=${
  330. isSRS.value
  331. ? LiveRoomTypeEnum.user_srs
  332. : LiveRoomTypeEnum.user_wertc
  333. }`
  334. );
  335. }
  336. const data: IUpdateJoinInfo['data'] = {
  337. live_room_id: Number(roomId.value),
  338. track: {
  339. audio: appStore.getTrackInfo().audio > 0 ? 1 : 2,
  340. video: appStore.getTrackInfo().video > 0 ? 1 : 2,
  341. },
  342. rtmp_url: resUrl,
  343. };
  344. networkStore.wsMap.get(roomId.value)?.send({
  345. msgType: WsMsgTypeEnum.updateJoinInfo,
  346. data,
  347. });
  348. }
  349. }
  350. function getSocketId() {
  351. return networkStore.wsMap.get(roomId.value)?.socketIo?.id || '-1';
  352. }
  353. function handleHeartbeat(liveId: number) {
  354. heartbeatTimer.value = setInterval(() => {
  355. const instance = networkStore.wsMap.get(roomId.value);
  356. if (!instance) return;
  357. const heartbeatData: IHeartbeat['data'] = {
  358. live_id: liveId,
  359. live_room_id: Number(roomId.value),
  360. };
  361. instance.send({
  362. msgType: WsMsgTypeEnum.heartbeat,
  363. data: heartbeatData,
  364. });
  365. }, 1000 * 5);
  366. }
  367. async function sendOffer({
  368. sender,
  369. receiver,
  370. }: {
  371. sender: string;
  372. receiver: string;
  373. }) {
  374. console.log('开始sendOffer');
  375. const ws = networkStore.wsMap.get(roomId.value);
  376. if (!ws) return;
  377. const rtc = networkStore.getRtcMap(`${roomId.value}___${receiver}`);
  378. if (!rtc) return;
  379. if (!isSRS.value) {
  380. const sdp = await rtc.createOffer();
  381. await rtc.setLocalDescription(sdp!);
  382. ws.send({
  383. msgType: WsMsgTypeEnum.offer,
  384. data: {
  385. sdp,
  386. sender,
  387. receiver,
  388. live_room_id: roomId.value,
  389. },
  390. });
  391. } else {
  392. const sdp = await rtc.createOffer();
  393. await rtc.setLocalDescription(sdp!);
  394. let res;
  395. if (isPull.value) {
  396. console.log(
  397. roomLiveing.value,
  398. 2222222222,
  399. roomLiveing.value!.live!.live_room!.rtmp_url!.replace(
  400. 'rtmp',
  401. 'webrtc'
  402. )
  403. );
  404. res = await fetchRtcV1Play({
  405. api: `/rtc/v1/play/`,
  406. clientip: null,
  407. sdp: sdp!.sdp!,
  408. streamurl: roomLiveing.value!.live!.live_room!.rtmp_url!.replace(
  409. 'rtmp',
  410. 'webrtc'
  411. ),
  412. tid: getRandomString(10),
  413. });
  414. } else {
  415. res = await fetchRtcV1Publish({
  416. api: `/rtc/v1/publish/`,
  417. clientip: null,
  418. sdp: sdp!.sdp!,
  419. streamurl: userStore.userInfo!.live_rooms![0]!.rtmp_url!.replace(
  420. 'rtmp',
  421. 'webrtc'
  422. ),
  423. tid: getRandomString(10),
  424. });
  425. const data: IUpdateJoinInfo['data'] = {
  426. live_room_id: Number(roomId.value),
  427. track: {
  428. audio: appStore.getTrackInfo().audio > 0 ? 1 : 2,
  429. video: appStore.getTrackInfo().video > 0 ? 1 : 2,
  430. },
  431. };
  432. networkStore.wsMap.get(roomId.value)?.send({
  433. msgType: WsMsgTypeEnum.updateJoinInfo,
  434. data,
  435. });
  436. }
  437. if (res.data.code !== 0) {
  438. console.error('/rtc/v1/publish/拿不到sdp');
  439. return;
  440. }
  441. await rtc.setRemoteDescription(
  442. new RTCSessionDescription({ type: 'answer', sdp: res.data.sdp })
  443. );
  444. }
  445. }
  446. function sendJoin() {
  447. const instance = networkStore.wsMap.get(roomId.value);
  448. if (!instance) return;
  449. let resUrl = '';
  450. const rtmpUrl = userStore.userInfo?.live_rooms?.[0].rtmp_url;
  451. // 如果是用户看直播,发送join时不需要rtmpUrl;只有房主直播的时候需要带rtmpUrl
  452. if (rtmpUrl) {
  453. if (rtmpUrl.indexOf('type=') === -1) {
  454. resUrl += `${rtmpUrl}&type=${
  455. isSRS.value ? LiveRoomTypeEnum.user_srs : LiveRoomTypeEnum.user_wertc
  456. }`;
  457. } else {
  458. resUrl = rtmpUrl.replace(
  459. /type=([0-9]+)/,
  460. `type=${
  461. isSRS.value
  462. ? LiveRoomTypeEnum.user_srs
  463. : LiveRoomTypeEnum.user_wertc
  464. }`
  465. );
  466. }
  467. }
  468. const joinData: IJoin['data'] = {
  469. live_room: {
  470. id: Number(roomId.value),
  471. name: roomName.value,
  472. cover_img: lastCoverImg.value,
  473. type: isSRS.value
  474. ? LiveRoomTypeEnum.user_srs
  475. : LiveRoomTypeEnum.user_wertc,
  476. rtmp_url: resUrl,
  477. },
  478. live: {
  479. track_audio: appStore.getTrackInfo().audio > 0 ? 1 : 2,
  480. track_video: appStore.getTrackInfo().video > 0 ? 1 : 2,
  481. },
  482. };
  483. instance.send({
  484. msgType: WsMsgTypeEnum.join,
  485. data: joinData,
  486. });
  487. }
  488. function handleNegotiationneeded(data: { roomId: string; isSRS: boolean }) {
  489. console.warn(`${data.roomId},开始监听pc的negotiationneeded`);
  490. const rtc = networkStore.getRtcMap(data.roomId);
  491. if (!rtc) return;
  492. console.warn(`监听pc的negotiationneeded`);
  493. rtc.peerConnection?.addEventListener('negotiationneeded', (event) => {
  494. console.warn(`${data.roomId},pc收到negotiationneeded`, event);
  495. sendOffer({
  496. sender: getSocketId(),
  497. receiver: rtc.receiver,
  498. });
  499. });
  500. }
  501. /** 原生的webrtc时,receiver必传 */
  502. function startNewWebRtc({
  503. receiver,
  504. videoEl,
  505. }: {
  506. receiver: string;
  507. videoEl: HTMLVideoElement;
  508. }) {
  509. let rtc: WebRTCClass;
  510. if (isSRS.value) {
  511. console.warn('SRS开始new WebRTCClass', `${roomId.value}___${receiver!}`);
  512. rtc = new WebRTCClass({
  513. maxBitrate: isPull.value ? -1 : currentMaxBitrate.value,
  514. maxFramerate: isPull.value ? -1 : currentMaxFramerate.value,
  515. resolutionRatio: isPull.value ? -1 : currentResolutionRatio.value,
  516. roomId: `${roomId.value}___${receiver!}`,
  517. videoEl,
  518. isSRS: true,
  519. receiver,
  520. });
  521. if (isPull.value) {
  522. if (trackInfo.track_video === 1) {
  523. rtc.peerConnection?.addTransceiver('video', {
  524. direction: 'recvonly',
  525. });
  526. }
  527. if (trackInfo.track_audio === 1) {
  528. rtc.peerConnection?.addTransceiver('audio', {
  529. direction: 'recvonly',
  530. });
  531. }
  532. }
  533. // handleNegotiationneeded({
  534. // roomId: `${roomId.value}___${receiver}`,
  535. // isSRS: true,
  536. // });
  537. rtc.localStream = localStream.value;
  538. localStream.value?.getTracks().forEach((track) => {
  539. console.warn(
  540. 'srs startNewWebRtc,pc插入track',
  541. track.id,
  542. localStream.value?.id
  543. );
  544. rtc.peerConnection?.addTrack(track, localStream.value!);
  545. });
  546. sendOffer({
  547. sender: getSocketId(),
  548. receiver,
  549. });
  550. } else {
  551. console.warn('开始new WebRTCClass', `${roomId.value}___${receiver!}`);
  552. rtc = new WebRTCClass({
  553. maxBitrate: isPull.value ? -1 : currentMaxBitrate.value,
  554. maxFramerate: isPull.value ? -1 : currentMaxFramerate.value,
  555. resolutionRatio: isPull.value ? -1 : currentResolutionRatio.value,
  556. roomId: `${roomId.value}___${receiver!}`,
  557. videoEl,
  558. isSRS: false,
  559. receiver,
  560. });
  561. if (isAnchor.value) {
  562. handleNegotiationneeded({
  563. roomId: `${roomId.value}___${receiver}`,
  564. isSRS: false,
  565. });
  566. rtc.localStream = localStream.value;
  567. localStream.value?.getTracks().forEach((track) => {
  568. // rtc.peerConnection?.addTransceiver(track, {
  569. // streams: [localStream.value!],
  570. // direction: 'sendonly',
  571. // });
  572. rtc.peerConnection?.addTrack(track, localStream.value!);
  573. });
  574. }
  575. }
  576. return rtc;
  577. }
  578. function initReceive() {
  579. const ws = networkStore.wsMap.get(roomId.value);
  580. if (!ws?.socketIo) return;
  581. // websocket连接成功
  582. ws.socketIo.on(WsConnectStatusEnum.connect, () => {
  583. prettierReceiveWebsocket(WsConnectStatusEnum.connect);
  584. if (!ws) return;
  585. ws.status = WsConnectStatusEnum.connect;
  586. ws.update();
  587. sendJoin();
  588. });
  589. // websocket连接断开
  590. ws.socketIo.on(WsConnectStatusEnum.disconnect, () => {
  591. prettierReceiveWebsocket(WsConnectStatusEnum.disconnect, ws);
  592. if (!ws) return;
  593. ws.status = WsConnectStatusEnum.disconnect;
  594. ws.update();
  595. });
  596. // 收到offer
  597. ws.socketIo.on(WsMsgTypeEnum.offer, async (data: IOffer) => {
  598. prettierReceiveWebsocket(
  599. WsMsgTypeEnum.offer,
  600. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  601. data
  602. );
  603. if (isSRS.value) return;
  604. if (!ws) return;
  605. if (data.data.receiver === getSocketId()) {
  606. console.log('收到offer,这个offer是发给我的');
  607. if (!isAnchor.value) {
  608. // 如果是用户进来看直播
  609. let rtc = networkStore.getRtcMap(
  610. `${roomId.value}___${data.data.sender}`
  611. );
  612. if (!rtc) {
  613. rtc = await startNewWebRtc({
  614. receiver: data.data.sender,
  615. videoEl: localVideo.value,
  616. });
  617. }
  618. await rtc.setRemoteDescription(data.data.sdp);
  619. const sdp = await rtc.createAnswer();
  620. await rtc.setLocalDescription(sdp!);
  621. const answerData: IAnswer = {
  622. sdp,
  623. sender: getSocketId(),
  624. receiver: data.data.sender,
  625. live_room_id: data.data.live_room_id,
  626. };
  627. ws.send({
  628. msgType: WsMsgTypeEnum.answer,
  629. data: answerData,
  630. });
  631. }
  632. } else {
  633. console.log('收到offer,但是这个offer不是发给我的');
  634. }
  635. });
  636. // 收到answer
  637. ws.socketIo.on(WsMsgTypeEnum.answer, async (data: IOffer) => {
  638. prettierReceiveWebsocket(
  639. WsMsgTypeEnum.answer,
  640. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  641. data
  642. );
  643. if (isSRS.value) return;
  644. if (!ws) return;
  645. const rtc = networkStore.getRtcMap(`${roomId.value}___${data.socket_id}`);
  646. if (!rtc) return;
  647. rtc.update();
  648. if (data.data.receiver === getSocketId()) {
  649. console.log('收到answer,这个answer是发给我的');
  650. await rtc.setRemoteDescription(data.data.sdp);
  651. } else {
  652. console.log('收到answer,但这个answer不是发给我的');
  653. }
  654. });
  655. // 收到candidate
  656. ws.socketIo.on(WsMsgTypeEnum.candidate, (data: ICandidate) => {
  657. prettierReceiveWebsocket(
  658. WsMsgTypeEnum.candidate,
  659. `发送者:${data.data.sender},接收者:${data.data.receiver}`,
  660. data
  661. );
  662. if (isSRS.value) return;
  663. if (!ws) return;
  664. const rtc = networkStore.getRtcMap(`${roomId.value}___${data.socket_id}`);
  665. if (!rtc) return;
  666. if (data.socket_id !== getSocketId()) {
  667. console.log('不是我发的candidate');
  668. const candidate = new RTCIceCandidate({
  669. sdpMid: data.data.sdpMid,
  670. sdpMLineIndex: data.data.sdpMLineIndex,
  671. candidate: data.data.candidate,
  672. });
  673. rtc.peerConnection
  674. ?.addIceCandidate(candidate)
  675. .then(() => {
  676. console.log('candidate成功');
  677. })
  678. .catch((err) => {
  679. console.error('candidate失败', err);
  680. });
  681. } else {
  682. console.log('是我发的candidate');
  683. }
  684. });
  685. // 管理员正在直播
  686. ws.socketIo.on(WsMsgTypeEnum.roomLiveing, (data: IJoin) => {
  687. prettierReceiveWebsocket(WsMsgTypeEnum.roomLiveing, data);
  688. roomLiveing.value = data.data;
  689. console.log(isSRS.value, isPull.value, data, 111);
  690. // 如果是srs开播,则不需要等有人进来了才new webrtc,只要Websocket连上了就开始new webrtc
  691. if (isSRS.value) {
  692. if (isPull.value) {
  693. console.log('llllll');
  694. if (roomLiveType.value === liveTypeEnum.srsWebrtcPull) {
  695. startNewWebRtc({
  696. receiver: 'srs',
  697. videoEl: localVideo.value,
  698. });
  699. }
  700. }
  701. }
  702. });
  703. // 管理员不在直播
  704. ws.socketIo.on(WsMsgTypeEnum.roomNoLive, (data) => {
  705. prettierReceiveWebsocket(WsMsgTypeEnum.roomNoLive, data);
  706. roomNoLive.value = true;
  707. });
  708. // 当前所有在线用户
  709. ws.socketIo.on(WsMsgTypeEnum.liveUser, (data) => {
  710. prettierReceiveWebsocket(WsMsgTypeEnum.liveUser, data);
  711. });
  712. // 收到用户发送消息
  713. ws.socketIo.on(WsMsgTypeEnum.message, (data: IMessage) => {
  714. prettierReceiveWebsocket(WsMsgTypeEnum.message, data);
  715. if (!ws) return;
  716. damuList.value.push({
  717. socket_id: data.socket_id,
  718. msgType: DanmuMsgTypeEnum.danmu,
  719. msg: data.data.msg,
  720. userInfo: data.user_info,
  721. });
  722. });
  723. // 用户加入房间完成
  724. ws.socketIo.on(WsMsgTypeEnum.joined, (data: IJoin) => {
  725. prettierReceiveWebsocket(WsMsgTypeEnum.joined, data);
  726. handleHeartbeat(data.data.live?.id || -1);
  727. joined.value = true;
  728. trackInfo.track_audio = data.data.live?.track_audio!;
  729. trackInfo.track_video = data.data.live?.track_video!;
  730. liveUserList.value.push({
  731. id: `${getSocketId()}`,
  732. userInfo: data.user_info,
  733. });
  734. if (!isAnchor.value) {
  735. liveRoomInfo.value = data.data;
  736. }
  737. // 如果是srs开播,则不需要等有人进来了才new webrtc,只要Websocket连上了就开始new webrtc
  738. if (isSRS.value) {
  739. if (!isPull.value) {
  740. startNewWebRtc({
  741. receiver: 'srs',
  742. videoEl: localVideo.value,
  743. });
  744. }
  745. }
  746. });
  747. // 其他用户加入房间
  748. ws.socketIo.on(WsMsgTypeEnum.otherJoin, (data: IOtherJoin) => {
  749. prettierReceiveWebsocket(WsMsgTypeEnum.otherJoin, data);
  750. liveUserList.value.push({
  751. id: data.data.join_socket_id,
  752. userInfo: data.data.liveRoom.user,
  753. });
  754. const danmu: IDanmu = {
  755. msgType: DanmuMsgTypeEnum.otherJoin,
  756. socket_id: data.data.join_socket_id,
  757. userInfo: data.data.liveRoom.user,
  758. msg: '',
  759. };
  760. damuList.value.push(danmu);
  761. // 如果是srs开播,且进来的用户不是srs-webrtc-pull,则不能再new webrtc了
  762. if (isSRS.value) return;
  763. if (joined.value) {
  764. startNewWebRtc({
  765. receiver: data.data.join_socket_id,
  766. videoEl: localVideo.value,
  767. });
  768. }
  769. });
  770. // 用户离开房间
  771. ws.socketIo.on(WsMsgTypeEnum.leave, (data) => {
  772. prettierReceiveWebsocket(WsMsgTypeEnum.leave, data);
  773. if (!ws) return;
  774. ws.send({
  775. msgType: WsMsgTypeEnum.leave,
  776. data: { roomId: ws.roomId },
  777. });
  778. });
  779. // 用户离开房间完成
  780. ws.socketIo.on(WsMsgTypeEnum.leaved, (data) => {
  781. prettierReceiveWebsocket(WsMsgTypeEnum.leaved, data);
  782. networkStore.rtcMap
  783. .get(`${roomId.value}___${data.socketId as string}`)
  784. ?.close();
  785. networkStore.removeRtc(`${roomId.value}___${data.socketId as string}`);
  786. const res = liveUserList.value.filter(
  787. (item) => item.id !== data.socketId
  788. );
  789. liveUserList.value = res;
  790. damuList.value.push({
  791. socket_id: data.socketId,
  792. msgType: DanmuMsgTypeEnum.userLeaved,
  793. msg: '',
  794. });
  795. });
  796. }
  797. function initWs(data: {
  798. isAnchor: boolean;
  799. roomId: string;
  800. isSRS: boolean;
  801. isPull: boolean;
  802. currentResolutionRatio?: number;
  803. currentMaxFramerate?: number;
  804. currentMaxBitrate?: number;
  805. roomLiveType: liveTypeEnum;
  806. }) {
  807. roomId.value = data.roomId;
  808. isAnchor.value = data.isAnchor;
  809. roomLiveType.value = data.roomLiveType;
  810. if (data.currentMaxBitrate) {
  811. currentMaxBitrate.value = data.currentMaxBitrate;
  812. }
  813. if (data.currentMaxFramerate) {
  814. currentMaxFramerate.value = data.currentMaxFramerate;
  815. }
  816. if (data.currentResolutionRatio) {
  817. currentResolutionRatio.value = data.currentResolutionRatio;
  818. }
  819. isSRS.value = data.isSRS;
  820. isPull.value = data.isPull;
  821. new WebSocketClass({
  822. roomId: roomId.value,
  823. url: WEBSOCKET_URL,
  824. isAnchor: data.isAnchor,
  825. });
  826. initReceive();
  827. }
  828. return {
  829. getSocketId,
  830. initWs,
  831. addTrack,
  832. delTrack,
  833. lastCoverImg,
  834. roomLiveing,
  835. liveRoomInfo,
  836. roomNoLive,
  837. heartbeatTimer,
  838. localStream,
  839. liveUserList,
  840. damuList,
  841. maxBitrate,
  842. maxFramerate,
  843. resolutionRatio,
  844. currentMaxFramerate,
  845. currentMaxBitrate,
  846. currentResolutionRatio,
  847. };
  848. };