我正在尝试在 Agora 中切换麦克风和摄像头设备。我正在使用agora-rtc-sdk-ng和agora-rtc-react库来实现这一点。我创建了一个自定义钩子 use-agora 来处理所有情况。即当远程用户加入或处理本地用户流等时。这是我的钩子实现:
import { useEffect, useState } from 'react'
import AgoraRTC, { ClientConfig, IAgoraRTCRemoteUser } from 'agora-rtc-sdk-ng'
import { createClient, createMicrophoneAndCameraTracks } from 'agora-rtc-react'
import { useRecoilState } from 'recoil'
import config from '../../../config'
import { studioStore } from '../stores'
const videoConfig: ClientConfig = {
mode: 'rtc',
codec: 'vp8',
}
const { appId } = config.agora
const useClient = createClient(videoConfig)
const useMicrophoneAndCameraTracks = createMicrophoneAndCameraTracks()
export interface RTCUser extends IAgoraRTCRemoteUser {
mediaStream?: MediaStream
}
export default function useAgora(
channel: string,
{
onTokenWillExpire,
onTokenDidExpire,
}: { onTokenWillExpire: () => void; onTokenDidExpire: () => void }
) {
const client = useClient()
const [users, setUsers] = useState<RTCUser[]>([])
const [stream, setStream] = useState<MediaStream>()
const [studio, setStudio] = useRecoilState(studioStore)
const [userAudios, setUserAudios] = useState<MediaStream[]>([])
const [cameraDevice, setCameraDevice] = useState<MediaDeviceInfo>()
const [microphoneDevice, setMicrophoneDevice] = useState<MediaDeviceInfo>()
const { ready, tracks } = useMicrophoneAndCameraTracks()
useEffect(() => {
;(async () => {
init()
})()
}, [])
useEffect(() => {
if (!tracks) return
setStream(
new MediaStream([
tracks[0].getMediaStreamTrack(),
tracks[1].getMediaStreamTrack(),
])
) // I am doing setStream here. So whenever the track will update a new stream will be created and returned through the hook
}, [tracks])
useEffect(() => {
if (!tracks) return
;(async () => {
if (cameraDevice) {
await tracks[1].setDevice(cameraDevice.deviceId) // I am setting the cameraDevice here
}
})()
}, [cameraDevice])
useEffect(() => {
if (!tracks) return
;(async () => {
if (microphoneDevice) {
console.log('i came here', microphoneDevice) // This log is coming perfectly
await tracks[0].setDevice(microphoneDevice.deviceId) // I am setting the Microphone device here
console.log('Newmic', microphoneDevice) // This line never executes, because setDevice function is throwing timeout
}
})()
}, [microphoneDevice])
const getMediaDevices = async () => {
const camDevices = await AgoraRTC.getCameras()
setCameraDevice(camDevices[0])
const audiDevices = await AgoraRTC.getMicrophones()
setMicrophoneDevice(audiDevices[0])
}
const init = async () => {
try {
await getMediaDevices()
client.on('user-published', async (user, mediaType) => {
await client.subscribe(user, mediaType)
const tracks: MediaStreamTrack[] = []
if (user.audioTrack) tracks.push(user.audioTrack?.getMediaStreamTrack())
if (user.videoTrack) tracks.push(user.videoTrack?.getMediaStreamTrack())
if (mediaType === 'video') {
setUsers((prevUsers) => {
if (prevUsers.find((element) => element.uid === user.uid))
return [...prevUsers]
return [
...prevUsers,
{
...user,
mediaStream:
tracks && tracks.length > 0
? // @ts-ignore
new MediaStream(tracks.filter((track) => !!track))
: undefined,
},
]
})
}
if (mediaType === 'audio') {
user.audioTrack?.play()
setUserAudios((prev) => [
...prev,
new MediaStream([
user.audioTrack?.getMediaStreamTrack() as MediaStreamTrack,
]),
])
}
})
client.on('user-left', (user) => {
setUsers((prevUsers) => {
return prevUsers.filter((User) => User.uid !== user.uid)
})
})
client.on('token-privilege-will-expire', () => {
onTokenWillExpire()
})
client.on('token-privilege-did-expire', () => {
onTokenDidExpire()
})
client.on('user-left', (user) => {
setUsers((prevUsers) => {
return prevUsers.filter((User) => User.uid !== user.uid)
})
})
} catch (error) {
console.log(error)
throw error
}
}
AgoraRTC.onCameraChanged = async (changedDevice) => {
if (changedDevice.state === 'ACTIVE') {
setCameraDevice(changedDevice.device)
// Switch to an existing device when the current device is unplugged.
} else if (changedDevice.device.label === tracks?.[0].getTrackLabel()) {
const oldCameras = await AgoraRTC.getCameras()
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
oldCameras[0] && setCameraDevice(oldCameras[0])
}
}
AgoraRTC.onMicrophoneChanged = async (changedDevice) => {
if (changedDevice.state === 'ACTIVE') {
setMicrophoneDevice(changedDevice.device)
// Switch to an existing device when the current device is unplugged.
} else if (changedDevice.device.label === tracks?.[0].getTrackLabel()) {
const oldMicrophones = await AgoraRTC.getMicrophones()
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
oldMicrophones[0] && setMicrophoneDevice(oldMicrophones[0])
}
}
const renewToken = async (token: string) => {
client.renewToken(token)
}
const join = async (token: string, uid: string) => {
try {
if (!ready) throw new Error('Not ready')
await client.join(appId, channel, token, uid)
if (tracks) await client.publish(tracks)
} catch (error) {
console.error(error)
throw error
}
}
const mute = async (type: 'audio' | 'video') => {
const { constraints } = studio
if (type === 'audio') {
const newValue = !constraints?.audio
await tracks?.[0].setEnabled(newValue)
setStudio((studio) => {
return {
...studio,
constraints: {
...studio.constraints,
audio: newValue,
},
}
})
} else if (type === 'video') {
const newValue = !constraints?.video
await tracks?.[1].setEnabled(newValue)
setStudio((studio) => {
return {
...studio,
constraints: {
...studio.constraints,
video: newValue,
},
}
})
}
}
const getCameras = async () => {
return AgoraRTC.getCameras()
}
const updateCameraDevices = (device: MediaDeviceInfo) => {
setCameraDevice(device)
}
const getMicrophones = async () => {
return AgoraRTC.getMicrophones()
}
const updateMicroPhoneDevices = (device: MediaDeviceInfo) => {
setMicrophoneDevice(device)
}
const leave = async () => {
try {
tracks?.forEach((track) => track.stop())
await client.leave()
} catch (error) {
console.error(error)
throw error
}
}
return {
ready,
users,
join,
leave,
mute,
tracks,
stream,
userAudios,
renewToken,
cameraDevice,
microphoneDevice,
getCameras,
getMicrophones,
updateCameraDevices,
updateMicroPhoneDevices,
}
}
现在从我调用的组件updateCameraDevices
和updateMicroPhoneDevices
函数中更新输入设备。这是下拉代码:
<Select
onChange={(device) => {
if (!device) return
const selectedCameraDevice = cameraDevices.find(
(d) => d.deviceId === device.value
)
if (selectedCameraDevice)
updateCameraDevices(selectedCameraDevice)
}}
...
<Select
onChange={(device) => {
if (!device) return
const selectedMicrophoneDevice = microphoneDevices.find(
(d) => d.deviceId === device.value
)
console.log('I set microphone', selectedMicrophoneDevice)
if (selectedMicrophoneDevice)
updateMicroPhoneDevices(selectedMicrophoneDevice)
}}
...
现在,当我设置设备时,agora 会向我发送日志,表明新设备已设置。但一段时间后它显示 setDevice 超时。以下是日志:
I set microphone InputDeviceInfo {deviceId: '21b351d2e67c42c09478fd6578e971e6d4e43074346acd853785349bc536631b', kind: 'audioinput', label: 'Krisp Microphone (Krisp)', groupId: '348e200e095d9193419f72418cfdee7f94bc4cecea2e5ac686bf1238bdc8b561'}
use-agora.ts:67 i came here InputDeviceInfo {deviceId: '21b351d2e67c42c09478fd6578e971e6d4e43074346acd853785349bc536631b', kind: 'audioinput', label: 'Krisp Microphone (Krisp)', groupId: '348e200e095d9193419f72418cfdee7f94bc4cecea2e5ac686bf1238bdc8b561'}
AgoraRTC_N-production.js:395 17:02:32:956 Agora-SDK [INFO]: [getTrackId() {
return this._ID;
}] start set device to 21b351d2e67c42c09478fd6578e971e6d4e43074346acd853785349bc536631b
AgoraRTC_N-production.js:395 17:02:32:988 Agora-SDK [DEBUG]: [track-26722f69] GetUserMedia {"audio":{"deviceId":{"exact":"21b351d2e67c42c09478fd6578e971e6d4e43074346acd853785349bc536631b"}}}
AgoraRTC_N-production.js:395 17:02:33:50 Agora-SDK [DEBUG]: [track-track-26722f69] update player source track
AgoraRTC_N-production.js:395 17:03:02:143 Agora-SDK [DEBUG]: [client-2b69d] receive exception msg, code: 2001, msg: AUDIO_INPUT_LEVEL_TOO_LOW, uid: DtplUfgXdoN7ZpOMioMiH0i2opO2
AgoraRTC_N-production.js:395 17:03:07:143 Agora-SDK [DEBUG]: [client-2b69d] receive exception msg, code: 4001, msg: AUDIO_INPUT_LEVEL_TOO_LOW_RECOVER, uid: DtplUfgXdoN7ZpOMioMiH0i2opO2
AgoraRTC_N-production.js:395 17:03:32:956 Agora-SDK [DEBUG]: MicrophoneAudioTrack.setDevice timeout
AgoraRTC_N-production.js:395 17:04:07:142 Agora-SDK [DEBUG]: [client-2b69d] receive exception msg, code: 2001, msg: AUDIO_INPUT_LEVEL_TOO_LOW, uid: DtplUfgXdoN7ZpOMioMiH0i2opO2
现在我无法弄清楚为什么 setDevice 发送超时。我相信这是我的流没有更新的唯一原因。我在这里先向您的帮助表示感谢 :)