|
@@ -1,6 +1,7 @@
|
|
|
import { reactive, ref, Ref } from 'vue'
|
|
|
import * as RongIMLib from '@rongcloud/imlib-next'
|
|
|
-import { installer, device, RCRTCCode, RCRTCClient, RCLocalTrack, RCLivingType, IRoomEventListener } from '@rongcloud/plugin-rtc'
|
|
|
+import * as RTC from '@rongcloud/plugin-rtc'
|
|
|
+import request from '/src/helpers/request'
|
|
|
import event, { LIVE_EVENT_MESSAGE } from './event'
|
|
|
import { removeMedia } from './helpers'
|
|
|
|
|
@@ -9,14 +10,16 @@ type imConnectStatus = 'connecting' | 'connected' | 'disconnect'
|
|
|
type VideoStatus = 'init' | 'stream' | 'liveing' | 'stopped' | 'error' | 'loading'
|
|
|
|
|
|
const runtime = reactive({
|
|
|
- // IM连接状态
|
|
|
+ /** IM连接状态 */
|
|
|
imConnectStatus: 'connecting' as imConnectStatus,
|
|
|
// 屏幕分享状态
|
|
|
screenShareStatus: false,
|
|
|
// 视频节点
|
|
|
videoRef: ref<HTMLVideoElement | null>(null),
|
|
|
// RTC实例
|
|
|
- rtcClient: null as RCRTCClient | null,
|
|
|
+ rtcClient: null as RTC.RCRTCClient | null,
|
|
|
+ /** 加入房间实例 */
|
|
|
+ joinedRoom: null as RTC.RCLivingRoom | null,
|
|
|
// Tracks
|
|
|
mediaStreamTrack: [] as MediaStreamTrack[],
|
|
|
// 媒体流
|
|
@@ -31,6 +34,12 @@ const runtime = reactive({
|
|
|
selectedCamera: null as MediaDeviceInfo | null,
|
|
|
// 麦克风设备
|
|
|
selectedMicrophone: null as MediaDeviceInfo | null,
|
|
|
+ // 点赞数量
|
|
|
+ likeCount: 0,
|
|
|
+ // 上一次点赞数量
|
|
|
+ lastLikeCount: 0,
|
|
|
+ /** 当前活跃的数据流 */
|
|
|
+ // activeTracks null as RTC.RCLiveStream | null,
|
|
|
})
|
|
|
|
|
|
export default runtime
|
|
@@ -42,34 +51,58 @@ RongIMLib.init({
|
|
|
appkey: RONG_IM_TOKEN,
|
|
|
})
|
|
|
|
|
|
+type MessageProps = {
|
|
|
+ messageType: 'RC:Chatroom:Welcome' | 'RC:TxtMsg' | 'RC:Chatroom:Barrage' | 'RC:Chatroom:Like',
|
|
|
+ content: any,
|
|
|
+}
|
|
|
+
|
|
|
+type MessageEvent = {
|
|
|
+ messages: MessageProps[],
|
|
|
+}
|
|
|
+
|
|
|
+const Events = RongIMLib.Events
|
|
|
/**
|
|
|
* 监听消息通知
|
|
|
*/
|
|
|
- const Events = RongIMLib.Events;
|
|
|
- RongIMLib.addEventListener(Events.MESSAGES, (event) => {
|
|
|
- console.log('received messages', event.messages)
|
|
|
+ const { MESSAGES, ...RestMessage } = Events
|
|
|
+ RongIMLib.addEventListener(Events.MESSAGES, (evt: MessageEvent) => {
|
|
|
+ const { messages } = evt
|
|
|
+ for (const message of messages) {
|
|
|
+ if (LIVE_EVENT_MESSAGE[message.messageType]) {
|
|
|
+ event.emit(LIVE_EVENT_MESSAGE[message.messageType], {...message.content, $EventMessage: message})
|
|
|
+ }
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
+ for (const Message of Object.values(RestMessage)) {
|
|
|
+ RongIMLib.addEventListener(Message, () => {
|
|
|
+ event.emit(Message, {$EventMessage: null})
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 监听 IM 连接状态变化
|
|
|
*/
|
|
|
RongIMLib.addEventListener(Events.CONNECTING, () => {
|
|
|
+ console.log('connecting')
|
|
|
runtime.imConnectStatus = 'connecting'
|
|
|
})
|
|
|
|
|
|
RongIMLib.addEventListener(Events.CONNECTED, () => {
|
|
|
+ console.log('connected')
|
|
|
runtime.imConnectStatus = 'connected'
|
|
|
})
|
|
|
|
|
|
RongIMLib.addEventListener(Events.DISCONNECT, () => {
|
|
|
+ console.log('disconnect')
|
|
|
runtime.imConnectStatus = 'disconnect'
|
|
|
})
|
|
|
|
|
|
- export const connectIM = async () => {
|
|
|
+ export const connectIM = async (imToken: string) => {
|
|
|
try {
|
|
|
- const user = await RongIMLib.connect('hXAfqkZDs146m6MjMukBMEHvmRoZQV7Hgh8ZG4iscyE=@n56a.cn.rongnav.com;n56a.cn.rongcfg.com')
|
|
|
- runtime.rtcClient = RongIMLib.installPlugin(installer, {})
|
|
|
- console.log('connect success', user.data?.userId);
|
|
|
+ const user = await RongIMLib.connect(imToken)
|
|
|
+ runtime.rtcClient = RongIMLib.installPlugin(RTC.installer, {})
|
|
|
+ console.log('connect success', user.data?.userId)
|
|
|
return user
|
|
|
} catch (error) {
|
|
|
throw error
|
|
@@ -94,20 +127,15 @@ export const setVideoSrcObject = (video: HTMLVideoElement | null, mediaStreams:
|
|
|
*/
|
|
|
export const shareScreenVideo = async () => {
|
|
|
if (runtime.rtcClient) {
|
|
|
- const { code, track: screenTrack } = await runtime.rtcClient.createScreenVideoTrack()
|
|
|
- if (code !== RCRTCCode.SUCCESS) {
|
|
|
- runtime.screenShareStatus = false
|
|
|
- } else {
|
|
|
- runtime.screenShareStatus = true
|
|
|
- console.log(runtime.videoRef)
|
|
|
- // @ts-ignore
|
|
|
- // setVideoSrcObject(runtime.videoRef, screenTrack?._msStream as MediaStream)
|
|
|
- }
|
|
|
- screenTrack?.on(RCLocalTrack.EVENT_LOCAL_TRACK_END, (track: RCLocalTrack) => {
|
|
|
- console.log('screen track end', track)
|
|
|
+ const screenTrack = await getTrack('screen')
|
|
|
+ console.log(screenTrack)
|
|
|
+ setTrack([screenTrack as RTC.RCLocalTrack])
|
|
|
+ screenTrack?.on(RTC.RCLocalTrack.EVENT_LOCAL_TRACK_END, (track: RTC.RCLocalTrack) => {
|
|
|
runtime.screenShareStatus = false
|
|
|
+ console.log('end')
|
|
|
// setVideoSrcObject(runtime.videoRef, this.mediaStreams)
|
|
|
})
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -117,7 +145,7 @@ export const shareScreenVideo = async () => {
|
|
|
* @returns {Promise<void>}
|
|
|
*/
|
|
|
export const getMicrophones = async () => {
|
|
|
- const microphones = await device.getMicrophones()
|
|
|
+ const microphones = await RTC.device.getMicrophones()
|
|
|
runtime.microphones = microphones
|
|
|
return microphones
|
|
|
}
|
|
@@ -128,7 +156,7 @@ export const getMicrophones = async () => {
|
|
|
* @returns {Promise<void>}
|
|
|
*/
|
|
|
export const getCameras = async () => {
|
|
|
- const cameras = await device.getCameras()
|
|
|
+ const cameras = await RTC.device.getCameras()
|
|
|
runtime.cameras = cameras
|
|
|
return cameras
|
|
|
}
|
|
@@ -151,17 +179,96 @@ export const setSelectMicrophone = (microphone: MediaDeviceInfo) => {
|
|
|
runtime.selectedMicrophone = microphone
|
|
|
}
|
|
|
|
|
|
-export const joinRoom = async (roomId: string, type: RCLivingType, listenEvents: IRoomEventListener | null) => {
|
|
|
+type TrackResult = {
|
|
|
+ code: RTC.RCRTCCode,
|
|
|
+ track: RTC.RCMicphoneAudioTrack | RTC.RCCameraVideoTrack | RTC.RCScreenVideoTrack | undefined
|
|
|
+}
|
|
|
+
|
|
|
+export const getTrack = async (trackType: 'microphone' | 'camera' | 'screen'): Promise<RTC.RCLocalTrack> => {
|
|
|
+ let res: TrackResult | undefined
|
|
|
+ let Track: RTC.RCLocalTrack | null = null
|
|
|
+ if (trackType === 'microphone') {
|
|
|
+ res = await runtime.rtcClient?.createMicrophoneAudioTrack('RongCloudRTC', {
|
|
|
+ micphoneId: runtime.selectedMicrophone?.deviceId,
|
|
|
+ }) as TrackResult
|
|
|
+ } else if (trackType === 'camera') {
|
|
|
+ res = await runtime.rtcClient?.createCameraVideoTrack('RongCloudRTC', {
|
|
|
+ cameraId: runtime.selectedCamera?.deviceId,
|
|
|
+ faceMode: 'user',
|
|
|
+ frameRate: RTC.RCFrameRate.FPS_15,
|
|
|
+ resolution: RTC.RCResolution.W1280_H720,
|
|
|
+ }) as TrackResult
|
|
|
+ } else {
|
|
|
+ res = await runtime?.rtcClient?.createScreenVideoTrack() as TrackResult
|
|
|
+ }
|
|
|
+
|
|
|
+ Track = res?.track as RTC.RCLocalTrack
|
|
|
+ if (res.code !== RTC.RCRTCCode.SUCCESS || !Track) {
|
|
|
+ throw new Error('获取数据流失败')
|
|
|
+ }
|
|
|
+ return Track
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 添加视频流,会同步修改当先视频与推送的流
|
|
|
+ * @param track
|
|
|
+ */
|
|
|
+export const setTrack = (tracks: [RTC.RCLocalTrack]) => {
|
|
|
+ for (const track of tracks) {
|
|
|
+ // @ts-ignore
|
|
|
+ runtime.mediaStreams?.addTrack(track._msTrack)
|
|
|
+ }
|
|
|
+ runtime.joinedRoom?.publish(tracks)
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 删除视频流,会同步修改当先视频与推送的流
|
|
|
+ * @param track
|
|
|
+ */
|
|
|
+export const removeTrack = (tracks: [RTC.RCLocalTrack]) => {
|
|
|
+ for (const track of tracks) {
|
|
|
+ // @ts-ignore
|
|
|
+ runtime.mediaStreams?.removeTrack(track._msTrack)
|
|
|
+ }
|
|
|
+ runtime.joinedRoom?.unpublish(tracks)
|
|
|
+}
|
|
|
+
|
|
|
+export const joinRoom = async (roomId: string, type: RTC.RCLivingType, listenEvents: RTC.IRoomEventListener | null) => {
|
|
|
+ await RongIMLib.joinChatRoom(roomId, {count: 0})
|
|
|
const join = await runtime.rtcClient?.joinLivingRoom(roomId, type)
|
|
|
- if (join?.code != RCRTCCode.SUCCESS) throw Error('加入房间失败')
|
|
|
+ if (join?.code != RTC.RCRTCCode.SUCCESS) throw Error('加入房间失败')
|
|
|
join.room?.registerRoomEventListener(listenEvents)
|
|
|
return join
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 开始直播
|
|
|
+ */
|
|
|
+
|
|
|
+export const startLive = async () => {
|
|
|
+ if (runtime.videoStatus !== 'stream') throw Error('当前无视频流')
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
* 关闭直播
|
|
|
*/
|
|
|
export const closeLive = async () => {
|
|
|
removeMedia(runtime.mediaStreams, runtime.mediaStreamTrack)
|
|
|
runtime.videoStatus = 'stopped'
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 同步点赞数量
|
|
|
+ */
|
|
|
+export const loopSyncLike = async () => {
|
|
|
+ if (runtime.likeCount !== runtime.lastLikeCount) {
|
|
|
+ try {
|
|
|
+ await request.post('/api/live/like', {})
|
|
|
+ runtime.lastLikeCount = runtime.likeCount
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ setTimeout(() => {
|
|
|
+ loopSyncLike()
|
|
|
+ }, 1000 * 60 * 5)
|
|
|
+}
|
|
|
+
|