Trtccom.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <template>
  2. <div class="trtccom" v-if="show">
  3. <Device @switchDevice="switchDevice" @canUseDevice="canUseDevice" />
  4. <div class="local" :class="{disabledlocal:role=='customer'}" id="local" v-if="isJoined"></div>
  5. <template v-if="isJoined && invitedRemoteStreams.length > 0">
  6. <div class="local" :data-role="item.userId_" :class="{disabledlocal:item.userId_.indexOf('customer')>-1}" v-for="(item, i) in invitedRemoteStreams" :id="item.getId()" :key="i"></div>
  7. </template>
  8. <div class="videoBox userVideo" v-show="props.videoMuted">
  9. <img :src="require('@/assets/images/rtcLive/avatar_small@2x.png')" alt="" />
  10. <img class="loadingTip" :src="require('@/assets/images/rtcLive/loading@2x.png')" alt="" />
  11. </div>
  12. </div>
  13. </template>
  14. <script setup>
  15. import TRTC, { Client, LocalStream } from "trtc-js-sdk";
  16. import { Dialog } from "@/global_components/";
  17. import { ref, computed, watch, defineEmits, defineProps, nextTick } from "vue";
  18. import Device from "./trtc/Device";
  19. import { useStore } from "vuex";
  20. import browser from "@/utils/browser";
  21. const emit = defineEmits(["audioMuted", "videoMuted"]);
  22. const store = useStore();
  23. const show = ref(false);
  24. const invitedRemoteStreams = ref([]);
  25. const role = ref(browser.getURLParam("role"));
  26. const isJoined = computed(() => store.getters["rtc/isJoined"]);
  27. const isPublished = computed(() => store.getters["rtc/isPublished"]);
  28. const userSig = computed(() => store.getters["rtc/userSig"]);
  29. const initParamsStates = computed(
  30. () => !!(store.getters["rtc/sdkAppId"] && store.getters["rtc/secretKey"] && store.getters["rtc/roomId"] && store.getters["rtc/userId"])
  31. );
  32. let localClient = "";
  33. let localStream = "";
  34. let shareClient = "";
  35. const props = defineProps({
  36. audioMuted: {
  37. default: false,
  38. },
  39. videoMuted: {
  40. default: false,
  41. },
  42. });
  43. watch(
  44. () => props.audioMuted,
  45. () => {
  46. if (props.audioMuted) {
  47. localStream.muteAudio();
  48. } else {
  49. localStream.unmuteAudio();
  50. }
  51. }
  52. );
  53. watch(
  54. () => props.videoMuted,
  55. () => {
  56. if (props.videoMuted) {
  57. localStream.muteVideo();
  58. } else {
  59. localStream.unmuteVideo();
  60. }
  61. }
  62. );
  63. watch(
  64. () => isJoined.value,
  65. () => {
  66. if (!isJoined.value) {
  67. handleLeave();
  68. }
  69. }
  70. );
  71. TRTC.checkSystemRequirements().then((checkResult) => {
  72. if (!checkResult.result) {
  73. Dialog.toast({ content: `您的設備不支持音視頻通訊`, type: "error" });
  74. } else {
  75. show.value = true;
  76. }
  77. });
  78. async function createLocalStream() {
  79. let isLeader = role.value == 'leader'
  80. try {
  81. localStream = TRTC.createStream({
  82. userId: store.getters["rtc/userId"],
  83. audio: true,
  84. video: isLeader,
  85. cameraId: isLeader ? store.getters["rtc/videoDeviceId"] : '',
  86. microphoneId: store.getters["rtc/audioDeviceId"],
  87. });
  88. isLeader && localStream.setVideoProfile("480p");
  89. await localStream.initialize();
  90. } catch (error) {}
  91. }
  92. async function handleJoin() {
  93. if (!initParamsStates.value) {
  94. return;
  95. }
  96. try {
  97. localClient = TRTC.createClient({
  98. mode: "rtc",
  99. sdkAppId: parseInt(store.getters["rtc/sdkAppId"], 10),
  100. userId: store.getters["rtc/userId"],
  101. userSig: userSig.value,
  102. });
  103. installEventHandlers();
  104. await localClient.join({ roomId: parseInt(store.getters["rtc/roomId"], 10) });
  105. store.commit("rtc/setIsJoined", true);
  106. // inviteLink.value = store.commit("rtc/createShareLink");
  107. } catch (error) {
  108. console.error(error, "error-----------");
  109. }
  110. await createLocalStream();
  111. await handlePublish();
  112. localStream
  113. .play("local")
  114. .then(() => {
  115. // addLocalControlView();
  116. })
  117. .catch((e) => {});
  118. }
  119. async function handlePublish() {
  120. if (!isJoined.value) {
  121. return;
  122. }
  123. if (isPublished.value) {
  124. return;
  125. }
  126. try {
  127. await localClient.publish(localStream);
  128. store.commit("rtc/setIsPublished", true);
  129. } catch (error) {}
  130. }
  131. async function handleStartShare() {
  132. shareClient = new ShareClient({
  133. sdkAppId: parseInt(store.getters["rtc/sdkAppId"], 10),
  134. userId: `share${store.getters["rtc/userId"]}`,
  135. roomId: parseInt(store.getters["rtc/roomId"], 10),
  136. secretKey: store.getters["rtc/secretKey"],
  137. });
  138. try {
  139. await shareClient.join();
  140. await shareClient.publish();
  141. console.log("Start share screen success");
  142. store.isShared = true;
  143. } catch (error) {
  144. console.error(`Start share error: ${error.message_}`);
  145. }
  146. }
  147. async function handleUnpublish() {
  148. if (!isJoined.value) {
  149. return;
  150. }
  151. if (!isPublished.value) {
  152. return;
  153. }
  154. try {
  155. await localClient.unpublish(localStream);
  156. store.commit("rtc/setIsPublished", false);
  157. } catch (error) {}
  158. }
  159. async function handleLeave() {
  160. if (isPublished.value) {
  161. await handleUnpublish();
  162. }
  163. try {
  164. uninstallEventHandlers();
  165. await localClient.leave();
  166. if (localStream) {
  167. localStream.stop();
  168. localStream.close();
  169. localStream = null;
  170. }
  171. } catch (error) {}
  172. }
  173. function installEventHandlers() {
  174. if (!localClient) {
  175. return;
  176. }
  177. localClient.on("error", handleError);
  178. localClient.on("client-banned", handleBanned);
  179. localClient.on("peer-join", handlePeerJoin);
  180. localClient.on("peer-leave", handlePeerLeave);
  181. localClient.on("stream-added", handleStreamAdded);
  182. localClient.on("stream-subscribed", handleStreamSubscribed);
  183. localClient.on("stream-removed", handleStreamRemoved);
  184. localClient.on("stream-updated", handleStreamUpdated);
  185. localClient.on("mute-video", handleMuteVideo);
  186. localClient.on("mute-audio", handleMuteAudio);
  187. localClient.on("unmute-video", handleUnmuteVideo);
  188. localClient.on("unmute-audio", handleUnmuteAudio);
  189. }
  190. function uninstallEventHandlers() {
  191. if (!localClient) {
  192. return;
  193. }
  194. localClient.off("error", handleError);
  195. localClient.off("error", handleError);
  196. localClient.off("client-banned", handleBanned);
  197. localClient.off("peer-join", handlePeerJoin);
  198. localClient.off("peer-leave", handlePeerLeave);
  199. localClient.off("stream-added", handleStreamAdded);
  200. localClient.off("stream-subscribed", handleStreamSubscribed);
  201. localClient.off("stream-removed", handleStreamRemoved);
  202. localClient.off("stream-updated", handleStreamUpdated);
  203. localClient.off("mute-video", handleMuteVideo);
  204. localClient.off("mute-audio", handleMuteAudio);
  205. localClient.off("unmute-video", handleUnmuteVideo);
  206. localClient.off("unmute-audio", handleUnmuteAudio);
  207. }
  208. function handleMuteVideo(event) {
  209. console.log(`[${event.userId}] mute video`);
  210. }
  211. function handleMuteAudio(event) {
  212. console.log(`[${event.userId}] mute audio`);
  213. }
  214. function handleUnmuteVideo(event) {
  215. console.log(`[${event.userId}] unmute video`);
  216. }
  217. function handleUnmuteAudio(event) {
  218. console.log(`[${event.userId}] unmute audio`);
  219. }
  220. function handleError(error) {
  221. console.log(`LocalClient error: ${error.message_}`);
  222. }
  223. function handleBanned(error) {
  224. console.log(`Client has been banned for ${error.message_}`);
  225. }
  226. function handlePeerJoin(event) {
  227. const { userId } = event;
  228. if (userId !== "local-screen") {
  229. console.log(`Peer Client [${userId}] joined`);
  230. }
  231. }
  232. function handlePeerLeave(event) {
  233. const { userId } = event;
  234. if (userId !== "local-screen") {
  235. console.log(`[${userId}] leave`);
  236. }
  237. }
  238. function handleStreamAdded(event) {
  239. const remoteStream = event.stream;
  240. const id = remoteStream.getId();
  241. const userId = remoteStream.getUserId();
  242. console.log(remoteStream, "-------------remoteStream");
  243. if (remoteStream.getUserId() === store.getters["rtc/userId"]) {
  244. // don't need to screen shared by us
  245. localClient.unsubscribe(remoteStream).catch((error) => {
  246. console.error(`unsubscribe failed: ${error.message_}`);
  247. });
  248. } else {
  249. console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
  250. localClient.subscribe(remoteStream).catch((error) => {
  251. console.error(`subscribe failed: ${error.message_}`);
  252. });
  253. }
  254. }
  255. async function handleStreamSubscribed(event) {
  256. const remoteStream = event.stream;
  257. if (remoteStream.userId_ == store.getters["rtc/userId"]) {
  258. return
  259. }
  260. if (!invitedRemoteStreams.value.some(item=>item.userId_==remoteStream.userId_)) {
  261. invitedRemoteStreams.value.push(remoteStream);
  262. }
  263. console.log(invitedRemoteStreams.value, "invitedRemoteStreams.value");
  264. await nextTick();
  265. remoteStream
  266. .play(remoteStream.getId())
  267. .then(() => {
  268. console.log(`RemoteStream play success`, 88888888888888888888);
  269. })
  270. .catch((error) => {
  271. console.log(`RemoteStream play failed: error: ${error.message_}`);
  272. });
  273. // const remoteStream = event.stream;
  274. // const userId = remoteStream.getUserId();
  275. // console.log(`RemoteStream subscribed: [${userId}]`);
  276. }
  277. function handleStreamRemoved(event) {
  278. const remoteStream = event.stream;
  279. const userId = remoteStream.getUserId();
  280. console.log(`RemoteStream removed: [${userId}]`);
  281. }
  282. function handleStreamUpdated(event) {
  283. const remoteStream = event.stream;
  284. const userId = remoteStream.getUserId();
  285. console.log(`RemoteStream updated: [${userId}] audio:${remoteStream.hasAudio()} video:${remoteStream.hasVideo()}`);
  286. }
  287. let switchDevice = async ({ videoId, audioId }) => {
  288. if (!isJoined.value) {
  289. return;
  290. }
  291. if (videoId) {
  292. try {
  293. await localStream.switchDevice("video", videoId);
  294. } catch (error) {}
  295. }
  296. if (audioId) {
  297. try {
  298. await localStream.switchDevice("audio", audioId);
  299. } catch (error) {}
  300. }
  301. };
  302. let canUseDevice = () => {
  303. console.log("可用");
  304. handleJoin();
  305. };
  306. </script>
  307. <style lang="scss" scoped>
  308. .trtccom {
  309. .local {
  310. width: 70px;
  311. height: 70px;
  312. position: fixed;
  313. z-index: 9999;
  314. top: 0.69rem;
  315. left: 0.53rem;
  316. border-radius: 50%;
  317. overflow: hidden;
  318. background: url(~@/assets/images/rtcLive/avatar_small@2x.png) center center no-repeat;
  319. }
  320. .videoBox {
  321. width: 72px;
  322. height: 72px;
  323. top: 0.67rem;
  324. left: 0.52rem;
  325. position: fixed;
  326. z-index: 99999;
  327. .loadingTip {
  328. position: absolute;
  329. z-index: 101;
  330. left: 0;
  331. top: 0;
  332. bottom: 0;
  333. right: 0;
  334. animation: Rotate 1.5s infinite;
  335. @keyframes Rotate {
  336. 0% {
  337. transform: rotate(0deg);
  338. }
  339. 100% {
  340. transform: rotate(360deg);
  341. }
  342. }
  343. }
  344. > img {
  345. width: 1.94rem;
  346. height: 1.94rem;
  347. }
  348. }
  349. .disabledlocal {
  350. opacity: 0 !important;
  351. visibility: hidden !important;
  352. }
  353. }
  354. </style>