Procházet zdrojové kódy

feat: 全面优化

shuisheng před 1 rokem
rodič
revize
14b0770a57

+ 13 - 4
doc/备忘.md

@@ -3,11 +3,20 @@
 - srs直播
   > todo
 - webrtc直播
-  > 在ohterJoin的时候,主播给除自己以外的所有人发offer
-- webrtc会议
-  > 在roomLiving的时候,主播给除自己以外的所有人发offer
+  > 在ohterJoin的时候,主播给除自己以外的所有人发offer,其他所有人只获取主播的视频流
+  > 有主播和2个观众,结果就是画面是主播的画面,主播new了2个rtc,每个观众各自new了一个rtc
+- webrtc会议实现一
+  > 在roomLiving的时候,主播给除自己以外的所有人发offer,其他所有人都和主播视频通话,主播将自己和其他所有人的流混在一起
+  > 有主播和2个观众,结果就是画面是所有人的画面,主播new了2个rtc,每个观众各自new了一个rtc
+  > 优点:实现简单;缺点:主播断开后,所有观众都会跟着断开。
+- webrtc会议实现二
+  > 在roomLiving的时候,每个人都给除自己以外的所有人发offer,并和除自己以外的所有人进行视频通话,将收到的流进行展示
+  > 有主播和2个观众,结果就是画面是所有人的画面,主播new了2个rtc,每个观众各自new了一个rtc
+  > 优点:所有视频通话都是独立的,一个断开不会影响另一个;缺点:实现起来稍微麻烦一点。
 - 打pk
-  > 主播1和主播2先webrtc视频通话,再由任意一方转推srs。
+  > 主播和其他人先webrtc视频通话,再由任意一方转推srs。
+- 腾讯云云直播
+  > 在上述基础上再增加即可。
 
 ## 测试
 

+ 1 - 0
package.json

@@ -46,6 +46,7 @@
     "pinia-plugin-persistedstate": "^3.2.0",
     "qrcode": "^1.5.3",
     "socket.io-client": "^4.7.2",
+    "socket.io-msgpack-parser": "^3.0.2",
     "spark-md5": "^3.0.2",
     "video.js": "^8.3.0",
     "vue": "^3.3.4",

+ 94 - 0
pnpm-lock.yaml

@@ -47,6 +47,9 @@ dependencies:
   socket.io-client:
     specifier: ^4.7.2
     version: 4.7.2
+  socket.io-msgpack-parser:
+    specifier: ^3.0.2
+    version: 3.0.2
   spark-md5:
     specifier: ^3.0.2
     version: 3.0.2
@@ -1394,6 +1397,7 @@ packages:
   /@commitlint/config-validator@17.4.4:
     resolution: {integrity: sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==}
     engines: {node: '>=v14'}
+    requiresBuild: true
     dependencies:
       '@commitlint/types': 17.4.4
       ajv: 8.12.0
@@ -1416,6 +1420,7 @@ packages:
   /@commitlint/execute-rule@17.4.0:
     resolution: {integrity: sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==}
     engines: {node: '>=v14'}
+    requiresBuild: true
     dev: true
     optional: true
 
@@ -1529,6 +1534,7 @@ packages:
   /@commitlint/resolve-extends@17.4.4:
     resolution: {integrity: sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==}
     engines: {node: '>=v14'}
+    requiresBuild: true
     dependencies:
       '@commitlint/config-validator': 17.4.4
       '@commitlint/types': 17.4.4
@@ -1572,6 +1578,7 @@ packages:
   /@commitlint/types@17.4.4:
     resolution: {integrity: sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==}
     engines: {node: '>=v14'}
+    requiresBuild: true
     dependencies:
       chalk: 4.1.2
     dev: true
@@ -2323,6 +2330,7 @@ packages:
   /@mapbox/node-pre-gyp@1.0.11:
     resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
     hasBin: true
+    requiresBuild: true
     dependencies:
       detect-libc: 2.0.2
       https-proxy-agent: 5.0.1
