【发布时间】:2020-04-29 01:12:55
【问题描述】:
我构建了一个简单的点对点应用程序,允许 2 个用户进行视频和音频聊天。我正在尝试创建多个音频输入选择(基于可用的内容)并且能够通过使用以下代码来获得:
gotDevices(deviceInfos) {
const document = ReactDOM.findDOMNode(this);
const videoElement = document.querySelector('video');
const audioInputSelect = document.querySelector('select#audioSource');
const audioOutputSelect = document.querySelector('select#audioOutput');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [audioInputSelect, audioOutputSelect, videoSelect];
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
for (let i = 0; i < deviceInfos.length; i++) {
var deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'audioinput') {
this.state.audioInputOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label}</option>)
} else if (deviceInfo.kind === 'audiooutput') {
this.state.audioOutputOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`}</option>)
} else if (deviceInfo.kind === 'videoinput') {
this.state.videoSourceOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`}</option>)
} else {
console.log('Some other kind of source/device: ', deviceInfo);
}
}
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
}
此代码引用自此网址https://github.com/webrtc/samples/blob/gh-pages/src/content/devices/input-output/js/main.js
我能够成功获取每个音频输入伴随的不同 deviceId。我附上了一个 consol.log 屏幕截图,当我更改输入时显示不同的 deviceId 和音轨。
另外,这是我设置音频输入的代码:
getUserMedia = (audioInput) => {
return new Promise((resolve, reject) => {
// This is for the different browsers
navigator.getUserMedia = navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia
// Setting up constraints
console.log(audioInput)
const constraints = {
video: {
width: { min: 160, ideal: 640, max: 1280 },
height: { min: 120, ideal: 360, max: 720 }
},
audio: {deviceId: audioInput}
}
navigator.getUserMedia(
constraints,
stream => {
this.setState({ streamUrl: stream, localStream: stream })
this.localVideo.srcObject = stream
resolve()
},
() => {}
)
console.log(constraints.audio)
})}
其中 audioInput 是一个状态变量。在我的 componentdidUpdate 中,我检查了该变量的不同状态值,并使用新的 audioInput 状态运行 getUserMedia,如下所示:
componentDidUpdate(prevProps, prevState) {
if (prevState.audioSource !== this.state.audioSource) {
this.getUserMedia(this.state.audioSource)
}
}
但是,用户的音频不会改变,但如果我在代码中手动更改 audioInput 状态并运行它,那么我可以听到音频的变化。以下是我对此功能的所有代码作为参考:
import React from 'react'
import VideoCall from '../helpers/simple-peer'
import '../styles/video.css'
import io from 'socket.io-client'
import ReactDOM from "react-dom";
import ShareScreenIcon from './icons/ShareScreenIcon';
import AudioIcon from './icons/AudioIcon';
import AudioMuteIcon from './icons/AudioMuteIcon';
import VideoMuteIcon from './icons/VideoMuteIcon';
import VideoIcon from './icons/VideoIcon';
import LeaveChatIcon from './icons/LeaveChatIcon';
import Select from 'react-select';
var socket;
class Video extends React.Component {
constructor() {
super()
this.state = {
localStream: {},
remoteStreamUrl: '',
streamUrl: '',
initiator: false,
peer: {},
connecting: false,
waiting: true,
audioMute: false,
videoMute: false,
data: [],
viewChat: false,
leaveChat: false,
audioInputOptions: [],
audioOutputOptions: [],
videoSourceOptions: [],
audioSource:'default',
audioConfigRecieved: false
}
}
videoCall = new VideoCall()
componentDidMount() {
socket = io("http://localhost:8080")
const component = this
this.setState({ socket })
const { roomId } = this.props.match.params
navigator.mediaDevices.enumerateDevices().then(input => this.gotDevices(input)).catch(this.handleError);
this.getUserMedia(this.state.audioSource).then(() => {
socket.emit('join', { roomId: roomId })
console.log("recived permission")
})
socket.on('init', () => {
component.setState({ initiator: true })
})
socket.on('ready', () => {
// Entering room (there are 2 people)
component.enter(roomId)
})
socket.on('desc', data => {
if (data.type === 'offer' && component.state.initiator) return
if (data.type === 'answer' && !component.state.initiator) return
component.call(data)
})
socket.on('disconnected', () => {
component.setState({ initiator: true })
// this.state.localStream.getVideoTracks()[0].enabled = !(this.state.localStream.getVideoTracks()[0].enabled);
})
socket.on('full', () => {
component.setState({ full: true })
})
}
componentDidUpdate(prevProps, prevState) {
if (prevState.audioSource !== this.state.audioSource) {
this.getUserMedia(this.state.audioSource)
}
}
getUserMedia = (audioInput) => {
return new Promise((resolve, reject) => {
// This is for the different browsers
navigator.getUserMedia = navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia
// Setting up constraints
console.log(audioInput)
const constraints = {
video: {
width: { min: 160, ideal: 640, max: 1280 },
height: { min: 120, ideal: 360, max: 720 }
},
audio: {deviceId: audioInput}
}
navigator.getUserMedia(
constraints,
stream => {
this.setState({ streamUrl: stream, localStream: stream })
this.localVideo.srcObject = stream
resolve()
},
() => {}
)
console.log(constraints.audio)
})
}
getDisplay(){
navigator.mediaDevices.getDisplayMedia().then(stream => {
stream.oninactive = () => {
this.state.peer.removeStream(this.state.localStream)
this.getUserMedia().then(() => {
this.state.peer.addStream(this.state.localStream)
})
}
this.setState({ streamUrl: stream, localStream: stream })
this.localVideo.srcObject = stream
this.state.peer.addStream(stream)
})
}
stopStreamingVideo = () => {
this.state.localStream.getVideoTracks()[0].enabled = !(this.state.localStream.getVideoTracks()[0].enabled);
this.setState({videoMute: !this.state.videoMute})
}
stopStreamingAudio = () => {
this.state.localStream.getAudioTracks()[0].enabled = !(this.state.localStream.getAudioTracks()[0].enabled);
this.setState({audioMute: !this.state.audioMute})
}
leaveChat = () => {
this.state.localStream.getVideoTracks()[0].enabled = !(this.state.localStream.getVideoTracks()[0].enabled);
this.props.history.push('/')
socket.on('disconnected', () => {
this.setState({ initiator: true })
this.state.localStream.getVideoTracks()[0].enabled = !(this.state.localStream.getVideoTracks()[0].enabled);
})
}
gotDevices(deviceInfos) {
const document = ReactDOM.findDOMNode(this);
const videoElement = document.querySelector('video');
const audioInputSelect = document.querySelector('select#audioSource');
const audioOutputSelect = document.querySelector('select#audioOutput');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [audioInputSelect, audioOutputSelect, videoSelect];
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
for (let i = 0; i < deviceInfos.length; i++) {
var deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'audioinput') {
this.state.audioInputOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label}</option>)
} else if (deviceInfo.kind === 'audiooutput') {
this.state.audioOutputOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`}</option>)
} else if (deviceInfo.kind === 'videoinput') {
this.state.videoSourceOptions.push(<option key={i} value={deviceInfo.deviceId}>{deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`}</option>)
} else {
console.log('Some other kind of source/device: ', deviceInfo);
}
}
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
}
handleError(){
}
onChangeAudio = ({target}) => {
this.setState({
audioSource: target.value
})
// this.getUserMedia(this.state.audioSource)
console.log(this.state.audioSource)
console.log(this.state.localStream.getAudioTracks()[0])
}
enter = roomId => {
this.setState({ connecting: true })
const peer = this.videoCall.init(
this.state.localStream,
this.state.initiator
)
this.setState({peer})
peer.on('signal', data => {
const signal = {
room: roomId,
desc: data
}
this.state.socket.emit('signal', signal)
})
peer.on('stream', stream => {
// Got remote video stream, now showing in the video tag
this.remoteVideo.srcObject = stream
this.setState({ connecting: false, waiting: false })
})
peer.on('error', function(err) {
console.log(err)
})
}
call = otherId => {
this.videoCall.connect(otherId)
}
render() {
return (
<div className="video-wrapper">
<div className="local-video-wrapper" >
<video
autoPlay
id="localVideo"
muted
ref={video => (this.localVideo = video)}
style={this.state.waiting ? {width:"90%", height:"90%", justifyContent:"center"}: {}}
/>
{this.state.audioMute ? <div className="video-mute-logo">
<AudioMuteIcon/>
</div> : <div></div>}
{this.state.waiting ? <div></div> :
<div className="local-video-user-name">
<p>User(you)</p>
</div>
}
</div>
<video
autoPlay
className={`${
this.state.connecting || this.state.waiting ? 'hide' : ''
}`}
id="remoteVideo"
ref={video => (this.remoteVideo = video)}
/>
<div className="lower-banner">
<button className="share-screen-btn" onClick={() => {
this.getDisplay()
}}><ShareScreenIcon/></button>
<button className={this.state.videoMute ? "mute-video-btn-red" : "mute-video-btn"} onClick={() => {
this.stopStreamingVideo()
}}>{this.state.videoMute ? <VideoMuteIcon/> : <VideoIcon/>}</button>
<button className={this.state.audioMute ? "mute-Audio-btn-red" : "mute-Audio-btn"} onClick={() => {this.stopStreamingAudio()}}>
{this.state.audioMute ? <AudioMuteIcon/> : <AudioIcon/>}
</button>
<button className="leave-button" onClick={() => {this.leaveChat()}}>
<LeaveChatIcon/>
</button>
<div className="members">
<h2>Members: {this.state.waiting ? "1" : "2"}</h2>
</div>
</div>
<div className="configurations">
<div className="select">
<label htmlFor="audioSource">Audio input source: </label><select id="audioSource" onChange={this.onChangeAudio}>{this.state.audioInputOptions}</select>
</div>
<div className="select">
<label htmlFor="audioOutput">Audio output destination: </label><select id="audioOutput">{this.state.audioOutputOptions}</select>
</div>
<div className="select">
<label htmlFor="videoSource">Video source: </label><select id="videoSource">{this.state.videoSourceOptions}</select>
</div>
</div>
{this.state.connecting && (
<div className="status">
<p>Connecting...</p>
</div>
)}
</div>
)
}
}
export default Video
【问题讨论】: