runtime.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. import { ElMessage, ElMessageBox } from 'element-plus';
  2. import { reactive, ref, Ref } from 'vue'
  3. import * as RongIMLib from '@rongcloud/imlib-next'
  4. import * as RTC from '@rongcloud/plugin-rtc'
  5. import request from '/src/helpers/request'
  6. import { state } from '/src/state'
  7. import event, { LIVE_EVENT_MESSAGE } from './event'
  8. import dayjs from 'dayjs'
  9. import { removeToken } from '/src/utils/auth';
  10. import qs from 'query-string'
  11. // import { SeatsCtrl } from './message-type'
  12. type imConnectStatus = 'connecting' | 'connected' | 'disconnect'
  13. type VideoStatus = 'init' | 'stream' | 'liveing' | 'stopped' | 'error' | 'loading'
  14. export type TrackType = 'microphone' | 'camera' | 'screen'
  15. let publishError = false
  16. type ActiveTracks = {
  17. [key in TrackType]: RTC.RCLocalTrack | null
  18. }
  19. type DeviceStatus = {
  20. [key in TrackType]: 'init' | 'granted' | 'denied' | 'closed' | 'none'
  21. }
  22. export const START_LIVE_TIME = 'start-live-time'
  23. export const START_LIVE_STATUS = 'start-live-status'
  24. export const VIDEO_DEVICE_ID = 'video-deviceId'
  25. export const AUDIO_DEVICE_ID = 'audio-deviceId'
  26. export const AUDIO_DEVICE_VOLUME = 'audio-device-volume'
  27. const runtime = reactive({
  28. /** 房间id */
  29. roomUid: sessionStorage.getItem('roomUid') || '',
  30. /** IM连接状态 */
  31. imConnectStatus: 'connecting' as imConnectStatus,
  32. // 屏幕分享状态
  33. screenShareStatus: false,
  34. // 视频节点
  35. videoRef: ref<HTMLVideoElement | null>(null),
  36. // RTC实例
  37. rtcClient: null as RTC.RCRTCClient | null,
  38. /** 加入房间实例 */
  39. joinedRoom: null as RTC.RCLivingRoom | null,
  40. // Tracks
  41. mediaStreamTrack: [] as MediaStreamTrack[],
  42. // 媒体流
  43. mediaStreams: null as MediaStream | null,
  44. // 视频状态
  45. videoStatus: 'init' as VideoStatus,
  46. // 麦克风设备列表
  47. microphones: [] as MediaDeviceInfo[],
  48. // 摄像头设备列表
  49. cameras: [] as MediaDeviceInfo[],
  50. // 摄像头设备
  51. selectedCamera: null as MediaDeviceInfo | null,
  52. // 麦克风设备
  53. selectedMicrophone: null as MediaDeviceInfo | null,
  54. // 点赞数量
  55. likeCount: 0,
  56. // 观看人数
  57. lookCount: 0,
  58. // 上一次点赞数量
  59. lastLikeCount: 0,
  60. /** 当前活跃的数据流 */
  61. activeTracks: {} as ActiveTracks,
  62. /** 是否关闭连麦 */
  63. allowSeatsCtrl: true,
  64. /** 是否关闭发言 */
  65. allowChatCtrl: true,
  66. /** 当前设备获取状态 */
  67. deviceStatus: {
  68. microphone: 'init',
  69. camera: 'init',
  70. screen: 'init'
  71. } as DeviceStatus,
  72. syncLikeTimer: null as any
  73. })
  74. export default runtime
  75. // c9kqb3rdc451j 测试环境
  76. // const RONG_IM_TOKEN = 'c9kqb3rdc451j'
  77. const RONG_IM_TOKEN = '6tnym1br6pv07'
  78. RongIMLib.init({
  79. appkey: RONG_IM_TOKEN,
  80. })
  81. // 注册自定义消息类型
  82. // 控制是否允许连麦
  83. const MessageSeatsCtrl = RongIMLib.registerMessageType('RC:Chatroom:SeatsCtrl', true, true)
  84. // 控制是否允许发言
  85. const MessageChatBan = RongIMLib.registerMessageType('RC:Chatroom:ChatBan', true, true)
  86. // 连麦消息
  87. const MessageSeatApply = RongIMLib.registerMessageType('RC:Chatroom:SeatApply', true, true)
  88. // 响应连麦消息
  89. const MessageSeatResponse = RongIMLib.registerMessageType('RC:Chatroom:SeatResponse', true, true)
  90. // 同步房间观众数量
  91. const MessageMemberCount = RongIMLib.registerMessageType('RC:Chatroom:MemberCount', true, true)
  92. // 当前连麦人员同步
  93. const MessageSeatMember = RongIMLib.registerMessageType('RC:Chatroom:SeatMember', true, true)
  94. // 当前点赞数量同步
  95. const MessageLikeCount = RongIMLib.registerMessageType('RC:Chatroom:LikeCount', true, true)
  96. type MessageProps = {
  97. messageType: 'RC:Chatroom:Welcome' | 'RC:TxtMsg' | 'RC:Chatroom:Barrage' | 'RC:Chatroom:Like' | 'RC:Chatroom:SeatsCtrl' | 'RC:Chatroom:ChatBan' | 'RC:Chatroom:SeatApply',
  98. content: any,
  99. senderUserId: any
  100. }
  101. type MessageEvent = {
  102. messages: MessageProps[],
  103. }
  104. const Events = RongIMLib.Events
  105. /**
  106. * 监听消息通知
  107. */
  108. const { MESSAGES, ...RestMessage } = Events
  109. RongIMLib.addEventListener(Events.MESSAGES, (evt: MessageEvent) => {
  110. console.log(evt, '收到消息')
  111. const { messages } = evt
  112. for (const message of messages) {
  113. // console.log(LIVE_EVENT_MESSAGE[message.messageType], message)
  114. const isSelf = message.senderUserId && Number(message.senderUserId) === state.user?.speakerId
  115. if (!isSelf && LIVE_EVENT_MESSAGE[message.messageType]) {
  116. event.emit(LIVE_EVENT_MESSAGE[message.messageType], {...message.content, $EventMessage: message})
  117. }
  118. }
  119. })
  120. for (const Message of Object.values(RestMessage)) {
  121. RongIMLib.addEventListener(Message, (evt: any) => {
  122. console.log(Message, evt)
  123. // chatroomDestroyed
  124. event.emit(Message, {$EventMessage: null})
  125. })
  126. }
  127. /**
  128. * 监听 IM 连接状态变化
  129. */
  130. RongIMLib.addEventListener(Events.CONNECTING, () => {
  131. console.log('connecting')
  132. runtime.imConnectStatus = 'connecting'
  133. })
  134. RongIMLib.addEventListener(Events.CONNECTED, () => {
  135. console.log('connected')
  136. runtime.imConnectStatus = 'connected'
  137. })
  138. RongIMLib.addEventListener(Events.DISCONNECT, () => {
  139. console.log('disconnect')
  140. runtime.imConnectStatus = 'disconnect'
  141. closeLive(true, 'IM')
  142. const search = qs.parse(window.location.search)
  143. console.log(search, 'disconnect')
  144. window.location.href = window.location.origin +'/live?' + qs.stringify({ ...search, time: new Date().getTime() })
  145. // event.emit(LIVE_EVENT_MESSAGE["RC:ForcedOffline"])
  146. // if (runtime.joinedRoom && runtime.videoStatus === 'liveing') {
  147. // closeLive(true, 'IM')
  148. // }
  149. })
  150. export const connectIM = async (imToken: string) => {
  151. try {
  152. const user = await RongIMLib.connect(imToken)
  153. runtime.rtcClient = RongIMLib.installPlugin(RTC.installer, {})
  154. console.log('connect success', user.data?.userId)
  155. return user
  156. } catch (error) {
  157. throw error
  158. }
  159. }
  160. /**
  161. * 设置声音
  162. * @param video
  163. * @param Value 声音大小
  164. */
  165. export const setVolume = (value: number) => {
  166. localStorage.setItem(AUDIO_DEVICE_VOLUME, value.toString())
  167. if(runtime.videoRef) {
  168. runtime.videoRef.volume = value / 100
  169. }
  170. // @ts-ignore
  171. if (runtime.activeTracks.microphone && runtime.activeTracks.microphone._element) {
  172. // @ts-ignore
  173. runtime.activeTracks.microphone._element.volume = value / 100
  174. }
  175. }
  176. /**
  177. * 设置video视频流
  178. */
  179. export const setVideoSrcObject = (video: HTMLVideoElement | null, mediaStreams: MediaStream | null) => {
  180. if (video && mediaStreams) {
  181. video.srcObject = mediaStreams
  182. video.onloadedmetadata = () => {
  183. video.play()
  184. }
  185. }
  186. }
  187. /**
  188. * 发起屏幕共享
  189. */
  190. export const shareScreenVideo = async () => {
  191. if (runtime.screenShareStatus) {
  192. ElMessage.error('正在屏幕共享中,请先关闭屏幕共享')
  193. return
  194. }
  195. if (runtime.rtcClient && !runtime.screenShareStatus && runtime.videoStatus === 'liveing') {
  196. let screenTrack: RTC.RCLocalTrack | undefined
  197. try {
  198. screenTrack = await getTrack('screen')
  199. } catch (error) {
  200. ElMessage.error('屏幕分享失败,请检查是否授权')
  201. }
  202. if (!screenTrack) {
  203. return
  204. }
  205. const oldTrack = runtime.activeTracks.camera as RTC.RCLocalTrack
  206. // removeTrack([oldTrack], 'camera')
  207. if (oldTrack) {
  208. await runtime.joinedRoom?.unpublish([oldTrack])
  209. }
  210. setTrack([screenTrack as RTC.RCLocalTrack], 'screen')
  211. if (runtime.videoRef) {
  212. screenTrack?.play(runtime.videoRef)
  213. runtime.screenShareStatus = true
  214. }
  215. screenTrack?.on(RTC.RCLocalTrack.EVENT_LOCAL_TRACK_END, (track: RTC.RCLocalTrack) => {
  216. runtime.screenShareStatus = false
  217. track.destroy()
  218. // removeTrack([track], 'screen')
  219. if (oldTrack) {
  220. setTrack([oldTrack as RTC.RCLocalTrack], 'camera')
  221. if (runtime.videoRef) {
  222. oldTrack.play(runtime.videoRef)
  223. }
  224. }
  225. // setVideoSrcObject(runtime.videoRef, this.mediaStreams)
  226. })
  227. }
  228. }
  229. /**
  230. * 取消屏幕共享流的访问,会导致取消屏幕共享
  231. */
  232. export const closeShareScreenVideo = () => {
  233. const screenTrack = runtime.activeTracks.screen as RTC.RCLocalTrack
  234. if (screenTrack) {
  235. screenTrack.destroy()
  236. runtime.screenShareStatus = false
  237. }
  238. const oldTrack = runtime.activeTracks.camera as RTC.RCLocalTrack
  239. if (oldTrack) {
  240. setTrack([oldTrack as RTC.RCLocalTrack], 'camera')
  241. if (runtime.videoRef) {
  242. oldTrack.play(runtime.videoRef)
  243. }
  244. }
  245. }
  246. export const toggleShareScreenVideo = async () => {
  247. if (runtime.screenShareStatus) {
  248. try {
  249. await ElMessageBox.confirm('是否确认取消屏幕共享?')
  250. closeShareScreenVideo()
  251. } catch (error) {}
  252. } else {
  253. shareScreenVideo()
  254. }
  255. }
  256. /**
  257. *
  258. * 获取所有音频输入设备
  259. * @returns {Promise<void>}
  260. */
  261. export const getMicrophones = async () => {
  262. const microphones = await RTC.device.getMicrophones()
  263. runtime.microphones = microphones
  264. return microphones
  265. }
  266. /**
  267. *
  268. * 获取所有视频输入设备
  269. * @returns {Promise<void>}
  270. */
  271. export const getCameras = async () => {
  272. const cameras = await RTC.device.getCameras()
  273. runtime.cameras = cameras
  274. return cameras
  275. }
  276. /**
  277. *
  278. * 设置当前视频设备
  279. * @param camera MediaDeviceInfo
  280. */
  281. export const setSelectCamera = async (camera: MediaDeviceInfo) => {
  282. runtime.selectedCamera = camera
  283. localStorage.setItem(VIDEO_DEVICE_ID, camera.deviceId)
  284. const oldTrack = runtime.activeTracks.camera as RTC.RCLocalTrack
  285. if (oldTrack) {
  286. await removeTrack([oldTrack], 'camera', oldTrack.isPublished())
  287. }
  288. const track = await getTrack('camera')
  289. setTrack([track], 'camera', runtime.videoStatus === 'liveing')
  290. }
  291. /**
  292. *
  293. * 设置当前麦克风设备
  294. * @param microphone MediaDeviceInfo
  295. */
  296. export const setSelectMicrophone = async (microphone: MediaDeviceInfo) => {
  297. runtime.selectedMicrophone = microphone
  298. localStorage.setItem(AUDIO_DEVICE_ID, microphone.deviceId)
  299. const oldTrack = runtime.activeTracks.microphone as RTC.RCLocalTrack
  300. if (oldTrack) {
  301. await removeTrack([oldTrack], 'microphone', oldTrack.isPublished())
  302. }
  303. const track = await getTrack('microphone')
  304. setTrack([track], 'microphone', runtime.videoStatus === 'liveing')
  305. }
  306. type TrackResult = {
  307. code: RTC.RCRTCCode,
  308. track: RTC.RCMicphoneAudioTrack | RTC.RCCameraVideoTrack | RTC.RCScreenVideoTrack | undefined
  309. }
  310. export const getTrack = async (trackType: TrackType): Promise<RTC.RCLocalTrack> => {
  311. let res: TrackResult | undefined
  312. let Track: RTC.RCLocalTrack | null = null
  313. if (trackType === 'microphone') {
  314. res = await runtime.rtcClient?.createMicrophoneAudioTrack('RongCloudRTC', {
  315. micphoneId: runtime.selectedMicrophone?.deviceId,
  316. }) as TrackResult
  317. } else if (trackType === 'camera') {
  318. res = await runtime.rtcClient?.createCameraVideoTrack('RongCloudRTC', {
  319. cameraId: runtime.selectedCamera?.deviceId,
  320. faceMode: 'user',
  321. frameRate: RTC.RCFrameRate.FPS_24,
  322. resolution: RTC.RCResolution.W1920_H1080,
  323. }) as TrackResult
  324. } else {
  325. res = await runtime?.rtcClient?.createScreenVideoTrack('screenshare', {
  326. frameRate: RTC.RCFrameRate.FPS_24,
  327. resolution: RTC.RCResolution.W1920_H1080,
  328. }) as TrackResult
  329. }
  330. Track = res?.track as RTC.RCLocalTrack
  331. if (trackType === 'camera' && !runtime.cameras.length) {
  332. runtime.deviceStatus[trackType] = 'none'
  333. } else if (trackType === 'microphone' && !runtime.microphones.length) {
  334. runtime.deviceStatus[trackType] = 'none'
  335. } else if (trackType === 'screen' && !runtime.screenShareStatus) {
  336. runtime.deviceStatus[trackType] = 'none'
  337. }
  338. if (res.code === RTC.RCRTCCode.PERMISSION_DENIED) {
  339. runtime.deviceStatus[trackType] = 'denied'
  340. } else {
  341. runtime.deviceStatus[trackType] = 'granted'
  342. }
  343. // if (res.code !== RTC.RCRTCCode.SUCCESS || !Track) {
  344. // throw new Error('获取数据流失败')
  345. // }
  346. if (res.code === RTC.RCRTCCode.GET_DISPLAY_MEDIA_FAILED) {
  347. throw new Error('获取屏幕共享失败')
  348. }
  349. return Track
  350. }
  351. /**
  352. * 添加视频流,会同步修改当先视频与推送的流
  353. * @param track
  354. */
  355. export const setTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
  356. for (const track of tracks) {
  357. // @ts-ignore
  358. // await runtime.mediaStreams?.addTrack(track._msTrack)
  359. // if (trackType === 'microphone') {
  360. // console.log('添加麦克风')
  361. // track?.play()
  362. // }
  363. runtime.activeTracks[trackType] = track
  364. }
  365. console.log(needPublish)
  366. if (needPublish) {
  367. // console.log('publish', runtime.joinedRoom)
  368. try {
  369. const res = await runtime.joinedRoom?.publish(tracks.filter(track => !!track))
  370. console.log(res, 'pub')
  371. if(res?.code !== RTC.RCRTCCode.SUCCESS && !publishError) {
  372. publishError = true
  373. window.onbeforeunload = null
  374. ElMessageBox.alert('视频就发送失败,请刷新页面重新开启?', '提示', {
  375. confirmButtonText: '确定',
  376. callback: () => {
  377. publishError = false
  378. window.location.reload()
  379. }})
  380. }
  381. } catch(err: any) {
  382. console.log(err, 'err')
  383. }
  384. }
  385. }
  386. /**
  387. * 删除视频流,会同步修改当先视频与推送的流
  388. * @param track
  389. */
  390. export const removeTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
  391. if (needPublish) {
  392. await runtime.joinedRoom?.unpublish(tracks.filter(track => !!track))
  393. }
  394. for (const track of tracks) {
  395. // @ts-ignore
  396. // await runtime.mediaStreams?.removeTrack(track._msTrack)
  397. // runtime.activeTracks[trackType].destroy()
  398. // console.log(runtime.activeTracks[trackType])
  399. track?.destroy()
  400. runtime.activeTracks[trackType] = null
  401. }
  402. }
  403. export const joinIMRoom = async (roomId: string, type: RTC.RCLivingType, listenEvents: RTC.IRoomEventListener | null) => {
  404. await RongIMLib.joinChatRoom(roomId, {count: -1})
  405. const join = await runtime.rtcClient?.joinLivingRoom(roomId, type)
  406. if (join?.code != RTC.RCRTCCode.SUCCESS) throw Error('加入房间失败')
  407. join.room?.registerRoomEventListener(listenEvents)
  408. return join
  409. }
  410. export const joinRoom = async (roomId: string, type: RTC.RCLivingType, listenEvents: RTC.IRoomEventListener | null) => {
  411. // try {
  412. // await request.get('/api-web/imLiveBroadcastRoom/joinRoom', {
  413. // params: {
  414. // roomUid: runtime.roomUid,
  415. // userId: state.user?.speakerId,
  416. // }
  417. // })
  418. // } catch (error) {}
  419. return await joinIMRoom(roomId, type, listenEvents)
  420. }
  421. /**
  422. * 开始直播
  423. */
  424. export const startLive = async (resetTime = true) => {
  425. if (runtime.videoStatus !== 'stream') {
  426. const errorMessage = '请确定摄像头已经开启'
  427. ElMessage.error(errorMessage)
  428. throw Error(errorMessage)
  429. }
  430. const room = runtime.joinedRoom
  431. if (room) {
  432. // const microphoneAudioTrack = await getTrack('microphone')
  433. // const cameraVideoTrack = await getTrack('camera')
  434. await setTrack([runtime.activeTracks.camera as RTC.RCLocalVideoTrack], 'camera')
  435. await setTrack([runtime.activeTracks.microphone as RTC.RCLocalAudioTrack], 'microphone')
  436. // const builder = await runtime.joinedRoom?.getMCUConfigBuilder()
  437. // // @ts-ignore
  438. // await builder.setOutputVideoRenderMode?.(RTC.MixVideoRenderMode.WHOLE)
  439. // // @ts-ignore
  440. // await builder.flush()
  441. // console.log(runtime.activeTracks)
  442. await request.get('/api-web/imLiveBroadcastRoom/opsLiveVideo', {
  443. params: {
  444. type: '1',
  445. roomUid: runtime.roomUid,
  446. userId: state.user?.speakerId,
  447. },
  448. })
  449. runtime.videoStatus = 'liveing'
  450. }
  451. if (resetTime) {
  452. sessionStorage.setItem(START_LIVE_TIME, dayjs().valueOf().toString())
  453. }
  454. sessionStorage.setItem(START_LIVE_STATUS, 'liveing')
  455. }
  456. /**
  457. * 关闭直播
  458. */
  459. export const closeLive = async (remove = false, source: 'IM' | 'Logout' = 'Logout') => {
  460. // removeMedia(runtime.mediaStreams, runtime.mediaStreamTrack)
  461. try {
  462. if(source === 'Logout') {
  463. await request.get('/api-web/imLiveBroadcastRoom/opsLiveVideo', {
  464. params: {
  465. type: '2',
  466. roomUid: runtime.roomUid,
  467. userId: state.user?.speakerId,
  468. }
  469. })
  470. }
  471. } catch {}
  472. sessionStorage.removeItem(START_LIVE_TIME)
  473. sessionStorage.removeItem(START_LIVE_STATUS)
  474. // 关闭房间仅移除推流即可
  475. for (const key in runtime.activeTracks) {
  476. if (Object.prototype.hasOwnProperty.call(runtime.activeTracks, key)) {
  477. const track = runtime.activeTracks[key as TrackType] as RTC.RCLocalTrack
  478. if (track) {
  479. await runtime.joinedRoom?.unpublish([track])
  480. if (remove) {
  481. await removeTrack([track], key as TrackType)
  482. }
  483. }
  484. }
  485. }
  486. runtime.videoStatus = 'stream'
  487. }
  488. /**
  489. * 同步点赞数量
  490. */
  491. export const loopSyncLike = async () => {
  492. // (runtime.likeCount !== runtime.lastLikeCount || runtime.likeCount === 0) &&
  493. if (state.user) {
  494. try {
  495. await request.get('/api-web/imLiveBroadcastRoom/syncLike', {
  496. hideLoading: true,
  497. hideMessage: true,
  498. params: {
  499. likeNum: runtime.likeCount,
  500. roomUid: runtime.roomUid,
  501. }
  502. })
  503. runtime.lastLikeCount = runtime.likeCount
  504. sendMessage({ count: runtime.likeCount }, 'LikeCount')
  505. } catch (error) {}
  506. }
  507. runtime.syncLikeTimer = setTimeout(() => {
  508. loopSyncLike()
  509. }, 1000 * 10)
  510. }
  511. type SendMessageType = 'text' | 'image' | 'audio' | 'video' | 'file' | 'SeatsCtrl' | 'ChatBan' | 'SeatApply' | 'SeatResponse' | 'MemberCount' | 'SeatMember' | 'LikeCount'
  512. export const getSendMessageUser = () => {
  513. return {
  514. id: String(state.user?.speakerId),
  515. name: state.user?.speakerName,
  516. userId: String(state.user?.speakerId),
  517. userName: state.user?.speakerName,
  518. }
  519. }
  520. /**
  521. *
  522. * @param msg 消息内容
  523. * @param type 消息类型
  524. * @returns null 或者 发送消息的结果
  525. */
  526. export const sendMessage = async (msg: any, type: SendMessageType = 'text') => {
  527. let message: RongIMLib.BaseMessage<unknown> | null = null
  528. if (!msg) return
  529. const conversation = {
  530. conversationType: RongIMLib.ConversationType.CHATROOM,
  531. targetId: runtime.joinedRoom?._roomId as string,
  532. }
  533. if (type === 'text') {
  534. message = new RongIMLib.TextMessage({
  535. user: getSendMessageUser(),
  536. content: msg
  537. })
  538. } else if (type === 'SeatsCtrl') {
  539. message = new MessageSeatsCtrl(msg)
  540. } else if (type === 'ChatBan') {
  541. message = new MessageChatBan(msg)
  542. } else if (type === 'SeatApply') {
  543. message = new MessageSeatApply(msg)
  544. } else if (type === 'SeatResponse') {
  545. message = new MessageSeatResponse(msg)
  546. } else if(type === 'MemberCount') {
  547. message = new MessageMemberCount(msg)
  548. } else if(type === 'SeatMember') {
  549. message = new MessageSeatMember(msg)
  550. } else if(type === 'LikeCount') {
  551. message = new MessageLikeCount(msg)
  552. }
  553. if (!message) return
  554. console.log(message)
  555. return await RongIMLib.sendMessage(conversation, message)
  556. }
  557. export const openDevice = async (trackType: TrackType, needPublish = true) => {
  558. if (trackType === 'microphone' && runtime.activeTracks[trackType]) {
  559. runtime.activeTracks[trackType]?.unmute()
  560. } else {
  561. const track = await getTrack(trackType)
  562. await setTrack([track], trackType, needPublish)
  563. if (runtime.videoRef) {
  564. track?.play(runtime.videoRef)
  565. }
  566. }
  567. }
  568. export const closeDevice = async (trackType: TrackType, needPublish = true) => {
  569. const track = runtime.activeTracks[trackType]
  570. if (trackType !== 'microphone') {
  571. // console.log('closeDevice', track)
  572. // track?.destroy()
  573. await removeTrack([track] as RTC.RCLocalTrack[], trackType, needPublish)
  574. } else {
  575. track?.mute()
  576. }
  577. }
  578. export const toggleDevice = async (trackType: TrackType) => {
  579. if (runtime.screenShareStatus) {
  580. await toggleShareScreenVideo()
  581. return
  582. }
  583. const track = runtime.activeTracks[trackType]
  584. const needPublish = runtime.videoStatus === 'liveing'
  585. if (track) {
  586. if (trackType === 'camera') {
  587. runtime.deviceStatus.camera = 'closed'
  588. }
  589. closeDevice(trackType, needPublish)
  590. } else {
  591. if (trackType === 'camera') {
  592. runtime.deviceStatus.camera = 'granted'
  593. }
  594. openDevice(trackType, needPublish)
  595. }
  596. }
  597. export const leaveIMRoom = async (source: 'IM' | 'Logout' = 'Logout') => {
  598. await closeLive(true, source)
  599. if (runtime.joinedRoom) {
  600. // @ts-ignore
  601. await runtime.rtcClient?.leaveRoom(runtime.joinedRoom)
  602. runtime.joinedRoom = null
  603. await RongIMLib.disconnect()
  604. runtime.imConnectStatus = 'disconnect'
  605. }
  606. }