@@ -2640,6 +2648,7 @@ packages:
   /@tootallnate/once@2.0.0:
     resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
     engines: {node: '>= 10'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -3283,11 +3292,13 @@ packages:
 
   /abab@2.0.6:
     resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /abbrev@1.1.1:
     resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -3300,6 +3311,7 @@ packages:
 
   /acorn-globals@6.0.0:
     resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
+    requiresBuild: true
     dependencies:
       acorn: 7.4.1
       acorn-walk: 7.2.0
@@ -3324,6 +3336,7 @@ packages:
   /acorn-walk@7.2.0:
     resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
     engines: {node: '>=0.4.0'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -3335,6 +3348,7 @@ packages:
     resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
     engines: {node: '>=0.4.0'}
     hasBin: true
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -3359,6 +3373,7 @@ packages:
   /agent-base@6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
+    requiresBuild: true
     dependencies:
       debug: 4.3.4(supports-color@9.3.1)
     transitivePeerDependencies:
@@ -3485,12 +3500,14 @@ packages:
 
   /aproba@2.0.0:
     resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /are-we-there-yet@2.0.0:
     resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
     engines: {node: '>=10'}
+    requiresBuild: true
     dependencies:
       delegates: 1.0.0
       readable-stream: 3.6.2
@@ -3828,6 +3845,7 @@ packages:
 
   /browser-process-hrtime@1.0.0:
     resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -3977,6 +3995,7 @@ packages:
   /chownr@2.0.0:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
     engines: {node: '>=10'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -4105,6 +4124,7 @@ packages:
   /color-support@1.1.3:
     resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
     hasBin: true
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -4173,6 +4193,10 @@ packages:
       dot-prop: 5.3.0
     dev: true
 
+  /component-emitter@1.3.1:
+    resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
+    dev: false
+
   /compressible@2.0.18:
     resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
     engines: {node: '>= 0.6'}
@@ -4227,6 +4251,7 @@ packages:
 
   /console-control-strings@1.1.0:
     resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -4478,6 +4503,7 @@ packages:
   /cosmiconfig-typescript-loader@4.3.0(@types/node@18.15.3)(cosmiconfig@8.1.3)(ts-node@10.9.1)(typescript@4.9.5):
     resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==}
     engines: {node: '>=12', npm: '>=6'}
+    requiresBuild: true
     peerDependencies:
       '@types/node': '*'
       cosmiconfig: '>=7'
@@ -4505,6 +4531,7 @@ packages:
   /cosmiconfig@8.1.3:
     resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==}
     engines: {node: '>=14'}
+    requiresBuild: true
     dependencies:
       import-fresh: 3.3.0
       js-yaml: 4.1.0
@@ -4732,17 +4759,20 @@ packages:
 
   /cssom@0.3.8:
     resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /cssom@0.5.0:
     resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /cssstyle@2.3.0:
     resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dependencies:
       cssom: 0.3.8
     dev: false
@@ -4792,6 +4822,7 @@ packages:
   /data-urls@3.0.2:
     resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       abab: 2.0.6
       whatwg-mimetype: 3.0.0
@@ -4865,12 +4896,14 @@ packages:
 
   /decimal.js@10.4.3:
     resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /decompress-response@4.2.1:
     resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dependencies:
       mimic-response: 2.1.0
     dev: false
@@ -4942,6 +4975,7 @@ packages:
 
   /delegates@1.0.0:
     resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -4970,6 +5004,7 @@ packages:
   /detect-libc@2.0.2:
     resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -5052,6 +5087,7 @@ packages:
   /domexception@4.0.0:
     resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       webidl-conversions: 7.0.0
     dev: false
@@ -5541,6 +5577,7 @@ packages:
     resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
     engines: {node: '>=6.0'}
     hasBin: true
+    requiresBuild: true
     dependencies:
       esprima: 4.0.1
       estraverse: 5.3.0
@@ -5796,6 +5833,7 @@ packages:
     resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
     engines: {node: '>=4'}
     hasBin: true
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -6229,6 +6267,7 @@ packages:
   /fs-minipass@2.1.0:
     resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
     engines: {node: '>= 8'}
+    requiresBuild: true
     dependencies:
       minipass: 3.3.6
     dev: false
@@ -6267,6 +6306,7 @@ packages:
   /gauge@3.0.2:
     resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
     engines: {node: '>=10'}
+    requiresBuild: true
     dependencies:
       aproba: 2.0.0
       color-support: 1.1.3
@@ -6577,6 +6617,7 @@ packages:
 
   /has-unicode@2.0.1:
     resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -6633,6 +6674,7 @@ packages:
   /html-encoding-sniffer@3.0.0:
     resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       whatwg-encoding: 2.0.0
     dev: false
@@ -6720,6 +6762,7 @@ packages:
   /http-proxy-agent@5.0.0:
     resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
     engines: {node: '>= 6'}
+    requiresBuild: true
     dependencies:
       '@tootallnate/once': 2.0.0
       agent-base: 6.0.2
@@ -6760,6 +6803,7 @@ packages:
   /https-proxy-agent@5.0.1:
     resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
     engines: {node: '>= 6'}
+    requiresBuild: true
     dependencies:
       agent-base: 6.0.2
       debug: 4.3.4(supports-color@9.3.1)
@@ -6792,6 +6836,7 @@ packages:
   /iconv-lite@0.6.3:
     resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
     engines: {node: '>=0.10.0'}
+    requiresBuild: true
     dependencies:
       safer-buffer: 2.1.2
     dev: false
@@ -7094,6 +7139,7 @@ packages:
 
   /is-potential-custom-element-name@1.0.1:
     resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -7514,6 +7560,7 @@ packages:
 
   /lodash.isplainobject@4.0.6:
     resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+    requiresBuild: true
     dev: true
     optional: true
 
@@ -7531,6 +7578,7 @@ packages:
 
   /lodash.mergewith@4.6.2:
     resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+    requiresBuild: true
     dev: true
     optional: true
 
@@ -7724,6 +7772,7 @@ packages:
   /mimic-response@2.1.0:
     resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -7783,6 +7832,7 @@ packages:
   /minipass@3.3.6:
     resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dependencies:
       yallist: 4.0.0
     dev: false
@@ -7791,12 +7841,14 @@ packages:
   /minipass@5.0.0:
     resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
     engines: {node: '>=8'}
+    requiresBuild: true
     dev: false
     optional: true
 
   /minizlib@2.1.2:
     resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
     engines: {node: '>= 8'}
+    requiresBuild: true
     dependencies:
       minipass: 3.3.6
       yallist: 4.0.0
@@ -7814,6 +7866,7 @@ packages:
     resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
     engines: {node: '>=10'}
     hasBin: true
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -7924,6 +7977,7 @@ packages:
 
   /nan@2.17.0:
     resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -7967,6 +8021,7 @@ packages:
   /node-fetch@2.6.12:
     resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
     engines: {node: 4.x || >=6.0.0}
+    requiresBuild: true
     peerDependencies:
       encoding: ^0.1.0
     peerDependenciesMeta:
@@ -7988,6 +8043,7 @@ packages:
     resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
     engines: {node: '>=6'}
     hasBin: true
+    requiresBuild: true
     dependencies:
       abbrev: 1.1.1
     dev: false
@@ -8026,6 +8082,10 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /notepack.io@2.2.0:
+    resolution: {integrity: sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw==}
+    dev: false
+
   /npm-run-path@3.1.0:
     resolution: {integrity: sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==}
     engines: {node: '>=8'}
@@ -8048,6 +8108,7 @@ packages:
 
   /npmlog@5.0.1:
     resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+    requiresBuild: true
     dependencies:
       are-we-there-yet: 2.0.0
       console-control-strings: 1.1.0
@@ -8064,12 +8125,14 @@ packages:
 
   /nwsapi@2.2.7:
     resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /object-assign@4.1.1:
     resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
     engines: {node: '>=0.10.0'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -8336,6 +8399,7 @@ packages:
 
   /parse5@6.0.1:
     resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -9234,6 +9298,7 @@ packages:
 
   /psl@1.9.0:
     resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -9272,6 +9337,7 @@ packages:
 
   /querystringify@2.2.0:
     resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -9715,6 +9781,7 @@ packages:
   /saxes@5.0.1:
     resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
     engines: {node: '>=10'}
+    requiresBuild: true
     dependencies:
       xmlchars: 2.2.0
     dev: false
@@ -9902,11 +9969,13 @@ packages:
 
   /simple-concat@1.0.1:
     resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /simple-get@3.1.1:
     resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==}
+    requiresBuild: true
     dependencies:
       decompress-response: 4.2.1
       once: 1.4.0
@@ -9977,6 +10046,13 @@ packages:
       - utf-8-validate
     dev: false
 
+  /socket.io-msgpack-parser@3.0.2:
+    resolution: {integrity: sha512-1e76bJ1PCKi9H+JiYk+S29PBJvknHjQWM7Mtj0hjF2KxDA6b6rQxv3rTsnwBoz/haZOhlCDIMQvPATbqYeuMxg==}
+    dependencies:
+      component-emitter: 1.3.1
+      notepack.io: 2.2.0
+    dev: false
+
   /socket.io-parser@4.2.4:
     resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
     engines: {node: '>=10.0.0'}
@@ -10312,6 +10388,7 @@ packages:
 
   /symbol-tree@3.2.4:
     resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -10330,6 +10407,7 @@ packages:
   /tar@6.1.15:
     resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
     engines: {node: '>=10'}
+    requiresBuild: true
     dependencies:
       chownr: 2.0.0
       fs-minipass: 2.1.0
@@ -10459,6 +10537,7 @@ packages:
   /tough-cookie@4.1.3:
     resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
     engines: {node: '>=6'}
+    requiresBuild: true
     dependencies:
       psl: 1.9.0
       punycode: 2.3.0
@@ -10469,12 +10548,14 @@ packages:
 
   /tr46@0.0.3:
     resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /tr46@3.0.0:
     resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       punycode: 2.3.0
     dev: false
@@ -10709,6 +10790,7 @@ packages:
   /universalify@0.2.0:
     resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
     engines: {node: '>= 4.0.0'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -10780,6 +10862,7 @@ packages:
 
   /url-parse@1.5.10:
     resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+    requiresBuild: true
     dependencies:
       querystringify: 2.2.0
       requires-port: 1.0.0
@@ -11037,6 +11120,7 @@ packages:
   /w3c-hr-time@1.0.2:
     resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
     deprecated: Use your platform's native performance.now() and performance.timeOrigin.
+    requiresBuild: true
     dependencies:
       browser-process-hrtime: 1.0.0
     dev: false
@@ -11045,6 +11129,7 @@ packages:
   /w3c-xmlserializer@3.0.0:
     resolution: {integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       xml-name-validator: 4.0.0
     dev: false
@@ -11070,12 +11155,14 @@ packages:
 
   /webidl-conversions@3.0.1:
     resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+    requiresBuild: true
     dev: false
     optional: true
 
   /webidl-conversions@7.0.0:
     resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dev: false
     optional: true
 
@@ -11299,6 +11386,7 @@ packages:
   /whatwg-encoding@2.0.0:
     resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       iconv-lite: 0.6.3
     dev: false
@@ -11307,12 +11395,14 @@ packages:
   /whatwg-mimetype@3.0.0:
     resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dev: false
     optional: true
 
   /whatwg-url@10.0.0:
     resolution: {integrity: sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       tr46: 3.0.0
       webidl-conversions: 7.0.0
@@ -11322,6 +11412,7 @@ packages:
   /whatwg-url@11.0.0:
     resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
     engines: {node: '>=12'}
+    requiresBuild: true
     dependencies:
       tr46: 3.0.0
       webidl-conversions: 7.0.0
@@ -11330,6 +11421,7 @@ packages:
 
   /whatwg-url@5.0.0:
     resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+    requiresBuild: true
     dependencies:
       tr46: 0.0.3
       webidl-conversions: 3.0.1
@@ -11378,6 +11470,7 @@ packages:
 
   /wide-align@1.1.5:
     resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+    requiresBuild: true
     dependencies:
       string-width: 4.2.3
     dev: false
@@ -11494,6 +11587,7 @@ packages:
 
   /xmlchars@2.2.0:
     resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+    requiresBuild: true
     dev: false
     optional: true
 

+ 2 - 2
src/components/VideoControls/index.vue

@@ -158,13 +158,13 @@ function changePlay() {
 function changeLiveLine(item) {
   if (
     item === LiveLineEnum.rtc &&
-    appStore.liveRoomInfo?.type !== LiveRoomTypeEnum.user_wertc_live
+    appStore.liveRoomInfo?.type !== LiveRoomTypeEnum.wertc_live
   ) {
     window.$message.info('不支持该线路!');
     return;
   }
   if (
-    appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc_live &&
+    appStore.liveRoomInfo?.type === LiveRoomTypeEnum.wertc_live &&
     item !== LiveLineEnum.rtc
   ) {
     window.$message.info('不支持该线路!');

+ 15 - 0
src/constant.ts

@@ -1,6 +1,8 @@
 import { MediaTypeEnum } from '@/interface';
 import { domain } from '@/spec-config';
 
+import { LiveRoomTypeEnum } from './types/ILiveRoom';
+
 export const QQ_CLIENT_ID = `101958191`;
 export const QQ_OAUTH_URL = `https://graph.qq.com/oauth2.0`;
 export const QQ_REDIRECT_URI = `https://live.${domain}/oauth/qq_login`;
@@ -189,6 +191,19 @@ export const mediaTypeEnumMap = {
   [MediaTypeEnum.metting]: '会议',
 };
 
+export const liveRoomTypeEnumMap = {
+  [LiveRoomTypeEnum.msr]: 'msr推流',
+  [LiveRoomTypeEnum.obs]: 'obs推流',
+  [LiveRoomTypeEnum.pk]: '打pk',
+  [LiveRoomTypeEnum.srs]: 'srs推流',
+  [LiveRoomTypeEnum.system]: '系统推流',
+  [LiveRoomTypeEnum.tencent_css]: '腾讯云css推流',
+  [LiveRoomTypeEnum.tencent_css_pk]: '腾讯云css打pk',
+  [LiveRoomTypeEnum.wertc_live]: 'webrtc直播',
+  [LiveRoomTypeEnum.wertc_meeting_one]: 'webrtc会议一',
+  [LiveRoomTypeEnum.wertc_meeting_two]: 'webrtc会议二',
+};
+
 export const sliderList = [
   {
     img: `https://resource.${domain}/billd-live/image/a4039f86e5352bcfccaddecc4b72a1df.webp`,

+ 32 - 50
src/hooks/use-pull.ts

@@ -75,23 +75,6 @@ export function usePull(roomId: string) {
       stopDrawingArr.value = [];
       stopDrawingArr.value.forEach((cb) => cb());
       if (videoWrapRef.value) {
-        const rect = videoWrapRef.value.getBoundingClientRect();
-        // const { canvas, stopDrawing } = videoToCanvas({
-        //   wrapSize: {
-        //     width: rect.width,
-        //     height: rect.height,
-        //   },
-        //   videoEl: networkStore.getRtcMap(`${mySocketId.value}___${roomId}`)
-        //     ?.videoEl!,
-        //   videoResize: ({ w, h }) => {
-        //     videoHeight.value = `${w}x${h}`;
-        //   },
-        // });
-        // document.body.appendChild(canvas);
-        // stopDrawingArr.value.push(stopDrawing);
-        // remoteVideo.value.push(canvas);
-        // roomLiving.value = true;
-        // videoLoading.value = false;
       }
     }
   );
@@ -142,6 +125,7 @@ export function usePull(roomId: string) {
           videoHeight.value = `${w}x${h}`;
         },
       });
+      console.log(canvas, 2221211223);
       stopDrawingArr.value.push(stopDrawing);
       remoteVideo.value.push(canvas);
       videoLoading.value = false;
@@ -163,21 +147,21 @@ export function usePull(roomId: string) {
     flvurl.value = data.flv_url!;
     hlsurl.value = data.hls_url!;
     switch (data.type) {
-      case LiveRoomTypeEnum.user_srs:
+      case LiveRoomTypeEnum.srs:
         if (appStore.liveLine === LiveLineEnum.flv) {
           handleFlvPlay();
         } else if (appStore.liveLine === LiveLineEnum.hls) {
           handleHlsPlay(data.hls_url!);
         }
         break;
-      case LiveRoomTypeEnum.user_obs:
+      case LiveRoomTypeEnum.obs:
         if (appStore.liveLine === LiveLineEnum.flv) {
           handleFlvPlay();
         } else if (appStore.liveLine === LiveLineEnum.hls) {
           handleHlsPlay(data.hls_url!);
         }
         break;
-      case LiveRoomTypeEnum.user_msr:
+      case LiveRoomTypeEnum.msr:
         if (appStore.liveLine === LiveLineEnum.flv) {
           handleFlvPlay();
         } else if (appStore.liveLine === LiveLineEnum.hls) {
@@ -191,34 +175,44 @@ export function usePull(roomId: string) {
           handleHlsPlay(data.hls_url!);
         }
         break;
-      case LiveRoomTypeEnum.user_wertc_live:
+      case LiveRoomTypeEnum.pk:
+        if (appStore.liveLine === LiveLineEnum.flv) {
+          handleFlvPlay();
+        } else if (appStore.liveLine === LiveLineEnum.hls) {
+          handleHlsPlay(data.hls_url!);
+        }
+        break;
+      case LiveRoomTypeEnum.wertc_live:
         appStore.setLiveLine(LiveLineEnum.rtc);
         break;
     }
   }
 
   watch(
-    () => roomLiving.value,
-    (val) => {
-      if (val) {
+    [() => roomLiving.value, () => appStore.liveRoomInfo],
+    ([val, liveRoomInfo]) => {
+      if (val && liveRoomInfo) {
         if (
-          appStore.liveRoomInfo &&
           [
             LiveRoomTypeEnum.system,
-            LiveRoomTypeEnum.user_msr,
-            LiveRoomTypeEnum.user_srs,
-            LiveRoomTypeEnum.user_obs,
-            LiveRoomTypeEnum.user_pk,
-            LiveRoomTypeEnum.user_tencent_css,
-            LiveRoomTypeEnum.user_tencent_css_pk,
-          ].includes(appStore.liveRoomInfo.type!)
+            LiveRoomTypeEnum.msr,
+            LiveRoomTypeEnum.srs,
+            LiveRoomTypeEnum.obs,
+            LiveRoomTypeEnum.pk,
+            LiveRoomTypeEnum.tencent_css,
+            LiveRoomTypeEnum.tencent_css_pk,
+          ].includes(liveRoomInfo.type!)
         ) {
-          handlePlay(appStore.liveRoomInfo!);
+          handlePlay(liveRoomInfo!);
         }
       } else {
         closeRtc();
         handleStopDrawing();
       }
+    },
+    {
+      deep: true,
+      immediate: true,
     }
   );
 
@@ -241,6 +235,7 @@ export function usePull(roomId: string) {
       }
     }
   );
+
   watch(
     () => cacheStore.muted,
     (newVal) => {
@@ -254,6 +249,7 @@ export function usePull(roomId: string) {
       }
     }
   );
+
   watch(
     () => cacheStore.volume,
     (newVal) => {
@@ -267,6 +263,7 @@ export function usePull(roomId: string) {
       }
     }
   );
+
   watch(
     () => appStore.play,
     (newVal) => {
@@ -296,24 +293,9 @@ export function usePull(roomId: string) {
   watch(
     () => networkStore.rtcMap,
     (newVal) => {
-      if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc_live) {
+      if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.wertc_live) {
         newVal.forEach((item) => {
           videoLoading.value = false;
-          // if (videoWrapRef.value) {
-          //   const rect = videoWrapRef.value.getBoundingClientRect();
-          //   const { canvas } = videoToCanvas({
-          //     wrapSize: {
-          //       width: rect.width,
-          //       height: rect.height,
-          //     },
-          //     videoEl: item.videoEl,
-          //     videoResize: ({ w, h }) => {
-          //       videoHeight.value = `${w}x${h}`;
-          //     },
-          //   });
-          //   videoElArr.value.push(item.videoEl);
-          //   remoteVideo.value.push(canvas);
-          // }
         });
       }
     },
@@ -330,7 +312,7 @@ export function usePull(roomId: string) {
         console.log('localStream变了');
         console.log('音频轨:', val?.getAudioTracks());
         console.log('视频轨:', val?.getVideoTracks());
-        if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.user_wertc_live) {
+        if (appStore.liveRoomInfo?.type === LiveRoomTypeEnum.wertc_live) {
           videoElArr.value.forEach((dom) => {
             dom.remove();
           });

+ 151 - 190
src/hooks/use-websocket.ts

@@ -1,15 +1,15 @@
 import { copyToClipBoard, getRandomString } from 'billd-utils';
-import { NButton, useDialog } from 'naive-ui';
+import { NButton } from 'naive-ui';
 import { computed, h, onUnmounted, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
 
 import { fetchVerifyPkKey } from '@/api/liveRoom';
-import { fetchRtcV1Publish } from '@/api/srs';
-import { SRS_CB_URL_PARAMS, THEME_COLOR, WEBSOCKET_URL } from '@/constant';
+import { THEME_COLOR, WEBSOCKET_URL } from '@/constant';
 import { useRTCParams } from '@/hooks/use-rtcParams';
 import { useTip } from '@/hooks/use-tip';
-import { useWebRtcManyToManyMeeting } from '@/hooks/webrtc/manyToManyMeeting';
-import { useWebRtcOneToManyLive } from '@/hooks/webrtc/oneToManyLive';
+import { useWebRtcLive } from '@/hooks/webrtc/live';
+import { useWebRtcMeetingOne } from '@/hooks/webrtc/meetingOne';
+import { useWebRtcSrs } from '@/hooks/webrtc/srs';
 import {
   DanmuMsgTypeEnum,
   ILiveUser,
@@ -43,20 +43,22 @@ import {
   WsStartLiveType,
   WsUpdateJoinInfoType,
 } from '@/types/websocket';
-import { createNullVideo, createVideo, handleUserMedia } from '@/utils';
+import { createNullVideo, handleUserMedia } from '@/utils';
+
+import { useWebRtcMeetingPk } from './webrtc/meetingPk';
 
 export const useWebsocket = () => {
   const route = useRoute();
   const appStore = useAppStore();
   const userStore = useUserStore();
   const networkStore = useNetworkStore();
-  const dialog = useDialog();
 
   const { maxBitrate, maxFramerate, resolutionRatio } = useRTCParams();
-  const { updateWebRtcOneToManyLiveConfig, webRtcOneToManyLive } =
-    useWebRtcOneToManyLive();
-  const { updateWebRtcManyToManyMeetingConfig, webRtcManyToManyMeeting } =
-    useWebRtcManyToManyMeeting();
+  const { updateWebRtcMeetingPkConfig, webRtcMeetingPk } = useWebRtcMeetingPk();
+  const { updateWebRtcSrsConfig, webRtcSrs } = useWebRtcSrs();
+  const { updateWebRtcLiveConfig, webRtcLive } = useWebRtcLive();
+  const { updateWebRtcMeetingOneConfig, webRtcMeetingOne } =
+    useWebRtcMeetingOne();
 
   const connectStatus = ref();
   const loopHeartbeatTimer = ref();
@@ -70,7 +72,7 @@ export const useWebsocket = () => {
   const anchorInfo = ref<IUser>();
   const anchorSocketId = ref('');
   const canvasVideoStream = ref<MediaStream>();
-  const meetingMyStream = ref<MediaStream>();
+  const userStream = ref<MediaStream>();
   const lastCoverImg = ref('');
   const currentMaxBitrate = ref(maxBitrate.value[3].value);
   const currentMaxFramerate = ref(maxFramerate.value[2].value);
@@ -83,19 +85,6 @@ export const useWebsocket = () => {
     clearInterval(loopGetLiveUserTimer.value);
   });
 
-  watch(
-    () => appStore.pkStream,
-    (newval) => {
-      if (newval && isAnchor.value) {
-        console.log('转推到srs', newval);
-        srsWebRtc.sendOffer({
-          isPk: true,
-          sender: mySocketId.value,
-        });
-      }
-    }
-  );
-
   watch(
     [() => userStore.userInfo?.id, () => connectStatus.value],
     ([userInfo, status]) => {
@@ -173,18 +162,37 @@ export const useWebsocket = () => {
         msrMaxDelay,
       },
     });
-    // if (type === LiveRoomTypeEnum.user_msr) {
-    //   return;
-    // }
-    // if ([LiveRoomTypeEnum.user_srs, LiveRoomTypeEnum.user_obs].includes(type)) {
-    //   isSRS.value = true;
-    //   srsWebRtc.sendOffer({
-    //     isPk: false,
-    //     sender: mySocketId.value,
-    //   });
-    // } else {
-    //   isSRS.value = false;
-    // }
+    if (type === LiveRoomTypeEnum.srs) {
+      updateWebRtcSrsConfig({
+        isPk: false,
+        roomId: roomId.value,
+        canvasVideoStream: canvasVideoStream.value,
+      });
+      webRtcSrs.newWebRtc({
+        sender: mySocketId.value,
+        receiver: 'srs',
+        videoEl: createNullVideo(),
+      });
+      webRtcSrs.sendOffer({
+        sender: mySocketId.value,
+        receiver: 'srs',
+      });
+    } else if (type === LiveRoomTypeEnum.pk) {
+      updateWebRtcSrsConfig({
+        isPk: true,
+        roomId: roomId.value,
+        canvasVideoStream: canvasVideoStream.value,
+      });
+      webRtcSrs.newWebRtc({
+        sender: mySocketId.value,
+        receiver: 'srs',
+        videoEl: createNullVideo(),
+      });
+      webRtcSrs.sendOffer({
+        sender: mySocketId.value,
+        receiver: 'srs',
+      });
+    }
   }
 
   function sendJoin() {
@@ -201,83 +209,6 @@ export const useWebsocket = () => {
     });
   }
 
-  const srsWebRtc = {
-    newWebrtc: ({
-      roomId,
-      sender,
-      videoEl,
-    }: {
-      roomId: string;
-      sender: string;
-      videoEl: HTMLVideoElement;
-    }) => {
-      return new WebRTCClass({
-        maxBitrate: currentMaxBitrate.value,
-        maxFramerate: currentMaxFramerate.value,
-        resolutionRatio: currentResolutionRatio.value,
-        roomId,
-        videoEl,
-        isSRS: true,
-        sender,
-        receiver: 'srs',
-      });
-    },
-    /**
-     * srs的webrtc推流视频通话
-     * 视频发起方是房主,房主发offer给srs
-     */
-    sendOffer: async ({ isPk, sender }: { isPk: boolean; sender: string }) => {
-      console.log('开始srsWebRtc的sendOffer', { sender });
-      try {
-        const ws = networkStore.wsMap.get(roomId.value);
-        if (!ws) return;
-        const rtc = srsWebRtc.newWebrtc({
-          roomId: `${isPk ? `${roomId.value}___pk` : `${roomId.value}`}`,
-          sender,
-          videoEl: createVideo({ appendChild: true }),
-        });
-        canvasVideoStream.value?.getTracks().forEach((track) => {
-          if (rtc && canvasVideoStream.value) {
-            console.log(
-              'srsWebRtc的canvasVideoStream插入track',
-              track.kind,
-              track
-            );
-            rtc.peerConnection?.addTrack(track, canvasVideoStream.value);
-          }
-        });
-        const offerSdp = await rtc.createOffer();
-        if (!offerSdp) {
-          console.error('srsWebRtc的offerSdp为空');
-          return;
-        }
-        await rtc.setLocalDescription(offerSdp!);
-        const myLiveRoom = userStore.userInfo!.live_rooms![0];
-        const answerRes = await fetchRtcV1Publish({
-          api: `/rtc/v1/publish/`,
-          clientip: null,
-          sdp: offerSdp!.sdp!,
-          streamurl: `${myLiveRoom.rtmp_url!}?${
-            SRS_CB_URL_PARAMS.publishKey
-          }=${myLiveRoom.key!}&${SRS_CB_URL_PARAMS.publishType}=${
-            isPk ? LiveRoomTypeEnum.user_pk : LiveRoomTypeEnum.user_srs
-          }`,
-          tid: getRandomString(10),
-        });
-        if (answerRes.data.code !== 0) {
-          console.error('/rtc/v1/publish/拿不到sdp');
-          window.$message.error('/rtc/v1/publish/拿不到sdp');
-          return;
-        }
-        await rtc.setRemoteDescription(
-          new RTCSessionDescription({ type: 'answer', sdp: answerRes.data.sdp })
-        );
-      } catch (error) {
-        console.error('srsWebRtc的sendOffer错误');
-      }
-    },
-  };
-
   function initReceive() {
     const ws = networkStore.wsMap.get(roomId.value);
     if (!ws?.socketIo) return;
@@ -410,64 +341,67 @@ export const useWebsocket = () => {
       WsMsgTypeEnum.nativeWebRtcOffer,
       async (data: WsOfferType['data']) => {
         console.log('收到nativeWebRtcOffer', data);
-        if (data.live_room.type === LiveRoomTypeEnum.user_pk) {
-          if (!isAnchor.value) {
+        if (data.live_room.type === LiveRoomTypeEnum.pk) {
+          if (!route.query.pkKey) {
+            return;
+          }
+          if (data.receiver === mySocketId.value) {
+            console.warn('是发给我的nativeWebRtcOffer');
             const res = await fetchVerifyPkKey({
               liveRoomId: Number(roomId.value),
               key: route.query.pkKey,
             });
             if (res.code === 200 && res.data === true) {
-              dialog.warning({
-                title: '提示',
-                content: '是否加入PK',
-                positiveText: '确认',
-                onPositiveClick() {
-                  async function main() {
-                    // if (data.receiver === mySocketId.value) {
-                    //   console.warn('是发给我的nativeWebRtcOffer');
-                    //   await nativeWebRtc.newWebrtc({
-                    //     // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
-                    //     // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
-                    //     sender: mySocketId.value,
-                    //     receiver: data.sender,
-                    //     videoEl: createNullVideo(),
-                    //   });
-                    //   nativeWebRtc.sendAnswer({
-                    //     isPk: true,
-                    //     sender: mySocketId.value,
-                    //     // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
-                    //     receiver: data.sender,
-                    //     sdp: data.sdp,
-                    //   });
-                    // } else {
-                    //   console.error('不是发给我的nativeWebRtcOffer');
-                    // }
-                  }
-                  return main();
-                },
+              await useTip({
+                content: '是否加入PK?',
+              });
+              const stream = await handleUserMedia({
+                video: true,
+                audio: true,
+              });
+              userStream.value = stream;
+              updateWebRtcMeetingPkConfig({
+                roomId: roomId.value,
+                anchorStream: canvasVideoStream.value,
+                userStream: userStream.value,
+              });
+              webRtcMeetingPk.newWebRtc({
+                // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
+                // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
+                sender: mySocketId.value,
+                receiver: data.sender,
+                videoEl: createNullVideo(),
+              });
+              await webRtcMeetingPk.sendAnswer({
+                sender: mySocketId.value,
+                // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
+                receiver: data.sender,
+                sdp: data.sdp,
               });
             } else {
               window.$message.error('验证pkKey错误!');
             }
+          } else {
+            console.error('不是发给我的nativeWebRtcOffer');
           }
-        } else if (data.live_room.type === LiveRoomTypeEnum.user_wertc_live) {
+        } else if (data.live_room.type === LiveRoomTypeEnum.wertc_live) {
           if (data.receiver === mySocketId.value) {
             console.warn('是发给我的nativeWebRtcOffer');
             if (networkStore.rtcMap.get(data.sender)) {
               return;
             }
-            updateWebRtcOneToManyLiveConfig({
+            updateWebRtcLiveConfig({
               roomId: roomId.value,
               canvasVideoStream: canvasVideoStream.value,
             });
-            webRtcOneToManyLive.newWebRtc({
+            webRtcLive.newWebRtc({
               // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
               // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
               sender: mySocketId.value,
               receiver: data.sender,
               videoEl: createNullVideo(),
             });
-            await webRtcOneToManyLive.sendAnswer({
+            await webRtcLive.sendAnswer({
               sender: mySocketId.value,
               // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
               receiver: data.sender,
@@ -476,27 +410,33 @@ export const useWebsocket = () => {
           } else {
             console.error('不是发给我的nativeWebRtcOffer');
           }
-        } else if (
-          data.live_room.type === LiveRoomTypeEnum.user_wertc_meeting
-        ) {
+        } else if (data.live_room.type === LiveRoomTypeEnum.wertc_meeting_one) {
           if (data.receiver === mySocketId.value) {
             console.warn('是发给我的nativeWebRtcOffer');
-            updateWebRtcManyToManyMeetingConfig({
+            await useTip({
+              content: '是否加入会议?',
+            });
+            const stream = await handleUserMedia({
+              video: true,
+              audio: true,
+            });
+            userStream.value = stream;
+            updateWebRtcMeetingOneConfig({
               roomId: roomId.value,
-              meetingMyStream: meetingMyStream.value,
-              meetingAnchorStream: canvasVideoStream.value,
+              userStream: userStream.value,
+              anchorStream: canvasVideoStream.value,
             });
             if (networkStore.rtcMap.get(data.sender)) {
               return;
             }
-            webRtcManyToManyMeeting.newWebRtc({
+            webRtcMeetingOne.newWebRtc({
               // 因为这里是收到offer,而offer是房主发的,所以此时的data.data.sender是房主;data.data.receiver是接收者;
               // 但是这里的nativeWebRtc的sender,得是自己,不能是data.data.sender,不要混淆
               sender: mySocketId.value,
               receiver: data.sender,
               videoEl: createNullVideo(),
             });
-            await webRtcManyToManyMeeting.sendAnswer({
+            await webRtcMeetingOne.sendAnswer({
               sender: mySocketId.value,
               // data.data.receiver是接收者;我们现在new pc,发送者是自己,接收者肯定是房主,不能是data.data.receiver,因为data.data.receiver是自己
               receiver: data.sender,
@@ -544,7 +484,7 @@ export const useWebsocket = () => {
     // 主播正在直播
     ws.socketIo.on(
       WsMsgTypeEnum.roomLiving,
-      async (data: WsRoomLivingType['data']) => {
+      (data: WsRoomLivingType['data']) => {
         prettierReceiveWsMsg(WsMsgTypeEnum.roomLiving, data);
         roomLiving.value = true;
         if (data.anchor_socket_id) {
@@ -552,33 +492,8 @@ export const useWebsocket = () => {
         }
         if (route.name === routerName.pull) {
           // 当前是拉流页面
-          if (data.live_room?.type === LiveRoomTypeEnum.user_wertc_meeting) {
-            const stream = await handleUserMedia({
-              video: true,
-              audio: true,
-            });
-            meetingMyStream.value = stream;
-            updateWebRtcManyToManyMeetingConfig({
-              roomId: roomId.value,
-              meetingMyStream: stream,
-            });
-            data.socket_list?.forEach((item) => {
-              if (item !== mySocketId.value) {
-                if (networkStore.rtcMap.get(item)) {
-                  return;
-                }
-                webRtcManyToManyMeeting.newWebRtc({
-                  sender: mySocketId.value,
-                  receiver: item,
-                  videoEl: createNullVideo(),
-                });
-                webRtcManyToManyMeeting.sendOffer({
-                  sender: mySocketId.value,
-                  receiver: item,
-                });
-              }
-            });
-          }
+        } else if (route.name === routerName.push) {
+          // 当前是推流页面
         }
       }
     );
@@ -700,17 +615,21 @@ export const useWebsocket = () => {
           console.error('主播没点开始直播');
           return;
         }
+        if (userStore.userInfo?.id === data.join_user_info?.id) {
+          console.error('自己进入直播间,退出');
+          return;
+        }
         if (
           [
             LiveRoomTypeEnum.system,
-            LiveRoomTypeEnum.user_srs,
-            LiveRoomTypeEnum.user_obs,
+            LiveRoomTypeEnum.srs,
+            LiveRoomTypeEnum.obs,
           ].includes(data.live_room.type!)
         ) {
           return;
         }
-        if (data.live_room.type === LiveRoomTypeEnum.user_wertc_live) {
-          updateWebRtcOneToManyLiveConfig({
+        if (data.live_room.type === LiveRoomTypeEnum.wertc_live) {
+          updateWebRtcLiveConfig({
             roomId: roomId.value,
             canvasVideoStream: canvasVideoStream.value,
           });
@@ -719,12 +638,54 @@ export const useWebsocket = () => {
               if (networkStore.rtcMap.get(item)) {
                 return;
               }
-              webRtcOneToManyLive.newWebRtc({
+              webRtcLive.newWebRtc({
+                sender: mySocketId.value,
+                receiver: item,
+                videoEl: createNullVideo(),
+              });
+              webRtcLive.sendOffer({
+                sender: mySocketId.value,
+                receiver: item,
+              });
+            }
+          });
+        } else if (data.live_room.type === LiveRoomTypeEnum.wertc_meeting_one) {
+          updateWebRtcMeetingOneConfig({
+            roomId: roomId.value,
+            anchorStream: canvasVideoStream.value,
+          });
+          data.socket_list?.forEach((item) => {
+            if (item !== mySocketId.value) {
+              if (networkStore.rtcMap.get(item)) {
+                return;
+              }
+              webRtcMeetingOne.newWebRtc({
+                sender: mySocketId.value,
+                receiver: item,
+                videoEl: createNullVideo(),
+              });
+              webRtcMeetingOne.sendOffer({
+                sender: mySocketId.value,
+                receiver: item,
+              });
+            }
+          });
+        } else if (data.live_room.type === LiveRoomTypeEnum.pk) {
+          updateWebRtcMeetingPkConfig({
+            roomId: roomId.value,
+            anchorStream: canvasVideoStream.value,
+          });
+          data.socket_list?.forEach((item) => {
+            if (item !== mySocketId.value) {
+              if (networkStore.rtcMap.get(item)) {
+                return;
+              }
+              webRtcMeetingPk.newWebRtc({
                 sender: mySocketId.value,
                 receiver: item,
                 videoEl: createNullVideo(),
               });
-              webRtcOneToManyLive.sendOffer({
+              webRtcMeetingPk.sendOffer({
                 sender: mySocketId.value,
                 receiver: item,
               });

+ 11 - 14
src/hooks/webrtc/oneToManyLive.ts → src/hooks/webrtc/live.ts

@@ -7,7 +7,7 @@ import { useAppStore } from '@/store/app';
 import { useNetworkStore } from '@/store/network';
 import { WsAnswerType, WsMsgTypeEnum, WsOfferType } from '@/types/websocket';
 
-export const useWebRtcOneToManyLive = () => {
+export const useWebRtcLive = () => {
   const appStore = useAppStore();
   const networkStore = useNetworkStore();
 
@@ -18,15 +18,12 @@ export const useWebRtcOneToManyLive = () => {
   const roomId = ref('');
   const canvasVideoStream = ref<MediaStream>();
 
-  function updateWebRtcOneToManyLiveConfig(data: {
-    roomId;
-    canvasVideoStream;
-  }) {
+  function updateWebRtcLiveConfig(data: { roomId; canvasVideoStream }) {
     roomId.value = data.roomId;
     canvasVideoStream.value = data.canvasVideoStream;
   }
 
-  const webRtcOneToManyLive = {
+  const webRtcLive = {
     newWebRtc: (data: {
       sender: string;
       receiver: string;
@@ -53,7 +50,7 @@ export const useWebRtcOneToManyLive = () => {
       sender: string;
       receiver: string;
     }) => {
-      console.log('开始webRtcOneToManyLive的sendOffer', {
+      console.log('开始webRtcLive的sendOffer', {
         sender,
         receiver,
       });
@@ -65,7 +62,7 @@ export const useWebRtcOneToManyLive = () => {
           canvasVideoStream.value?.getTracks().forEach((track) => {
             if (canvasVideoStream.value) {
               console.log(
-                'webRtcOneToManyLive的canvasVideoStream插入track',
+                'webRtcLive的canvasVideoStream插入track',
                 track.kind,
                 track
               );
@@ -74,7 +71,7 @@ export const useWebRtcOneToManyLive = () => {
           });
           const offerSdp = await rtc.createOffer();
           if (!offerSdp) {
-            console.error('webRtcOneToManyLive的offerSdp为空');
+            console.error('webRtcLive的offerSdp为空');
             return;
           }
           await rtc.setLocalDescription(offerSdp!);
@@ -93,7 +90,7 @@ export const useWebRtcOneToManyLive = () => {
           console.error('rtc不存在');
         }
       } catch (error) {
-        console.error('webRtcOneToManyLive的sendOffer错误');
+        console.error('webRtcLive的sendOffer错误');
       }
     },
     /**
@@ -108,7 +105,7 @@ export const useWebRtcOneToManyLive = () => {
       sender: string;
       receiver: string;
     }) => {
-      console.log('开始webRtcOneToManyLive的sendAnswer', {
+      console.log('开始webRtcLive的sendAnswer', {
         sender,
         receiver,
       });
@@ -120,7 +117,7 @@ export const useWebRtcOneToManyLive = () => {
           await rtc.setRemoteDescription(sdp);
           const answerSdp = await rtc.createAnswer();
           if (!answerSdp) {
-            console.error('webRtcOneToManyLive的answerSdp为空');
+            console.error('webRtcLive的answerSdp为空');
             return;
           }
           await rtc.setLocalDescription(answerSdp);
@@ -138,10 +135,10 @@ export const useWebRtcOneToManyLive = () => {
           console.error('rtc不存在');
         }
       } catch (error) {
-        console.error('webRtcOneToManyLive的sendAnswer错误');
+        console.error('webRtcLive的sendAnswer错误');
       }
     },
   };
 
-  return { updateWebRtcOneToManyLiveConfig, webRtcOneToManyLive };
+  return { updateWebRtcLiveConfig, webRtcLive };
 };

+ 152 - 0
src/hooks/webrtc/meetingOne.ts

@@ -0,0 +1,152 @@
+import { getRandomString } from 'billd-utils';
+import { ref } from 'vue';
+
+import { useRTCParams } from '@/hooks/use-rtcParams';
+import { WebRTCClass } from '@/network/webRTC';
+import { useAppStore } from '@/store/app';
+import { useNetworkStore } from '@/store/network';
+import { WsAnswerType, WsMsgTypeEnum, WsOfferType } from '@/types/websocket';
+
+export const useWebRtcMeetingOne = () => {
+  const appStore = useAppStore();
+  const networkStore = useNetworkStore();
+
+  const { maxBitrate, maxFramerate, resolutionRatio } = useRTCParams();
+  const currentMaxBitrate = ref(maxBitrate.value[3].value);
+  const currentMaxFramerate = ref(maxFramerate.value[2].value);
+  const currentResolutionRatio = ref(resolutionRatio.value[3].value);
+  const roomId = ref('');
+  const anchorStream = ref<MediaStream>();
+  const userStream = ref<MediaStream>();
+
+  function updateWebRtcMeetingOneConfig(data: {
+    roomId;
+    anchorStream;
+    userStream?;
+  }) {
+    roomId.value = data.roomId;
+    anchorStream.value = data.anchorStream;
+    userStream.value = data.userStream;
+  }
+
+  const webRtcMeetingOne = {
+    newWebRtc: (data: {
+      sender: string;
+      receiver: string;
+      videoEl: HTMLVideoElement;
+    }) => {
+      return new WebRTCClass({
+        maxBitrate: currentMaxBitrate.value,
+        maxFramerate: currentMaxFramerate.value,
+        resolutionRatio: currentResolutionRatio.value,
+        isSRS: false,
+        roomId: roomId.value,
+        videoEl: data.videoEl,
+        sender: data.sender,
+        receiver: data.receiver,
+      });
+    },
+    /**
+     * 主播发offer给观众
+     */
+    sendOffer: async ({
+      sender,
+      receiver,
+    }: {
+      sender: string;
+      receiver: string;
+    }) => {
+      console.log('meetingOne的sendOffer', {
+        sender,
+        receiver,
+      });
+      try {
+        const ws = networkStore.wsMap.get(roomId.value);
+        if (!ws) return;
+        const rtc = networkStore.rtcMap.get(receiver);
+        if (rtc) {
+          anchorStream.value?.getTracks().forEach((track) => {
+            if (anchorStream.value) {
+              console.log('meetingOne的sendOffer插入track', track.kind, track);
+              rtc.peerConnection?.addTrack(track, anchorStream.value);
+            }
+          });
+          const offerSdp = await rtc.createOffer();
+          if (!offerSdp) {
+            console.error('meetingOne的offerSdp为空');
+            return;
+          }
+          await rtc.setLocalDescription(offerSdp!);
+          networkStore.wsMap.get(roomId.value)?.send<WsOfferType['data']>({
+            requestId: getRandomString(8),
+            msgType: WsMsgTypeEnum.nativeWebRtcOffer,
+            data: {
+              live_room: appStore.liveRoomInfo!,
+              live_room_id: appStore.liveRoomInfo!.id!,
+              sender,
+              receiver,
+              sdp: offerSdp,
+            },
+          });
+        } else {
+          console.error('rtc不存在');
+        }
+      } catch (error) {
+        console.error('meetingOne的sendOffer错误');
+      }
+    },
+    /**
+     * 观众收到主播的offer,观众回复主播answer
+     */
+    sendAnswer: async ({
+      sdp,
+      sender,
+      receiver,
+    }: {
+      sdp: RTCSessionDescriptionInit;
+      sender: string;
+      receiver: string;
+    }) => {
+      console.log('meetingOne的sendAnswer', {
+        sender,
+        receiver,
+      });
+      try {
+        const ws = networkStore.wsMap.get(roomId.value);
+        if (!ws) return;
+        const rtc = networkStore.rtcMap.get(receiver);
+        if (rtc) {
+          await rtc.setRemoteDescription(sdp);
+          userStream.value?.getTracks().forEach((track) => {
+            if (userStream.value) {
+              console.log('meetingOne的sendAnswer插入track');
+              rtc.peerConnection?.addTrack(track, userStream.value);
+            }
+          });
+          const answerSdp = await rtc.createAnswer();
+          if (!answerSdp) {
+            console.error('meetingOne的answerSdp为空');
+            return;
+          }
+          await rtc.setLocalDescription(answerSdp);
+          networkStore.wsMap.get(roomId.value)?.send<WsAnswerType['data']>({
+            requestId: getRandomString(8),
+            msgType: WsMsgTypeEnum.nativeWebRtcAnswer,
+            data: {
+              live_room_id: Number(roomId.value),
+              sender,
+              receiver,
+              sdp: answerSdp,
+            },
+          });
+        } else {
+          console.error('rtc不存在');
+        }
+      } catch (error) {
+        console.error('meetingOne的sendAnswer错误');
+      }
+    },
+  };
+
+  return { updateWebRtcMeetingOneConfig, webRtcMeetingOne };
+};

+ 152 - 0
src/hooks/webrtc/meetingPk.ts

@@ -0,0 +1,152 @@
+import { getRandomString } from 'billd-utils';
+import { ref } from 'vue';
+
+import { useRTCParams } from '@/hooks/use-rtcParams';
+import { WebRTCClass } from '@/network/webRTC';
+import { useAppStore } from '@/store/app';
+import { useNetworkStore } from '@/store/network';
+import { WsAnswerType, WsMsgTypeEnum, WsOfferType } from '@/types/websocket';
+
+export const useWebRtcMeetingPk = () => {
+  const appStore = useAppStore();
+  const networkStore = useNetworkStore();
+
+  const { maxBitrate, maxFramerate, resolutionRatio } = useRTCParams();
+  const currentMaxBitrate = ref(maxBitrate.value[3].value);
+  const currentMaxFramerate = ref(maxFramerate.value[2].value);
+  const currentResolutionRatio = ref(resolutionRatio.value[3].value);
+  const roomId = ref('');
+  const anchorStream = ref<MediaStream>();
+  const userStream = ref<MediaStream>();
+
+  function updateWebRtcMeetingPkConfig(data: {
+    roomId;
+    anchorStream;
+    userStream?;
+  }) {
+    roomId.value = data.roomId;
+    anchorStream.value = data.anchorStream;
+    userStream.value = data.userStream;
+  }
+
+  const webRtcMeetingPk = {
+    newWebRtc: (data: {
+      sender: string;
+      receiver: string;
+      videoEl: HTMLVideoElement;
+    }) => {
+      return new WebRTCClass({
+        maxBitrate: currentMaxBitrate.value,
+        maxFramerate: currentMaxFramerate.value,
+        resolutionRatio: currentResolutionRatio.value,
+        isSRS: false,
+        roomId: roomId.value,
+        videoEl: data.videoEl,
+        sender: data.sender,
+        receiver: data.receiver,
+      });
+    },
+    /**
+     * 主播发offer给观众
+     */
+    sendOffer: async ({
+      sender,
+      receiver,
+    }: {
+      sender: string;
+      receiver: string;
+    }) => {
+      console.log('meetingOne的sendOffer', {
+        sender,
+        receiver,
+      });
+      try {
+        const ws = networkStore.wsMap.get(roomId.value);
+        if (!ws) return;
+        const rtc = networkStore.rtcMap.get(receiver);
+        if (rtc) {
+          anchorStream.value?.getTracks().forEach((track) => {
+            if (anchorStream.value) {
+              console.log('meetingOne的sendOffer插入track', track.kind, track);
+              rtc.peerConnection?.addTrack(track, anchorStream.value);
+            }
+          });
+          const offerSdp = await rtc.createOffer();
+          if (!offerSdp) {
+            console.error('meetingOne的offerSdp为空');
+            return;
+          }
+          await rtc.setLocalDescription(offerSdp!);
+          networkStore.wsMap.get(roomId.value)?.send<WsOfferType['data']>({
+            requestId: getRandomString(8),
+            msgType: WsMsgTypeEnum.nativeWebRtcOffer,
+            data: {
+              live_room: appStore.liveRoomInfo!,
+              live_room_id: appStore.liveRoomInfo!.id!,
+              sender,
+              receiver,
+              sdp: offerSdp,
+            },
+          });
+        } else {
+          console.error('rtc不存在');
+        }
+      } catch (error) {
+        console.error('meetingOne的sendOffer错误');
+      }
+    },
+    /**
+     * 观众收到主播的offer,观众回复主播answer
+     */
+    sendAnswer: async ({
+      sdp,
+      sender,
+      receiver,
+    }: {
+      sdp: RTCSessionDescriptionInit;
+      sender: string;
+      receiver: string;
+    }) => {
+      console.log('meetingOne的sendAnswer', {
+        sender,
+        receiver,
+      });
+      try {
+        const ws = networkStore.wsMap.get(roomId.value);
+        if (!ws) return;
+        const rtc = networkStore.rtcMap.get(receiver);
+        if (rtc) {
+          await rtc.setRemoteDescription(sdp);
+          userStream.value?.getTracks().forEach((track) => {
+            if (userStream.value) {
+              console.log('meetingOne的sendAnswer插入track');
+              rtc.peerConnection?.addTrack(track, userStream.value);
+            }
+          });
+          const answerSdp = await rtc.createAnswer();
+          if (!answerSdp) {
+            console.error('meetingOne的answerSdp为空');
+            return;
+          }
+          await rtc.setLocalDescription(answerSdp);
+          networkStore.wsMap.get(roomId.value)?.send<WsAnswerType['data']>({
+            requestId: getRandomString(8),
+            msgType: WsMsgTypeEnum.nativeWebRtcAnswer,
+            data: {
+              live_room_id: Number(roomId.value),
+              sender,
+              receiver,
+              sdp: answerSdp,
+            },
+          });
+        } else {
+          console.error('rtc不存在');
+        }
+      } catch (error) {
+        console.error('meetingOne的sendAnswer错误');
+      }
+    },
+  };
+
+  return { updateWebRtcMeetingPkConfig, webRtcMeetingPk };
+};

+ 0 - 0
src/hooks/webrtc/manyToManyMeeting.ts → src/hooks/webrtc/meetingTwo.ts


+ 123 - 0
src/hooks/webrtc/srs.ts

@@ -0,0 +1,123 @@
+import { getRandomString } from 'billd-utils';
+import { ref } from 'vue';
+
+import { fetchRtcV1Publish } from '@/api/srs';
+import { SRS_CB_URL_PARAMS } from '@/constant';
+import { useRTCParams } from '@/hooks/use-rtcParams';
+import { WebRTCClass } from '@/network/webRTC';
+import { useAppStore } from '@/store/app';
+import { useNetworkStore } from '@/store/network';
+import { useUserStore } from '@/store/user';
+import { LiveRoomTypeEnum } from '@/types/ILiveRoom';
+
+export const useWebRtcSrs = () => {
+  const userStore = useUserStore();
+  const appStore = useAppStore();
+  const networkStore = useNetworkStore();
+
+  const { maxBitrate, maxFramerate, resolutionRatio } = useRTCParams();
+  const currentMaxBitrate = ref(maxBitrate.value[3].value);
+  const currentMaxFramerate = ref(maxFramerate.value[2].value);
+  const currentResolutionRatio = ref(resolutionRatio.value[3].value);
+  const isPk = ref(false);
+  const roomId = ref('');
+  const canvasVideoStream = ref<MediaStream>();
+
+  function updateWebRtcSrsConfig(data: { isPk; roomId; canvasVideoStream }) {
+    isPk.value = data.isPk;
+    roomId.value = data.roomId;
+    canvasVideoStream.value = data.canvasVideoStream;
+  }
+
+  const webRtcSrs = {
+    newWebRtc: (data: {
+      sender: string;
+      receiver: string;
+      videoEl: HTMLVideoElement;
+    }) => {
+      return new WebRTCClass({
+        maxBitrate: currentMaxBitrate.value,
+        maxFramerate: currentMaxFramerate.value,
+        resolutionRatio: currentResolutionRatio.value,
+        isSRS: false,
+        roomId: roomId.value,
+        videoEl: data.videoEl,
+        sender: data.sender,
+        receiver: data.receiver,
+      });
+    },
+    /**
+     * 主播发offer给观众
+     */
+    sendOffer: async ({
+      sender,
+      receiver,
+    }: {
+      sender: string;
+      receiver: string;
+    }) => {
+      console.log('开始webRtcSrs的sendOffer', {
+        sender,
+        receiver,
+      });
+      try {
+        const ws = networkStore.wsMap.get(roomId.value);
+        if (!ws) return;
+        const rtc = networkStore.rtcMap.get(receiver);
+        if (rtc) {
+          canvasVideoStream.value?.getTracks().forEach((track) => {
+            if (canvasVideoStream.value) {
+              console.log(
+                'webRtcSrs的canvasVideoStream插入track',
+                track.kind,
+                track
+              );
+              rtc.peerConnection?.addTrack(track, canvasVideoStream.value);
+            }
+          });
+          const offerSdp = await rtc.createOffer();
+          if (!offerSdp) {
+            console.error('webRtcSrs的offerSdp为空');
+            window.$message.error('webRtcSrs的offerSdp为空');
+            return;
+          }
+          await rtc.setLocalDescription(offerSdp!);
+          const liveRooms = userStore.userInfo?.live_rooms;
+          const myLiveRoom = liveRooms?.[0];
+          if (!myLiveRoom) {
+            window.$message.error('你没有开通直播间');
+            return;
+          }
+          const answerRes = await fetchRtcV1Publish({
+            api: `/rtc/v1/publish/`,
+            clientip: null,
+            sdp: offerSdp.sdp!,
+            streamurl: `${myLiveRoom.rtmp_url!}?${
+              SRS_CB_URL_PARAMS.publishKey
+            }=${myLiveRoom.key!}&${SRS_CB_URL_PARAMS.publishType}=${
+              isPk.value ? LiveRoomTypeEnum.pk : LiveRoomTypeEnum.srs
+            }`,
+            tid: getRandomString(10),
+          });
+          if (answerRes.data.code !== 0) {
+            console.error('/rtc/v1/publish/拿不到sdp');
+            window.$message.error('/rtc/v1/publish/拿不到sdp');
+            return;
+          }
+          await rtc.setRemoteDescription(
+            new RTCSessionDescription({
+              type: 'answer',
+              sdp: answerRes.data.sdp,
+            })
+          );
+        } else {
+          console.error('rtc不存在');
+        }
+      } catch (error) {
+        console.error('webRtcSrs的sendOffer错误');
+      }
+    },
+  };
+
+  return { updateWebRtcSrsConfig, webRtcSrs };
+};

+ 0 - 11
src/interface.ts

@@ -534,17 +534,6 @@ export enum DanmuMsgTypeEnum {
   reward,
 }
 
-export interface IUpdateJoinInfo {
-  socket_id: string;
-  is_anchor: boolean;
-  user_info?: IUser;
-  data: {
-    live_room_id: number;
-    track?: { audio: number; video: number };
-    rtmp_url?: string;
-  };
-}
-
 export interface ILiveUser {
   // id: string;
   // rooms?: string[];

+ 5 - 7
src/layout/pc/head/index.vue

@@ -247,35 +247,33 @@
             <div class="list">
               <a
                 class="item"
-                @click.prevent="handleStartLive(LiveRoomTypeEnum.user_srs)"
+                @click.prevent="handleStartLive(LiveRoomTypeEnum.srs)"
               >
                 <div class="txt">{{ t('layout.srsLive') }}</div>
               </a>
               <a
                 class="item"
-                @click.prevent="
-                  handleStartLive(LiveRoomTypeEnum.user_wertc_live)
-                "
+                @click.prevent="handleStartLive(LiveRoomTypeEnum.wertc_live)"
               >
                 <div class="txt">{{ t('layout.webrtcLive') }}</div>
               </a>
               <a
                 class="item"
                 @click.prevent="
-                  handleStartLive(LiveRoomTypeEnum.user_wertc_meeting)
+                  handleStartLive(LiveRoomTypeEnum.wertc_meeting_one)
                 "
               >
                 <div class="txt">{{ t('layout.webrtcMeeting') }}</div>
               </a>
               <a
                 class="item"
-                @click.prevent="handleStartLive(LiveRoomTypeEnum.user_msr)"
+                @click.prevent="handleStartLive(LiveRoomTypeEnum.msr)"
               >
                 <div class="txt">{{ t('layout.msrLive') }}</div>
               </a>
               <a
                 class="item"
-                @click.prevent="handleStartLive(LiveRoomTypeEnum.user_pk)"
+                @click.prevent="handleStartLive(LiveRoomTypeEnum.pk)"
               >
                 <div class="txt">{{ t('layout.pkLive') }}</div>
               </a>

+ 23 - 6
src/network/webRTC.ts

@@ -115,12 +115,29 @@ export class WebRTCClass {
     this.createPeerConnection();
   }
 
-  prettierLog = (data: { msg: string; type?: 'log' | 'warn' | 'error' }) => {
+  prettierLog = (data: {
+    msg: string;
+    type?: 'log' | 'warn' | 'error' | 'success';
+  }) => {
     const { msg, type } = data;
-    console[type || 'log'](
-      `【WebRTCClass】${new Date().toLocaleString()},房间id:${this.roomId}`,
-      msg
-    );
+    if (type === 'success') {
+      console.log(
+        `%c ` +
+          `【WebRTCClass】${new Date().toLocaleString()},房间id:${
+            this.roomId
+          }` +
+          ` %c ${msg} ` +
+          `%c`,
+        'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
+        'background:#41b883 ; padding: 1px; border-radius: 0 3px 3px 0;  color: #fff',
+        'background:transparent'
+      );
+    } else {
+      console[type || 'log'](
+        `【WebRTCClass】${new Date().toLocaleString()},房间id:${this.roomId}`,
+        msg
+      );
+    }
   };
 
   /** 设置分辨率 */
@@ -426,7 +443,7 @@ export class WebRTCClass {
           });
           this.prettierLog({
             msg: 'webrtc连接成功!',
-            type: 'warn',
+            type: 'success',
           });
           console.log('sender', this.sender, 'receiver', this.receiver);
           this.update();

+ 4 - 1
src/network/webSocket.ts

@@ -41,7 +41,9 @@ export class WebSocketClass {
     this.url = data.url;
     this.socketIo = io(data.url, {
       transports: ['websocket'],
-      forceBase64: false,
+      // forceBase64: true,
+      // WARN 用了自定义parser之后,msrblob里面的blob文件错误
+      // parser: customParser,
     });
     this.update();
   }
@@ -75,6 +77,7 @@ export class WebSocketClass {
       user_token: userStore.token || undefined,
       data: data || {},
     };
+    // const binary = stringToArrayBuffer(JSON.stringify(sendData));
     this.socketIo?.emit(msgType, sendData);
   };
 

+ 11 - 9
src/types/ILiveRoom.ts

@@ -14,21 +14,23 @@ export enum LiveRoomTypeEnum {
   /** 系统推流 */
   system,
   /** 主播使用srs推流 */
-  user_srs,
+  srs,
   /** 主播使用obs/ffmpeg推流 */
-  user_obs,
+  obs,
   /** 主播使用webrtc推流,直播 */
-  user_wertc_live,
-  /** 主播使用webrtc推流,会议 */
-  user_wertc_meeting,
+  wertc_live,
+  /** 主播使用webrtc推流,会议,实现一 */
+  wertc_meeting_one,
+  /** 主播使用webrtc推流,会议,实现二 */
+  wertc_meeting_two,
   /** 主播使用msr推流 */
-  user_msr,
+  msr,
   /** 主播打pk */
-  user_pk,
+  pk,
   /** 主播使用腾讯云css推流 */
-  user_tencent_css,
+  tencent_css,
   /** 主播使用腾讯云css推流打pk */
-  user_tencent_css_pk,
+  tencent_css_pk,
 }
 
 /** 拉流是否需要鉴权 */

+ 6 - 0
src/utils/index.ts

@@ -2,6 +2,12 @@
 import { computeBox, getRangeRandom } from 'billd-utils';
 import sparkMD5 from 'spark-md5';
 
+export function stringToArrayBuffer(str: string) {
+  const encoder = new TextEncoder(); // 默认是'utf-8'编码
+  const uint8Array = encoder.encode(str);
+  return uint8Array.buffer;
+}
+
 export function createNullVideo() {
   const videoEl = document.createElement('video');
   videoEl.autoplay = true;

+ 2 - 2
src/views/account/index.vue

@@ -118,13 +118,13 @@ function openLiveRoom() {
   }
   const url = router.resolve({
     name: routerName.push,
-    query: { liveType: LiveRoomTypeEnum.user_srs },
+    query: { liveType: LiveRoomTypeEnum.srs },
   });
   openToTarget(url.href);
 }
 
 function handleUrl(data: { url: string; token: string }) {
-  return `${data.url}?${SRS_CB_URL_PARAMS.publishKey}=${data.token}&${SRS_CB_URL_PARAMS.publishType}=${LiveRoomTypeEnum.user_obs}`;
+  return `${data.url}?${SRS_CB_URL_PARAMS.publishKey}=${data.token}&${SRS_CB_URL_PARAMS.publishType}=${LiveRoomTypeEnum.obs}`;
 }
 
 async function handleUpdateKey() {

+ 1 - 1
src/views/h5/room/index.vue

@@ -345,7 +345,7 @@ async function getLiveRoomInfo() {
     const res = await fetchFindLiveRoom(roomId.value);
     if (res.code === 200) {
       appStore.setLiveRoomInfo(res.data);
-      if (res.data.type === LiveRoomTypeEnum.user_wertc_live) {
+      if (res.data.type === LiveRoomTypeEnum.wertc_live) {
         autoplayVal.value = true;
         showPlayBtn.value = false;
       } else {

+ 1 - 1
src/views/home/index.vue

@@ -310,7 +310,7 @@ function playLive(item: ILive) {
 
 function changeLiveRoom(item: ILive) {
   if (item.id === currentLiveRoom.value?.id) return;
-  if (item.live_room?.type !== LiveRoomTypeEnum.user_wertc_live) {
+  if (item.live_room?.type !== LiveRoomTypeEnum.wertc_live) {
     appStore.setLiveLine(LiveLineEnum.hls);
   }
   appStore.setPlay(true);

+ 61 - 18
src/views/pull/index.vue

@@ -69,6 +69,12 @@
           @click="handlePk"
         >
           <div class="top">
+            <div
+              class="item"
+              v-if="NODE_ENV === 'development'"
+            >
+              {{ liveRoomTypeEnumMap[appStore.liveRoomInfo?.type || ''] }}
+            </div>
             <div class="item">666人看过</div>
             <div class="item">666点赞</div>
             <div class="item">当前在线:{{ liveUserList.length }}人</div>
@@ -128,11 +134,11 @@
           <div
             ref="remoteVideoRef"
             class="media-list"
-            :class="{ item: appStore.allTrack.length > 1 }"
           ></div>
           <VideoControls
             :resolution="videoHeight"
             @refresh="handleRefresh"
+            @full-screen="handleFullScreen"
           ></VideoControls>
         </div>
       </div>
@@ -410,9 +416,14 @@ import {
 } from '@/api/giftRecord';
 import { fetchGoodsList } from '@/api/goods';
 import { fetchGetWsMessageList } from '@/api/wsMessage';
-import { MODULE_CONFIG_SWITCH, QINIU_LIVE } from '@/constant';
+import {
+  MODULE_CONFIG_SWITCH,
+  QINIU_LIVE,
+  liveRoomTypeEnumMap,
+} from '@/constant';
 import { emojiArray } from '@/emoji';
 import { commentAuthTip, loginTip } from '@/hooks/use-login';
+import { useFullScreen } from '@/hooks/use-play';
 import { usePull } from '@/hooks/use-pull';
 import { useUpload } from '@/hooks/use-upload';
 import {
@@ -429,6 +440,7 @@ import router, { routerName } from '@/router';
 import { useAppStore } from '@/store/app';
 import { useNetworkStore } from '@/store/network';
 import { useUserStore } from '@/store/user';
+import { LiveRoomTypeEnum } from '@/types/ILiveRoom';
 import { WsDisableSpeakingType, WsMsgTypeEnum } from '@/types/websocket';
 import {
   formatMoney,
@@ -465,7 +477,6 @@ const containerRef = ref<HTMLDivElement>();
 const uploadRef = ref<HTMLInputElement>();
 const danmuIptRef = ref<HTMLTextAreaElement>();
 const {
-  videoWrapRef,
   initPull,
   closeWs,
   closeRtc,
@@ -473,6 +484,8 @@ const {
   sendDanmu,
   handlePlay,
   handleSendGetLiveUser,
+  videoWrapRef,
+  remoteVideo,
   danmuMsgType,
   msgIsFile,
   mySocketId,
@@ -608,21 +621,43 @@ watch(
       roomLiving.value = true;
       videoLoading.value = false;
     }
-    newVal.forEach((item) => {
-      const rect = videoWrapRef.value?.getBoundingClientRect();
-      if (rect) {
-        videoFullBox({
-          wrapSize: {
-            width: rect.width,
-            height: rect.height,
-          },
-          videoEl: item.videoEl,
-          videoResize: ({ w, h }) => {
-            videoHeight.value = `${w}x${h}`;
-          },
-        });
-        remoteVideoRef.value?.appendChild(item.videoEl);
-      }
+    if (
+      appStore.liveRoomInfo?.type === LiveRoomTypeEnum.wertc_meeting_one ||
+      appStore.liveRoomInfo?.type === LiveRoomTypeEnum.wertc_live ||
+      appStore.liveRoomInfo?.type === LiveRoomTypeEnum.pk
+    ) {
+      newVal.forEach((item) => {
+        if (appStore.allTrack.find((v) => v.mediaName === item.receiver)) {
+          return;
+        }
+        const rect = videoWrapRef.value?.getBoundingClientRect();
+        if (rect) {
+          videoFullBox({
+            wrapSize: {
+              width: rect.width,
+              height: rect.height,
+            },
+            videoEl: item.videoEl,
+            videoResize: ({ w, h }) => {
+              videoHeight.value = `${w}x${h}`;
+            },
+          });
+          remoteVideoRef.value?.appendChild(item.videoEl);
+        }
+      });
+    }
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+
+watch(
+  () => remoteVideo.value,
+  (newval) => {
+    newval.forEach((videoEl) => {
+      remoteVideoRef.value?.appendChild(videoEl);
     });
   },
   {
@@ -786,6 +821,14 @@ async function handlePay(item: IGoods) {
   getGiftGroupList();
 }
 
+function handleFullScreen() {
+  const el = remoteVideoRef.value?.childNodes[0];
+  // @ts-ignore
+  if (el?.tagName?.toLowerCase() === 'video') {
+    useFullScreen(el);
+  }
+}
+
 function handleRefresh() {
   if (appStore.liveRoomInfo) {
     handlePlay(appStore.liveRoomInfo);

+ 14 - 13
src/views/push/index.vue

@@ -94,13 +94,19 @@
         </div>
         <div class="other">
           <div class="top">
-            <span class="item">
+            <div class="item">
               <i class="ico"></i>
               <span>
                 正在观看:
                 {{ liveUserList.length }}
               </span>
-            </span>
+            </div>
+            <div
+              class="item"
+              v-if="NODE_ENV === 'development'"
+            >
+              {{ liveRoomTypeEnumMap[appStore.liveRoomInfo?.type || ''] }}
+            </div>
           </div>
           <div class="bottom">
             <n-button
@@ -387,7 +393,7 @@ import {
 } from 'vue';
 import { useRoute } from 'vue-router';
 
-import { QINIU_LIVE, mediaTypeEnumMap } from '@/constant';
+import { QINIU_LIVE, liveRoomTypeEnumMap, mediaTypeEnumMap } from '@/constant';
 import { emojiArray } from '@/emoji';
 import { commentAuthTip, loginTip } from '@/hooks/use-login';
 import { usePush } from '@/hooks/use-push';
@@ -489,8 +495,8 @@ const msrMaxDelay = ref(1000 * 5);
 
 watch(
   () => roomLiving.value,
-  () => {
-    if (!roomLiving.value) {
+  (newval) => {
+    if (!newval) {
       handleEndLive();
       showNoLiveTipModalCpt.value = true;
     }
@@ -500,7 +506,7 @@ watch(
 watch(
   () => currentMaxBitrate.value,
   () => {
-    if (liveType === LiveRoomTypeEnum.user_msr) {
+    if (liveType === LiveRoomTypeEnum.msr) {
       const stream = pushCanvasRef.value!.captureStream();
       const audioTrack = webaudioVideo
         // @ts-ignore
@@ -551,6 +557,7 @@ watch(
       if (appStore.allTrack.find((v) => v.mediaName === item.receiver)) {
         return;
       }
+      console.log('addMediaOkaddMediaOk', item.receiver);
       addMediaOk({
         id: getRandomEnglishString(6),
         openEye: true,
@@ -928,7 +935,7 @@ function handleStartLive() {
     msrDelay: msrDelay.value,
     msrMaxDelay: 5000,
   });
-  if (liveType === LiveRoomTypeEnum.user_msr) {
+  if (liveType === LiveRoomTypeEnum.msr) {
     const stream = pushCanvasRef.value!.captureStream();
     // @ts-ignore
     const audioTrack = webaudioVideo.value!.captureStream().getAudioTracks()[0];
@@ -1680,12 +1687,6 @@ async function addMediaOk(val: AppRootState['allTrack'][0]) {
     cacheStore.setResourceList(res);
     console.log('获取摄像头成功');
   } else if (val.type === MediaTypeEnum.pk) {
-    // const event = await handleUserMedia({
-    //   video: {
-    //     deviceId: val.deviceId,
-    //   },
-    //   audio: false,
-    // });
     const event = val.stream;
     if (!event) return;
     const videoTrack: AppRootState['allTrack'][0] = {