use-pull.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. import mpegts from 'mpegts.js';
  2. import { Ref, nextTick, onUnmounted, ref, watch } from 'vue';
  3. import { useRoute } from 'vue-router';
  4. import { useFlvPlay, useHlsPlay } from '@/hooks/use-play';
  5. import { useWs } from '@/hooks/use-ws';
  6. import { DanmuMsgTypeEnum, IDanmu, IMessage, liveTypeEnum } from '@/interface';
  7. import { WsMsgTypeEnum } from '@/network/webSocket';
  8. import { useAppStore } from '@/store/app';
  9. import { useNetworkStore } from '@/store/network';
  10. import { useUserStore } from '@/store/user';
  11. import { createVideo, videoToCanvas } from '@/utils';
  12. export function usePull({
  13. localVideoRef,
  14. canvasRef,
  15. isSRS,
  16. liveType,
  17. }: {
  18. localVideoRef: Ref<HTMLVideoElement[]>;
  19. canvasRef: Ref<Element | undefined>;
  20. isSRS: boolean;
  21. liveType: liveTypeEnum;
  22. }) {
  23. const route = useRoute();
  24. const userStore = useUserStore();
  25. const networkStore = useNetworkStore();
  26. const appStore = useAppStore();
  27. const roomId = ref(route.params.roomId as string);
  28. const roomLiveType = ref<liveTypeEnum>(liveType);
  29. const danmuStr = ref('');
  30. const autoplayVal = ref(false);
  31. const videoLoading = ref(false);
  32. const flvurl = ref('');
  33. const hlsurl = ref('');
  34. const sidebarList = ref<
  35. {
  36. socketId: string;
  37. }[]
  38. >([]);
  39. const videoElArr = ref<HTMLVideoElement[]>([]);
  40. const {
  41. getSocketId,
  42. initWs,
  43. roomLiveing,
  44. liveRoomInfo,
  45. roomNoLive,
  46. loopHeartbeatTimer,
  47. localStream,
  48. liveUserList,
  49. damuList,
  50. maxBitrate,
  51. maxFramerate,
  52. resolutionRatio,
  53. currentMaxFramerate,
  54. currentMaxBitrate,
  55. currentResolutionRatio,
  56. addTrack,
  57. delTrack,
  58. } = useWs();
  59. const { flvPlayer, flvVideoEl, startFlvPlay, destroyFlv } = useFlvPlay();
  60. const { hlsVideoEl, startHlsPlay, destroyHls } = useHlsPlay();
  61. const stopDrawingArr = ref<any[]>([]);
  62. onUnmounted(() => {
  63. clearInterval(loopHeartbeatTimer.value);
  64. handleStopDrawing();
  65. });
  66. function handleStopDrawing() {
  67. stopDrawingArr.value.forEach((cb) => cb());
  68. stopDrawingArr.value = [];
  69. }
  70. async function handleHlsPlay(url?: string) {
  71. console.log('handleHlsPlay');
  72. handleStopDrawing();
  73. videoLoading.value = true;
  74. const { width, height } = await startHlsPlay({
  75. hlsurl: url || hlsurl.value,
  76. });
  77. const { canvas, stopDrawing } = videoToCanvas({
  78. videoEl: hlsVideoEl.value!,
  79. size: { width, height },
  80. });
  81. stopDrawingArr.value.push(stopDrawing);
  82. canvasRef.value!.appendChild(canvas);
  83. videoLoading.value = false;
  84. }
  85. async function handleFlvPlay() {
  86. console.log('handleFlvPlay');
  87. handleStopDrawing();
  88. const { width, height } = await startFlvPlay({
  89. flvurl: flvurl.value,
  90. });
  91. const initCanvas = videoToCanvas({
  92. videoEl: flvVideoEl.value!,
  93. size: {
  94. width,
  95. height,
  96. },
  97. });
  98. stopDrawingArr.value.push(initCanvas.stopDrawing);
  99. canvasRef.value!.appendChild(initCanvas.canvas);
  100. flvPlayer.value!.on(mpegts.Events.MEDIA_INFO, () => {
  101. initCanvas.videoEl = flvVideoEl.value!;
  102. });
  103. videoLoading.value = false;
  104. }
  105. async function handlePlay() {
  106. if (roomLiveType.value === liveTypeEnum.srsFlvPull) {
  107. if (!autoplayVal.value) return;
  108. await handleFlvPlay();
  109. } else if (roomLiveType.value === liveTypeEnum.srsHlsPull) {
  110. if (!autoplayVal.value) return;
  111. await handleHlsPlay();
  112. }
  113. }
  114. watch(
  115. () => autoplayVal.value,
  116. (val) => {
  117. console.log('autoplayVal变了', val);
  118. if (val && roomLiveType.value === liveTypeEnum.webrtcPull) {
  119. handlePlay();
  120. }
  121. }
  122. );
  123. watch(
  124. () => roomLiveing.value,
  125. (val) => {
  126. if (val) {
  127. flvurl.value = val.live?.live_room?.flv_url!;
  128. hlsurl.value = val.live?.live_room?.hls_url!;
  129. // if (val && roomLiveType.value === liveTypeEnum.webrtcPull) {
  130. handlePlay();
  131. // }
  132. }
  133. }
  134. );
  135. watch(
  136. () => appStore.muted,
  137. (newVal) => {
  138. console.log('muted变了', newVal);
  139. videoElArr.value.forEach((el) => {
  140. el.muted = newVal;
  141. });
  142. }
  143. );
  144. watch(
  145. () => localStream,
  146. (stream) => {
  147. if (stream.value) {
  148. console.log('localStream变了');
  149. console.log('音频轨:', stream.value?.getAudioTracks());
  150. console.log('视频轨:', stream.value?.getVideoTracks());
  151. if (roomLiveType.value === liveTypeEnum.webrtcPull) {
  152. videoElArr.value.forEach((dom) => {
  153. dom.remove();
  154. });
  155. stream.value?.getVideoTracks().forEach((track) => {
  156. console.log('视频轨enabled:', track.id, track.enabled);
  157. const video = createVideo({});
  158. video.setAttribute('track-id', track.id);
  159. video.srcObject = new MediaStream([track]);
  160. canvasRef.value?.appendChild(video);
  161. videoElArr.value.push(video);
  162. });
  163. stream.value?.getAudioTracks().forEach((track) => {
  164. console.log('音频轨enabled:', track.id, track.enabled);
  165. const video = createVideo({});
  166. video.setAttribute('track-id', track.id);
  167. video.srcObject = new MediaStream([track]);
  168. canvasRef.value?.appendChild(video);
  169. videoElArr.value.push(video);
  170. });
  171. videoLoading.value = false;
  172. } else if (roomLiveType.value === liveTypeEnum.srsWebrtcPull) {
  173. videoElArr.value.forEach((dom) => {
  174. dom.remove();
  175. });
  176. stream.value?.getVideoTracks().forEach((track) => {
  177. console.log('视频轨enabled:', track.id, track.enabled);
  178. const video = createVideo({});
  179. video.setAttribute('track-id', track.id);
  180. video.srcObject = new MediaStream([track]);
  181. // document.body.appendChild(video);
  182. // console.log('kkkk', video);
  183. canvasRef.value?.appendChild(video);
  184. videoElArr.value.push(video);
  185. });
  186. stream.value?.getAudioTracks().forEach((track) => {
  187. console.log('音频轨enabled:', track.id, track.enabled);
  188. const video = createVideo({});
  189. video.setAttribute('track-id', track.id);
  190. video.srcObject = new MediaStream([track]);
  191. canvasRef.value?.appendChild(video);
  192. videoElArr.value.push(video);
  193. });
  194. videoLoading.value = false;
  195. }
  196. } else {
  197. videoElArr.value?.forEach((item) => {
  198. item.remove();
  199. });
  200. }
  201. },
  202. { deep: true }
  203. );
  204. watch(
  205. [
  206. () => userStore.userInfo,
  207. () => networkStore.wsMap.get(roomId.value)?.socketIo?.connected,
  208. ],
  209. ([userInfo, connected]) => {
  210. if (userInfo && connected) {
  211. const instance = networkStore.wsMap.get(roomId.value);
  212. if (!instance) return;
  213. }
  214. }
  215. );
  216. function initPull(autolay = true) {
  217. autoplayVal.value = autolay;
  218. if (autoplayVal.value) {
  219. videoLoading.value = true;
  220. }
  221. initWs({
  222. roomId: roomId.value,
  223. isSRS,
  224. isAnchor: false,
  225. isPull: true,
  226. roomLiveType: roomLiveType.value,
  227. });
  228. }
  229. function closeWs() {
  230. const instance = networkStore.wsMap.get(roomId.value);
  231. instance?.close();
  232. }
  233. function closeRtc() {
  234. networkStore.rtcMap.forEach((rtc) => {
  235. rtc.close();
  236. });
  237. }
  238. function addVideo() {
  239. sidebarList.value.push({ socketId: getSocketId() });
  240. nextTick(() => {
  241. liveUserList.value.forEach((item) => {
  242. const socketId = item.id;
  243. if (socketId === getSocketId()) {
  244. localVideoRef.value[getSocketId()].srcObject = localStream.value;
  245. }
  246. });
  247. });
  248. }
  249. function keydownDanmu(event: KeyboardEvent) {
  250. const key = event.key.toLowerCase();
  251. if (key === 'enter') {
  252. event.preventDefault();
  253. sendDanmu();
  254. }
  255. }
  256. function sendDanmu() {
  257. if (!danmuStr.value.trim().length) {
  258. window.$message.warning('请输入弹幕内容!');
  259. return;
  260. }
  261. const instance = networkStore.wsMap.get(roomId.value);
  262. if (!instance) return;
  263. const danmu: IDanmu = {
  264. socket_id: getSocketId(),
  265. userInfo: userStore.userInfo,
  266. msgType: DanmuMsgTypeEnum.danmu,
  267. msg: danmuStr.value,
  268. };
  269. const messageData: IMessage['data'] = {
  270. msg: danmuStr.value,
  271. msgType: DanmuMsgTypeEnum.danmu,
  272. live_room_id: Number(roomId.value),
  273. };
  274. instance.send({
  275. msgType: WsMsgTypeEnum.message,
  276. data: messageData,
  277. });
  278. damuList.value.push(danmu);
  279. danmuStr.value = '';
  280. }
  281. return {
  282. initPull,
  283. closeWs,
  284. closeRtc,
  285. getSocketId,
  286. keydownDanmu,
  287. sendDanmu,
  288. addVideo,
  289. handleHlsPlay,
  290. handleFlvPlay,
  291. roomLiveType,
  292. roomLiveing,
  293. autoplayVal,
  294. videoLoading,
  295. roomNoLive,
  296. damuList,
  297. liveUserList,
  298. sidebarList,
  299. danmuStr,
  300. liveRoomInfo,
  301. };
  302. }