shuisheng 2 jaren geleden
bovenliggende
commit
d6880d2cf6
5 gewijzigde bestanden met toevoegingen van 214 en 109 verwijderingen
  1. 26 45
      src/hooks/use-push.ts
  2. 1 0
      src/hooks/use-rtcParams.ts
  3. 11 2
      src/hooks/use-srs-ws.ts
  4. 105 47
      src/network/webRTC.ts
  5. 71 15
      src/views/push/index.vue

+ 26 - 45
src/hooks/use-push.ts

@@ -8,6 +8,7 @@ import {
 } from '@/api/userLiveRoom';
 import { DanmuMsgTypeEnum, ILiveRoom, IMessage } from '@/interface';
 import { WsMsgTypeEnum, WsMsrBlobType } from '@/interface-ws';
+import { handleMaxFramerate } from '@/network/webRTC';
 import { useAppStore } from '@/store/app';
 import { useNetworkStore } from '@/store/network';
 import { useUserStore } from '@/store/user';
@@ -69,22 +70,6 @@ export function usePush() {
   watch(
     () => currentResolutionRatio.value,
     (newVal) => {
-      if (canvasVideoStream.value) {
-        canvasVideoStream.value.getVideoTracks().forEach((track) => {
-          track.applyConstraints({
-            frameRate: { max: currentMaxFramerate.value },
-            height: newVal,
-          });
-        });
-      } else {
-        appStore.allTrack.forEach((info) => {
-          info.track?.applyConstraints({
-            frameRate: { max: currentMaxFramerate.value },
-            height: newVal,
-          });
-        });
-      }
-
       networkStore.rtcMap.forEach(async (rtc) => {
         const res = await rtc.setResolutionRatio(newVal);
         if (res === 1) {
@@ -96,35 +81,25 @@ export function usePush() {
     }
   );
 
-  watch(
-    () => currentMaxFramerate.value,
-    (newVal) => {
-      if (canvasVideoStream.value) {
-        canvasVideoStream.value.getVideoTracks().forEach((track) => {
-          track.applyConstraints({
-            frameRate: { max: newVal },
-            height: currentResolutionRatio.value,
-          });
-        });
-      } else {
-        appStore.allTrack.forEach((info) => {
-          info.track?.applyConstraints({
-            frameRate: { max: newVal },
-            height: currentResolutionRatio.value,
-          });
-        });
-      }
-
-      networkStore.rtcMap.forEach(async (rtc) => {
-        const res = await rtc.setMaxFramerate(newVal);
-        if (res === 1) {
-          window.$message.success('切换帧率成功!');
-        } else {
-          window.$message.success('切换帧率失败!');
-        }
-      });
-    }
-  );
+  // watch(
+  //   () => currentMaxFramerate.value,
+  //   () => {
+  //     handleMaxFramerate({
+  //       stream: canvasVideoStream.value!,
+  //       height: currentResolutionRatio.value,
+  //       frameRate: currentMaxFramerate.value,
+  //     });
+  //     console.log(currentMaxFramerate.value, 'kkkkkk');
+  //     // networkStore.rtcMap.forEach(async (rtc) => {
+  //     //   const res = await rtc.setMaxFramerate(newVal);
+  //     //   if (res === 1) {
+  //     //     window.$message.success('切换帧率成功!');
+  //     //   } else {
+  //     //     window.$message.success('切换帧率失败!');
+  //     //   }
+  //     // });
+  //   }
+  // );
 
   watch(
     () => currentMaxBitrate.value,
@@ -270,6 +245,12 @@ export function usePush() {
         }
       }
     }
+
+    handleMaxFramerate({
+      stream: canvasVideoStream.value!,
+      height: currentResolutionRatio.value,
+      frameRate: currentMaxFramerate.value,
+    });
     handleStartLive({
       coverImg: lastCoverImg.value,
       name: roomName.value,

+ 1 - 0
src/hooks/use-rtcParams.ts

@@ -68,6 +68,7 @@ export const useRTCParams = () => {
     {
       label: '60帧',
       value: 60,
+      disabled: true,
     },
     {
       label: '120帧',

+ 11 - 2
src/hooks/use-srs-ws.ts

@@ -136,7 +136,7 @@ export const useSrsWs = () => {
     type: LiveRoomTypeEnum;
     receiver: string;
     videoEl?: HTMLVideoElement;
-    chunkDelay?: number;
+    chunkDelay: number;
   }) {
     console.log('handleStartLivehandleStartLive', receiver);
     networkStore.wsMap.get(roomId.value)?.send<WsStartLiveType['data']>({
@@ -185,7 +185,9 @@ export const useSrsWs = () => {
     console.warn(
       '22开始new WebRTCClass',
       receiver,
-      `${roomId.value}___${receiver!}`
+      `${roomId.value}___${receiver!}`,
+      isSRS.value,
+      canvasVideoStream.value
     );
     new WebRTCClass({
       maxBitrate: currentMaxBitrate.value,
@@ -195,6 +197,7 @@ export const useSrsWs = () => {
       videoEl,
       isSRS: true,
       receiver,
+      localStream: canvasVideoStream.value,
     });
     isSRS.value = true;
     handleSendOffer({
@@ -223,6 +226,8 @@ export const useSrsWs = () => {
       ws.status = WsConnectStatusEnum.disconnect;
       ws.update();
     });
+
+    // 收到offer
     ws.socketIo.on(WsMsgTypeEnum.offer, async (data: WsOfferType['data']) => {
       console.log('收到offer', data);
       if (data.receiver === mySocketId.value) {
@@ -262,6 +267,8 @@ export const useSrsWs = () => {
         console.error('不是发给我的offer');
       }
     });
+
+    // 收到answer
     ws.socketIo.on(WsMsgTypeEnum.answer, (data: WsAnswerType['data']) => {
       console.log('收到answer', data);
       if (data.receiver === mySocketId.value) {
@@ -272,6 +279,8 @@ export const useSrsWs = () => {
         console.error('不是发给我的answer');
       }
     });
+
+    // 收到candidate
     ws.socketIo.on(WsMsgTypeEnum.candidate, (data: WsCandidateType['data']) => {
       console.log('收到candidate', data);
       if (data.receiver === mySocketId.value) {

+ 105 - 47
src/network/webRTC.ts

@@ -5,6 +5,64 @@ import { WsCandidateType, WsMsgTypeEnum } from '@/interface-ws';
 import { AppRootState, useAppStore } from '@/store/app';
 import { useNetworkStore } from '@/store/network';
 
+/** 设置分辨率 */
+export async function handleResolutionRatio(data: {
+  frameRate: number;
+  height: number;
+  stream: MediaStream;
+}): Promise<number> {
+  const { frameRate, height, stream } = data;
+  const queue: Promise<any>[] = [];
+  console.log('开始设置分辨率', height);
+  stream.getTracks().forEach((track) => {
+    if (track.kind === 'video') {
+      queue.push(
+        track.applyConstraints({
+          height: { ideal: height },
+          frameRate: { ideal: frameRate },
+        })
+      );
+    }
+  });
+  try {
+    await Promise.all(queue);
+    console.log('设置分辨率成功');
+    return 1;
+  } catch (error) {
+    console.error('设置分辨率失败', height, error);
+    return 0;
+  }
+}
+/** 设置帧率 */
+export async function handleMaxFramerate(data: {
+  frameRate: number;
+  height: number;
+  stream: MediaStream;
+}): Promise<number> {
+  const { frameRate, height, stream } = data;
+  const queue: Promise<any>[] = [];
+  console.log('开始设置帧率', frameRate);
+  stream.getTracks().forEach((track) => {
+    if (track.kind === 'video') {
+      console.log('开始设置帧率---', frameRate, track.id);
+      queue.push(
+        track.applyConstraints({
+          height: { ideal: height },
+          frameRate: { ideal: frameRate },
+        })
+      );
+    }
+  });
+  try {
+    await Promise.all(queue);
+    console.log('设置帧率成功');
+    return 1;
+  } catch (error) {
+    console.error('设置帧率失败', frameRate, error);
+    return 0;
+  }
+}
+
 export class WebRTCClass {
   roomId = '-1';
   receiver = '';
@@ -32,10 +90,12 @@ export class WebRTCClass {
     resolutionRatio?: number;
     isSRS: boolean;
     receiver: string;
+    localStream?: MediaStream;
   }) {
     this.roomId = data.roomId;
     this.videoEl = data.videoEl;
     this.receiver = data.receiver;
+    this.localStream = data.localStream;
     if (data.maxBitrate) {
       this.maxBitrate = data.maxBitrate;
     }
@@ -152,58 +212,56 @@ export class WebRTCClass {
   };
 
   /** 设置分辨率 */
-  setResolutionRatio = (height: number) => {
-    console.log('开始设置分辨率', height);
-    return new Promise((resolve) => {
-      this.localStream?.getTracks().forEach((track) => {
-        if (track.kind === 'video') {
-          track
-            .applyConstraints({
-              height: { ideal: height },
-            })
-            .then(() => {
-              console.log('设置分辨率成功');
-              this.resolutionRatio = height;
-              resolve(1);
-            })
-            .catch((error) => {
-              console.error('设置分辨率失败', height, error);
-              resolve(0);
-            });
-        }
+  setResolutionRatio = async (height: number) => {
+    if (this.localStream) {
+      const res = await handleResolutionRatio({
+        frameRate: this.maxFramerate,
+        stream: this.localStream,
+        height,
       });
-    });
+      console.log(res, '设置分辨率');
+      return res;
+    }
   };
 
   /** 设置最大帧率 */
-  setMaxFramerate = (maxFramerate: number) => {
-    console.log('开始设置最大帧率', maxFramerate);
-    return new Promise<number>((resolve) => {
-      this.peerConnection?.getSenders().forEach((sender) => {
-        if (sender.track?.kind === 'video') {
-          const parameters = { ...sender.getParameters() };
-          if (parameters.encodings[0]) {
-            if (parameters.encodings[0].maxFramerate === maxFramerate) {
-              console.log('最大帧率不变,不设置');
-              resolve(1);
-              return;
-            }
-            parameters.encodings[0].maxFramerate = maxFramerate;
-            sender
-              .setParameters(parameters)
-              .then(() => {
-                console.log('设置最大帧率成功', maxFramerate);
-                this.maxFramerate = maxFramerate;
-                resolve(1);
-              })
-              .catch((error) => {
-                console.error('设置最大帧率失败', maxFramerate, error);
-                resolve(0);
-              });
-          }
-        }
+  setMaxFramerate = async (maxFramerate: number) => {
+    console.log(this.localStream, maxFramerate, '设置最大帧率111');
+    if (this.localStream) {
+      const res = await handleMaxFramerate({
+        frameRate: maxFramerate,
+        stream: this.localStream,
+        height: this.resolutionRatio,
       });
-    });
+      console.log(res, '设置最大帧率');
+      return res;
+    }
+    // return new Promise<number>((resolve) => {
+    // this.peerConnection?.getSenders().forEach((sender) => {
+    //   if (sender.track?.kind === 'video') {
+    //     const parameters = { ...sender.getParameters() };
+    //     if (parameters.encodings[0]) {
+    //       if (parameters.encodings[0].maxFramerate === maxFramerate) {
+    //         console.log('最大帧率不变,不设置');
+    //         resolve(1);
+    //         return;
+    //       }
+    //       parameters.encodings[0].maxFramerate = maxFramerate;
+    //       sender
+    //         .setParameters(parameters)
+    //         .then(() => {
+    //           console.log('设置最大帧率成功', maxFramerate);
+    //           this.maxFramerate = maxFramerate;
+    //           resolve(1);
+    //         })
+    //         .catch((error) => {
+    //           console.error('设置最大帧率失败', maxFramerate, error);
+    //           resolve(0);
+    //         });
+    //     }
+    //   }
+    // });
+    // });
   };
 
   /** 设置最大码率 */

+ 71 - 15
src/views/push/index.vue

@@ -375,6 +375,39 @@ const sendBlobTimer = ref();
 const bolbId = ref(0);
 const chunkDelay = ref(1000 * 2);
 
+watch(
+  () => currentMaxBitrate.value,
+  () => {
+    if (liveType === LiveRoomTypeEnum.user_msr) {
+      const stream = pushCanvasRef.value!.captureStream();
+      const audioTrack = webaudioVideo
+        // @ts-ignore
+        .value!.captureStream()
+        .getAudioTracks()[0];
+      stream.addTrack(audioTrack);
+      handleMsr(stream);
+    }
+  }
+);
+watch(
+  () => currentMaxFramerate.value,
+  () => {
+    workerTimers.clearInterval(workerTimerId.value);
+    renderFrame();
+  }
+);
+
+watch(
+  () => damuList.value.length,
+  () => {
+    setTimeout(() => {
+      if (danmuListRef.value) {
+        danmuListRef.value.scrollTop = danmuListRef.value.scrollHeight;
+      }
+    }, 0);
+  }
+);
+
 function handleSendBlob(event: BlobEvent) {
   bolbId.value += 1;
   sendBlob({
@@ -383,6 +416,25 @@ function handleSendBlob(event: BlobEvent) {
     delay: chunkDelay.value,
   });
 }
+// function handleAllType() {
+//   const types = [
+//     'video/webm',
+//     'audio/webm',
+//     'video/webm;codecs=vp8',
+//     'video/webm;codecs=daala',
+//     'video/webm;codecs=h264',
+//     'audio/webm;codecs=opus',
+//     'audio/webm;codecs=aac',
+//     'audio/webm;codecs=h264,opus',
+//     'video/webm;codecs=avc1.64001f,opus',
+//     'video/webm;codecs=avc1.4d002a,opus',
+//     'video/mpeg',
+//   ];
+//   Object.keys(types).forEach((item) => {
+//     console.log(types[item], MediaRecorder.isTypeSupported(types[item]));
+//   });
+// }
+// handleAllType();
 
 function handleMsr(stream: MediaStream) {
   // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
@@ -395,11 +447,26 @@ function handleMsr(stream: MediaStream) {
   } else {
     console.log('支持', mimeType);
   }
+  /**
+   * 小写的 "kb/s" 表示千比特每秒,而大写的 "KB/s" 表示千字节每秒
+   * 例如,当我们说 100 kb/s 时,意思是每秒传输100千比特(比特)的数据。而当我们说 100 KB/s 时,意思是每秒传输100千字节(字节)的数据,相当于800千比特(比特)
+   * 千字节(KB)、兆字节(MB)、千兆字节(GB)
+   * 8 比特(bits)等于 1 字节(byte)
+   * 1 Kbps(千比特每秒)等于 0.125 KB/s(千字节每秒)
+   * 1 Mbps(兆比特每秒)等于 0.125 MB/s(兆字节每秒)
+   * bit,比特
+   * byte,字节
+   * videoBitsPerSecond的单位是比特,假设videoBitsPerSecond值是1000*2000,即2000000
+   * 2000000比特等于2000000 / 8 / 1000 = 250 KB/s
+   */
+
   recorder.value = new MediaRecorder(stream, {
     mimeType,
-    // videoBitsPerSecond: 1000 * 2000,
+    // bitsPerSecond: 1000 * currentMaxBitrate.value,
+    videoBitsPerSecond: 1000 * currentMaxBitrate.value, // 单位是比特
     // audioBitsPerSecond: 1000 * 2000,
   });
+  console.log(currentMaxBitrate.value, ' currentMaxBitrate.value');
   recorder.value.ondataavailable = handleSendBlob;
   sendBlobTimer.value = setInterval(function () {
     recorder.value?.stop();
@@ -407,17 +474,6 @@ function handleMsr(stream: MediaStream) {
   }, chunkDelay.value);
 }
 
-watch(
-  () => damuList.value.length,
-  () => {
-    setTimeout(() => {
-      if (danmuListRef.value) {
-        danmuListRef.value.scrollTop = danmuListRef.value.scrollHeight;
-      }
-    }, 0);
-  }
-);
-
 onMounted(() => {
   setTimeout(() => {
     scrollTo(0, 0);
@@ -484,7 +540,8 @@ function clearFrame() {
 }
 
 function renderFrame() {
-  const delay = 1000 / 60; // 16.666666666666668
+  // currentMaxFramerate等于20,实际fps是17.68
+  const delay = 1000 / (currentMaxFramerate.value / (17.68 / 20)); // 60帧的话即16.666666666666668
   workerTimerId.value = workerTimers.setInterval(() => {
     renderAll();
   }, delay);
@@ -517,7 +574,7 @@ function handleMixedAudio() {
   if (webaudioVideo.value) {
     webaudioVideo.value.remove();
   }
-  webaudioVideo.value = createVideo({ appendChild: true, show: true });
+  webaudioVideo.value = createVideo({ appendChild: true, show: false });
   bodyAppendChildElArr.value.push(webaudioVideo.value);
   webaudioVideo.value.className = 'web-audio-video';
   webaudioVideo.value!.srcObject = destination.stream;
@@ -559,7 +616,6 @@ function handleStartLive() {
     const stream = pushCanvasRef.value!.captureStream();
     // @ts-ignore
     const audioTrack = webaudioVideo.value!.captureStream().getAudioTracks()[0];
-    // @ts-ignore
     stream.addTrack(audioTrack);
     handleMsr(stream);
   }