|
@@ -2,17 +2,20 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|
import { reactive, ref, Ref } from 'vue'
|
|
import { reactive, ref, Ref } from 'vue'
|
|
import * as RongIMLib from '@rongcloud/imlib-next'
|
|
import * as RongIMLib from '@rongcloud/imlib-next'
|
|
import * as RTC from '@rongcloud/plugin-rtc'
|
|
import * as RTC from '@rongcloud/plugin-rtc'
|
|
|
|
+import { debounce } from 'throttle-debounce'
|
|
import request from '/src/helpers/request'
|
|
import request from '/src/helpers/request'
|
|
import { state } from '/src/state'
|
|
import { state } from '/src/state'
|
|
|
|
+import mitt from 'mitt'
|
|
import event, { LIVE_EVENT_MESSAGE } from './event'
|
|
import event, { LIVE_EVENT_MESSAGE } from './event'
|
|
import dayjs from 'dayjs'
|
|
import dayjs from 'dayjs'
|
|
|
|
+import { requireMedia } from './helpers';
|
|
// import { SeatsCtrl } from './message-type'
|
|
// import { SeatsCtrl } from './message-type'
|
|
|
|
|
|
type imConnectStatus = 'connecting' | 'connected' | 'disconnect'
|
|
type imConnectStatus = 'connecting' | 'connected' | 'disconnect'
|
|
|
|
|
|
type VideoStatus = 'init' | 'stream' | 'liveing' | 'stopped' | 'error' | 'loading'
|
|
type VideoStatus = 'init' | 'stream' | 'liveing' | 'stopped' | 'error' | 'loading'
|
|
|
|
|
|
-export type TrackType = 'microphone' | 'camera' | 'screen'
|
|
|
|
|
|
+export type TrackType = 'microphone' | 'microphone2' | 'camera' | 'screen'
|
|
|
|
|
|
type ActiveTracks = {
|
|
type ActiveTracks = {
|
|
[key in TrackType]: RTC.RCLocalTrack | null
|
|
[key in TrackType]: RTC.RCLocalTrack | null
|
|
@@ -30,6 +33,8 @@ export const VIDEO_DEVICE_ID = 'video-deviceId'
|
|
|
|
|
|
export const AUDIO_DEVICE_ID = 'audio-deviceId'
|
|
export const AUDIO_DEVICE_ID = 'audio-deviceId'
|
|
|
|
|
|
|
|
+export const AUDIO_DEVICE_ID2 = 'audio-deviceId2'
|
|
|
|
+
|
|
export const AUDIO_DEVICE_VOLUME = 'audio-device-volume'
|
|
export const AUDIO_DEVICE_VOLUME = 'audio-device-volume'
|
|
|
|
|
|
const runtime = reactive({
|
|
const runtime = reactive({
|
|
@@ -59,6 +64,8 @@ const runtime = reactive({
|
|
selectedCamera: null as MediaDeviceInfo | null,
|
|
selectedCamera: null as MediaDeviceInfo | null,
|
|
// 麦克风设备
|
|
// 麦克风设备
|
|
selectedMicrophone: null as MediaDeviceInfo | null,
|
|
selectedMicrophone: null as MediaDeviceInfo | null,
|
|
|
|
+ // 系统音频设备
|
|
|
|
+ selectedMicrophone2: null as MediaDeviceInfo | null,
|
|
// 点赞数量
|
|
// 点赞数量
|
|
likeCount: 0,
|
|
likeCount: 0,
|
|
// 观看人数
|
|
// 观看人数
|
|
@@ -124,6 +131,8 @@ type MessageEvent = {
|
|
messages: MessageProps[],
|
|
messages: MessageProps[],
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+export const runtimeEvent = mitt()
|
|
|
|
+
|
|
const Events = RongIMLib.Events
|
|
const Events = RongIMLib.Events
|
|
/**
|
|
/**
|
|
* 监听消息通知
|
|
* 监听消息通知
|
|
@@ -260,6 +269,7 @@ export const shareScreenVideo = async () => {
|
|
*/
|
|
*/
|
|
|
|
|
|
export const closeShareScreenVideo = () => {
|
|
export const closeShareScreenVideo = () => {
|
|
|
|
+ document.exitPictureInPicture()
|
|
const screenTrack = runtime.activeTracks.screen as RTC.RCLocalTrack
|
|
const screenTrack = runtime.activeTracks.screen as RTC.RCLocalTrack
|
|
if (screenTrack) {
|
|
if (screenTrack) {
|
|
screenTrack.destroy()
|
|
screenTrack.destroy()
|
|
@@ -274,6 +284,20 @@ export const closeShareScreenVideo = () => {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+export const createVideoPictureInPicture = (ms: MediaStream) => {
|
|
|
|
+ const video = document.createElement('video')
|
|
|
|
+ video.style.display = 'none'
|
|
|
|
+ document.body.append(video)
|
|
|
|
+ video.srcObject = ms
|
|
|
|
+ video.play()
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ video.requestPictureInPicture()
|
|
|
|
+ }, 1000)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 开启或关闭屏幕共享
|
|
|
|
+ */
|
|
export const toggleShareScreenVideo = async () => {
|
|
export const toggleShareScreenVideo = async () => {
|
|
if (runtime.screenShareStatus) {
|
|
if (runtime.screenShareStatus) {
|
|
try {
|
|
try {
|
|
@@ -282,6 +306,10 @@ export const toggleShareScreenVideo = async () => {
|
|
} catch (error) {}
|
|
} catch (error) {}
|
|
} else {
|
|
} else {
|
|
shareScreenVideo()
|
|
shareScreenVideo()
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ createVideoPictureInPicture(runtime.activeTracks.camera._msStream)
|
|
|
|
+ console.log(runtime.activeTracks.camera)
|
|
|
|
+ // runtime.videoRef?.requestPictureInPicture()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -337,6 +365,24 @@ export const setSelectMicrophone = async (microphone: MediaDeviceInfo) => {
|
|
}
|
|
}
|
|
const track = await getTrack('microphone')
|
|
const track = await getTrack('microphone')
|
|
setTrack([track], 'microphone', runtime.videoStatus === 'liveing')
|
|
setTrack([track], 'microphone', runtime.videoStatus === 'liveing')
|
|
|
|
+ runtimeEvent.emit('microphoneChange', microphone)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ *
|
|
|
|
+ * 设置当前麦克风设备
|
|
|
|
+ * @param microphone MediaDeviceInfo
|
|
|
|
+ */
|
|
|
|
+ export const setSelectMicrophone2 = async (microphone: MediaDeviceInfo) => {
|
|
|
|
+ runtime.selectedMicrophone2 = microphone
|
|
|
|
+ localStorage.setItem(AUDIO_DEVICE_ID2, microphone.deviceId)
|
|
|
|
+ const oldTrack = runtime.activeTracks.microphone2 as RTC.RCLocalTrack
|
|
|
|
+ if (oldTrack) {
|
|
|
|
+ await removeTrack([oldTrack], 'microphone2', oldTrack.isPublished())
|
|
|
|
+ }
|
|
|
|
+ const track = await getTrack('microphone2')
|
|
|
|
+ setTrack([track], 'microphone2', runtime.videoStatus === 'liveing')
|
|
|
|
+ runtimeEvent.emit('microphone2Change', microphone)
|
|
}
|
|
}
|
|
|
|
|
|
type TrackResult = {
|
|
type TrackResult = {
|
|
@@ -350,17 +396,28 @@ export const getTrack = async (trackType: TrackType): Promise<RTC.RCLocalTrack>
|
|
if (trackType === 'microphone') {
|
|
if (trackType === 'microphone') {
|
|
res = await runtime.rtcClient?.createMicrophoneAudioTrack('RongCloudRTC', {
|
|
res = await runtime.rtcClient?.createMicrophoneAudioTrack('RongCloudRTC', {
|
|
micphoneId: runtime.selectedMicrophone?.deviceId,
|
|
micphoneId: runtime.selectedMicrophone?.deviceId,
|
|
|
|
+ sampleRate: 44100,
|
|
|
|
+ }) as TrackResult
|
|
|
|
+ } else if (trackType === 'microphone2') {
|
|
|
|
+ res = await runtime.rtcClient?.createMicrophoneAudioTrack('RongCloudRTC', {
|
|
|
|
+ micphoneId: runtime.selectedMicrophone2?.deviceId,
|
|
|
|
+
|
|
}) as TrackResult
|
|
}) as TrackResult
|
|
} else if (trackType === 'camera') {
|
|
} else if (trackType === 'camera') {
|
|
|
|
+ // const sm = await requireMedia({
|
|
|
|
+ // audio: true,
|
|
|
|
+ // video: true,
|
|
|
|
+ // })
|
|
|
|
+ // console.log(sm.getTracks())
|
|
res = await runtime.rtcClient?.createCameraVideoTrack('RongCloudRTC', {
|
|
res = await runtime.rtcClient?.createCameraVideoTrack('RongCloudRTC', {
|
|
cameraId: runtime.selectedCamera?.deviceId,
|
|
cameraId: runtime.selectedCamera?.deviceId,
|
|
faceMode: 'user',
|
|
faceMode: 'user',
|
|
- frameRate: RTC.RCFrameRate.FPS_24,
|
|
|
|
|
|
+ frameRate: RTC.RCFrameRate.FPS_30,
|
|
resolution: RTC.RCResolution.W1920_H1080,
|
|
resolution: RTC.RCResolution.W1920_H1080,
|
|
}) as TrackResult
|
|
}) as TrackResult
|
|
} else {
|
|
} else {
|
|
res = await runtime?.rtcClient?.createScreenVideoTrack('screenshare', {
|
|
res = await runtime?.rtcClient?.createScreenVideoTrack('screenshare', {
|
|
- frameRate: RTC.RCFrameRate.FPS_24,
|
|
|
|
|
|
+ frameRate: RTC.RCFrameRate.FPS_30,
|
|
resolution: RTC.RCResolution.W1920_H1080,
|
|
resolution: RTC.RCResolution.W1920_H1080,
|
|
}) as TrackResult
|
|
}) as TrackResult
|
|
}
|
|
}
|
|
@@ -368,7 +425,7 @@ export const getTrack = async (trackType: TrackType): Promise<RTC.RCLocalTrack>
|
|
Track = res?.track as RTC.RCLocalTrack
|
|
Track = res?.track as RTC.RCLocalTrack
|
|
if (trackType === 'camera' && !runtime.cameras.length) {
|
|
if (trackType === 'camera' && !runtime.cameras.length) {
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
- } else if (trackType === 'microphone' && !runtime.microphones.length) {
|
|
|
|
|
|
+ } else if ((trackType === 'microphone' || trackType === 'microphone2') && !runtime.microphones.length) {
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
} else if (trackType === 'screen' && !runtime.screenShareStatus) {
|
|
} else if (trackType === 'screen' && !runtime.screenShareStatus) {
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
runtime.deviceStatus[trackType] = 'none'
|
|
@@ -387,24 +444,80 @@ export const getTrack = async (trackType: TrackType): Promise<RTC.RCLocalTrack>
|
|
return Track
|
|
return Track
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+export type OnAudioProcess = (num: number, deviceId: string) => void
|
|
|
|
+
|
|
|
|
+export const listenAudioChecker = (stream: MediaStream, onaudioprocess: OnAudioProcess) => {
|
|
|
|
+ const audioContext = window.AudioContext
|
|
|
|
+ const ac = new audioContext()
|
|
|
|
+ const liveSource = ac.createMediaStreamSource(stream)
|
|
|
|
+ const analyser = ac.createAnalyser()
|
|
|
|
+ liveSource.connect(analyser)
|
|
|
|
+ analyser.fftSize = 2048
|
|
|
|
+ analyser.minDecibels = -90
|
|
|
|
+ analyser.maxDecibels = -10
|
|
|
|
+ analyser.smoothingTimeConstant = 0.85
|
|
|
|
+
|
|
|
|
+ // setInterval(() => {
|
|
|
|
+ // getVoiceSize(analyser)
|
|
|
|
+ // }, 50)
|
|
|
|
+ // return analyser
|
|
|
|
+
|
|
|
|
+ const levelChecker = ac.createScriptProcessor(4096, 1, 1)
|
|
|
|
+ levelChecker.connect(ac.destination)
|
|
|
|
+ liveSource.connect(levelChecker)
|
|
|
|
+ levelChecker.onaudioprocess = (e) => debounce(200, () => {
|
|
|
|
+ const buffer = e.inputBuffer.getChannelData(0)
|
|
|
|
+ var maxVal = 0;
|
|
|
|
+ for (var i = 0; i < buffer.length; i++) {
|
|
|
|
+ if (maxVal < buffer[i]) {
|
|
|
|
+ maxVal = buffer[i];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // console.log(stream.getAudioTracks()[0])
|
|
|
|
+ onaudioprocess(maxVal * 100, stream.getAudioTracks()[0]?.label)
|
|
|
|
+ // console.log(maxVal * 100, stream.getAudioTracks()[0]?.label)
|
|
|
|
+ // console.log(e.inputBuffer.getChannelData(0))
|
|
|
|
+ })()
|
|
|
|
+ return levelChecker
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const getVoiceSize = (analyser: AnalyserNode) => {
|
|
|
|
+ const dataArray = new Uint8Array(analyser.frequencyBinCount)
|
|
|
|
+ analyser.getByteFrequencyData(dataArray)
|
|
|
|
+ const data = dataArray.slice(100, 1000)
|
|
|
|
+ const sum = data.reduce((a, b) => a + b)
|
|
|
|
+
|
|
|
|
+ // for(var i = 0; i < analyser.frequencyBinCount; i++) {
|
|
|
|
+ // var v = dataArray[i] / 128.0
|
|
|
|
+ // console.log(v)
|
|
|
|
+ // }
|
|
|
|
+ console.log(sum, 128 * analyser.frequencyBinCount)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 添加视频流,会同步修改当先视频与推送的流
|
|
* 添加视频流,会同步修改当先视频与推送的流
|
|
* @param track
|
|
* @param track
|
|
*/
|
|
*/
|
|
export const setTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
|
|
export const setTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
|
|
- for (const track of tracks) {
|
|
|
|
|
|
+ const filterTracks = tracks.filter(track => !!track)
|
|
|
|
+ for (const track of filterTracks) {
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
// await runtime.mediaStreams?.addTrack(track._msTrack)
|
|
// await runtime.mediaStreams?.addTrack(track._msTrack)
|
|
- // if (trackType === 'microphone') {
|
|
|
|
- // console.log('添加麦克风')
|
|
|
|
- // track?.play()
|
|
|
|
- // }
|
|
|
|
|
|
+ if (trackType === 'microphone') {
|
|
|
|
+ // track?.play()
|
|
|
|
+ }
|
|
runtime.activeTracks[trackType] = track
|
|
runtime.activeTracks[trackType] = track
|
|
}
|
|
}
|
|
- console.log(needPublish)
|
|
|
|
|
|
+
|
|
|
|
+ if (trackType === 'camera' && runtime.videoRef) {
|
|
|
|
+ runtime.activeTracks[trackType]?.play(runtime.videoRef)
|
|
|
|
+ }
|
|
|
|
+
|
|
if (needPublish) {
|
|
if (needPublish) {
|
|
// console.log('publish', runtime.joinedRoom)
|
|
// console.log('publish', runtime.joinedRoom)
|
|
- await runtime.joinedRoom?.publish(tracks.filter(track => !!track))
|
|
|
|
|
|
+ await runtime.joinedRoom?.publish(filterTracks)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
/**
|
|
@@ -412,10 +525,11 @@ export const setTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType,
|
|
* @param track
|
|
* @param track
|
|
*/
|
|
*/
|
|
export const removeTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
|
|
export const removeTrack = async (tracks: RTC.RCLocalTrack[], trackType: TrackType, needPublish = true) => {
|
|
|
|
+ const filterTracks = tracks.filter(track => !!track)
|
|
if (needPublish) {
|
|
if (needPublish) {
|
|
- await runtime.joinedRoom?.unpublish(tracks.filter(track => !!track))
|
|
|
|
|
|
+ await runtime.joinedRoom?.unpublish(filterTracks)
|
|
}
|
|
}
|
|
- for (const track of tracks) {
|
|
|
|
|
|
+ for (const track of filterTracks) {
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
// await runtime.mediaStreams?.removeTrack(track._msTrack)
|
|
// await runtime.mediaStreams?.removeTrack(track._msTrack)
|
|
// runtime.activeTracks[trackType].destroy()
|
|
// runtime.activeTracks[trackType].destroy()
|
|
@@ -585,7 +699,7 @@ export const sendMessage = async (msg: any, type: SendMessageType = 'text') => {
|
|
}
|
|
}
|
|
|
|
|
|
export const openDevice = async (trackType: TrackType, needPublish = true) => {
|
|
export const openDevice = async (trackType: TrackType, needPublish = true) => {
|
|
- if (trackType === 'microphone' && runtime.activeTracks[trackType]) {
|
|
|
|
|
|
+ if ((trackType === 'microphone' || trackType === 'microphone2') && runtime.activeTracks[trackType]) {
|
|
runtime.activeTracks[trackType]?.unmute()
|
|
runtime.activeTracks[trackType]?.unmute()
|
|
} else {
|
|
} else {
|
|
const track = await getTrack(trackType)
|
|
const track = await getTrack(trackType)
|
|
@@ -598,7 +712,7 @@ export const openDevice = async (trackType: TrackType, needPublish = true) => {
|
|
|
|
|
|
export const closeDevice = async (trackType: TrackType, needPublish = true) => {
|
|
export const closeDevice = async (trackType: TrackType, needPublish = true) => {
|
|
const track = runtime.activeTracks[trackType]
|
|
const track = runtime.activeTracks[trackType]
|
|
- if (trackType !== 'microphone') {
|
|
|
|
|
|
+ if (trackType !== 'microphone' && trackType !== 'microphone2') {
|
|
// console.log('closeDevice', track)
|
|
// console.log('closeDevice', track)
|
|
// track?.destroy()
|
|
// track?.destroy()
|
|
await removeTrack([track] as RTC.RCLocalTrack[], trackType, needPublish)
|
|
await removeTrack([track] as RTC.RCLocalTrack[], trackType, needPublish)
|