Browse Source

feat: video chat for remote

feat_video_fine
kone-net 11 months ago
parent
commit
99da56a104
  1. 187
      src/chat/Panel.jsx
  2. 11
      src/chat/panel/right/component/ChatAudioOline.jsx
  3. 51
      src/chat/panel/right/component/ChatVideoOline.jsx
  4. 9
      src/chat/redux/module/panel.jsx

187
src/chat/Panel.jsx

@ -9,6 +9,7 @@ import { @@ -9,6 +9,7 @@ import {
Modal
} from 'antd';
import {
VideoCameraOutlined,
PoweroffOutlined,
FileOutlined,
} from '@ant-design/icons';
@ -24,7 +25,8 @@ import { connect } from 'react-redux' @@ -24,7 +25,8 @@ import { connect } from 'react-redux'
import { actions } from './redux/module/panel'
var socket = null;
var peer = null;
var remotePeer = null;
var localPeer = null;
var lockConnection = false;
var heartCheck = {
@ -96,7 +98,24 @@ class Panel extends React.Component { @@ -96,7 +98,24 @@ class Panel extends React.Component {
*/
connection = () => {
console.log("to connect...")
peer = new RTCPeerConnection();
const configuration = {
iceServers: [
{
"urls": "stun:stun.1.google.com:19302"
},
{
"urls": "stun:stun.2.google.com:19302"
}
]
};
remotePeer = new RTCPeerConnection(configuration);
localPeer = new RTCPeerConnection(configuration);
let peer = {
...this.props.peer,
localPeer: localPeer,
remotePeer: remotePeer
}
this.props.setPeer(peer);
var image = document.getElementById('receiver');
socket = new WebSocket("ws://" + Params.IP_PORT + "/socket.io?user=" + this.props.match.params.user)
@ -202,12 +221,44 @@ class Panel extends React.Component { @@ -202,12 +221,44 @@ class Panel extends React.Component {
* webrtc 绑定事件
*/
webrtcConnection = () => {
this.props.peer.localPeer.onicecandidate = (e) => {
if (e.candidate) {
console.log("A get candidate ", e.candidate)
// if (null != this.props.peer.remotePeer) {
// this.props.peer.remotePeer.addIceCandidate(e.candidate)
// }
// rtcTypeansweroffer
let candidate = {
type: 'offer_ice',
iceCandidate: e.candidate
}
let message = {
content: JSON.stringify(candidate),
type: Constant.MESSAGE_TRANS_TYPE,
}
this.sendMessage(message);
}
};
this.props.peer.localPeer.ontrack = (e) => {
if (e && e.streams) {
let remoteVideo = document.getElementById("remoteVideoReceiver");
remoteVideo.srcObject = e.streams[0];
}
};
/**
* 对等方收到ice信息后通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理
* @param {候选人信息} e
*/
peer.onicecandidate = (e) => {
this.props.peer.remotePeer.onicecandidate = (e) => {
if (e.candidate) {
console.log("B get candidate ", e.candidate)
// if (null != this.props.peer.localPeer) {
// this.props.peer.localPeer.addIceCandidate(e.candidate)
// }
// rtcTypeansweroffer
let candidate = {
type: 'answer_ice',
@ -226,7 +277,7 @@ class Panel extends React.Component { @@ -226,7 +277,7 @@ class Panel extends React.Component {
* 当连接成功后从里面获取语音视频流
* @param {包含语音视频流} e
*/
peer.ontrack = (e) => {
this.props.peer.remotePeer.ontrack = (e) => {
if (e && e.streams) {
if (this.state.onlineType === 1) {
let remoteVideo = document.getElementById("remoteVideoReceiver");
@ -239,25 +290,92 @@ class Panel extends React.Component { @@ -239,25 +290,92 @@ class Panel extends React.Component {
};
}
startVideoOnline = () => {
if (!this.checkMediaPermisssion()) {
return;
}
let media = {
...this.props.media,
showMediaPanel: true,
}
this.props.setMedia(media)
// let data = {
// contentType: Constant.DIAL_VIDEO_ONLINE,
// type: Constant.MESSAGE_TRANS_TYPE,
// }
// this.sendMessage(data);
this.sendVideoData();
}
sendVideoData = () => {
let preview = document.getElementById("localVideoReceiver");
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true,
}).then((stream) => {
preview.srcObject = stream;
stream.getTracks().forEach(track => {
this.props.peer.localPeer.addTrack(track, stream);
});
// offer
this.props.peer.localPeer.createOffer()
.then(offer => {
console.log("A send offer sdp ", offer)
this.props.peer.localPeer.setLocalDescription(offer);
let data = {
contentType: Constant.VIDEO_ONLINE,
content: JSON.stringify(offer),
type: Constant.MESSAGE_TRANS_TYPE,
}
this.sendMessage(data);
});
});
this.setState({
mediaPanelDrawerVisible: true
})
}
/**
* 处理webrtc消息包括获取请求方的offer回应answer等
* @param {消息内容}} messagePB
*/
dealWebRtcMessage = (messagePB) => {
console.log("props peer =================> ", this.props.peer)
if (messagePB.contentType >= Constant.DIAL_MEDIA_START && messagePB.contentType <= Constant.DIAL_MEDIA_END) {
this.dealMediaCall(messagePB);
return;
}
const { type, sdp, iceCandidate } = JSON.parse(messagePB.content);
if (type === "answer") {
if (type === "answer" && null != sdp) {
// console.log("A dealWebRtcMessage get answer message are ", type, sdp, iceCandidate)
console.log("A dealWebRtcMessage get answer message are ", type)
const answerSdp = new RTCSessionDescription({ type, sdp });
this.props.peer.localPeer.setRemoteDescription(answerSdp)
} else if (type === "answer_ice") {
console.log("answerSdp is ", answerSdp)
if (null != answerSdp) {
this.props.peer.localPeer.setRemoteDescription(answerSdp)
}
}
else if (type === "answer_ice" && null != iceCandidate) {
// console.log("A dealWebRtcMessage get answer_ice message are ", type, sdp, iceCandidate)
console.log("A dealWebRtcMessage get answer_ice message are ", type)
this.props.peer.localPeer.addIceCandidate(iceCandidate)
} else if (type === "offer_ice") {
peer.addIceCandidate(iceCandidate)
} else if (type === "offer") {
} else if (type === "offer_ice" && null != iceCandidate) {
// console.log("B dealWebRtcMessage get offer_ice message are ", type, sdp, iceCandidate)
console.log("B dealWebRtcMessage get offer_ice message are ", type)
if (null != this.props.peer.remotePeer) {
this.props.peer.remotePeer.addIceCandidate(iceCandidate)
}
}
else if (type === "offer") {
// console.log("B dealWebRtcMessage get offer message are ", type, sdp, iceCandidate)
console.log("B dealWebRtcMessage get offer message are ", type)
if (!this.checkMediaPermisssion()) {
return;
}
@ -270,6 +388,12 @@ class Panel extends React.Component { @@ -270,6 +388,12 @@ class Panel extends React.Component {
this.setState({
onlineType: 1,
})
let media = {
...this.props.media,
showMediaPanel: true,
}
this.props.setMedia(media)
} else {
preview = document.getElementById("audioPhone");
this.setState({
@ -283,24 +407,32 @@ class Panel extends React.Component { @@ -283,24 +407,32 @@ class Panel extends React.Component {
video: video,
}).then((stream) => {
preview.srcObject = stream;
let usePeer = this.props.peer.remotePeer
if (null == usePeer) {
return;
}
stream.getTracks().forEach(track => {
peer.addTrack(track, stream);
usePeer.addTrack(track, stream);
});
// answer
const offerSdp = new RTCSessionDescription({ type, sdp });
peer.setRemoteDescription(offerSdp)
usePeer.setRemoteDescription(offerSdp)
.then(() => {
peer.createAnswer().then(answer => {
peer.setLocalDescription(answer)
let message = {
content: JSON.stringify(answer),
type: Constant.MESSAGE_TRANS_TYPE,
messageType: messagePB.contentType
}
this.sendMessage(message);
})
usePeer.createAnswer()
.then(answer => {
usePeer.setLocalDescription(answer)
let message = {
content: JSON.stringify(answer),
type: Constant.MESSAGE_TRANS_TYPE,
messageType: messagePB.contentType
}
this.sendMessage(message);
})
});
});
}
@ -529,10 +661,18 @@ class Panel extends React.Component { @@ -529,10 +661,18 @@ class Panel extends React.Component {
checkMediaPermisssion={this.checkMediaPermisssion}
/>
</Col>
<Tooltip title="视频聊天">
<Button
shape="circle"
onClick={this.startVideoOnline}
style={{ marginRight: 10 }}
icon={<VideoCameraOutlined />}
/>
</Tooltip>
</Row>
<Drawer width='820px' forceRender={true} title="媒体面板" placement="right" onClose={this.mediaPanelDrawerOnClose} visible={this.props.media.showMediaPanel}>
<Drawer width='820px' forceRender={true} title="媒体面板" placement="right" onClose={this.mediaPanelDrawerOnClose} open={this.props.media.showMediaPanel}>
<Tooltip title="结束视频语音">
<Button
shape="circle"
@ -582,6 +722,7 @@ function mapDispatchToProps(dispatch) { @@ -582,6 +722,7 @@ function mapDispatchToProps(dispatch) {
setMessageList: (data) => dispatch(actions.setMessageList(data)),
setSocket: (data) => dispatch(actions.setSocket(data)),
setMedia: (data) => dispatch(actions.setMedia(data)),
setPeer: (data) => dispatch(actions.setPeer(data)),
}
}

11
src/chat/panel/right/component/ChatAudioOline.jsx

@ -24,7 +24,16 @@ class ChatAudioOline extends React.Component { @@ -24,7 +24,16 @@ class ChatAudioOline extends React.Component {
}
componentDidMount() {
localPeer = new RTCPeerConnection();
const configuration = {
iceServers: [
{
"urls": "stun:stun.1.google.com:19302"
}, {
"urls": "stun:stun.2.google.com:19302"
}
]
};
localPeer = new RTCPeerConnection(configuration);
let peer = {
...this.props.peer,
localPeer: localPeer

51
src/chat/panel/right/component/ChatVideoOline.jsx

@ -15,7 +15,7 @@ import * as Constant from '../../../common/constant/Constant' @@ -15,7 +15,7 @@ import * as Constant from '../../../common/constant/Constant'
import { connect } from 'react-redux'
import { actions } from '../../../redux/module/panel'
let localPeer = null;
// let localPeer = null;
class ChatVideoOline extends React.Component {
constructor(props) {
super(props)
@ -27,19 +27,21 @@ class ChatVideoOline extends React.Component { @@ -27,19 +27,21 @@ class ChatVideoOline extends React.Component {
componentDidMount() {
// const configuration = {
// iceServers: [{
// "url": "stun:23.21.150.121"
// }, {
// "url": "stun:stun.l.google.com:19302"
// }]
// iceServers: [
// {
// "urls": "stun:stun.1.google.com:19302"
// }, {
// "urls": "stun:stun.2.google.com:19302"
// }
// ]
// };
localPeer = new RTCPeerConnection();
let peer = {
...this.props.peer,
localPeer: localPeer
}
this.props.setPeer(peer);
this.webrtcConnection();
// localPeer = new RTCPeerConnection(configuration);
// let peer = {
// ...this.props.peer,
// localPeer: localPeer
// }
// this.props.setPeer(peer);
// this.webrtcConnection();
}
videoIntervalObj = null;
/**
@ -104,13 +106,14 @@ class ChatVideoOline extends React.Component { @@ -104,13 +106,14 @@ class ChatVideoOline extends React.Component {
}).then((stream) => {
preview.srcObject = stream;
stream.getTracks().forEach(track => {
localPeer.addTrack(track, stream);
this.props.peer.localPeer.addTrack(track, stream);
});
// offer
localPeer.createOffer()
this.props.peer.localPeer.createOffer()
.then(offer => {
localPeer.setLocalDescription(offer);
console.log("A send offer sdp ", offer)
this.props.peer.localPeer.setLocalDescription(offer);
let data = {
contentType: Constant.VIDEO_ONLINE,
content: JSON.stringify(offer),
@ -129,13 +132,21 @@ class ChatVideoOline extends React.Component { @@ -129,13 +132,21 @@ class ChatVideoOline extends React.Component {
* webrtc 绑定事件
*/
webrtcConnection = () => {
if (null == this.props.peer.localPeer) {
return;
}
/**
* 对等方收到ice信息后通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理
* @param {候选人信息} e
*/
localPeer.onicecandidate = (e) => {
this.props.peer.localPeer.onicecandidate = (e) => {
if (e.candidate) {
console.log("A get candidate ", e.candidate)
// if (null != this.props.peer.remotePeer) {
// this.props.peer.remotePeer.addIceCandidate(e.candidate)
// }
// rtcTypeansweroffer
let candidate = {
type: 'offer_ice',
@ -154,7 +165,7 @@ class ChatVideoOline extends React.Component { @@ -154,7 +165,7 @@ class ChatVideoOline extends React.Component {
* 当连接成功后从里面获取语音视频流
* @param {包含语音视频流} e
*/
localPeer.ontrack = (e) => {
this.props.peer.localPeer.ontrack = (e) => {
if (e && e.streams) {
let remoteVideo = document.getElementById("remoteVideoSender");
remoteVideo.srcObject = e.streams[0];
@ -217,7 +228,7 @@ class ChatVideoOline extends React.Component { @@ -217,7 +228,7 @@ class ChatVideoOline extends React.Component {
title="媒体面板"
placement="right"
onClose={this.mediaPanelDrawerOnClose}
visible={this.state.mediaPanelDrawerVisible}
open={this.state.mediaPanelDrawerVisible}
>
<Tooltip title="结束视频语音">
<Button
@ -234,7 +245,7 @@ class ChatVideoOline extends React.Component { @@ -234,7 +245,7 @@ class ChatVideoOline extends React.Component {
<Modal
title="视频电话"
visible={this.state.videoCallModal}
open={this.state.videoCallModal}
onOk={this.handleOk}
onCancel={this.handleCancel}
okText="确认"

9
src/chat/redux/module/panel.jsx

@ -27,6 +27,7 @@ export const types = { @@ -27,6 +27,7 @@ export const types = {
SOCKET_SET: 'SOCKET/SET',
MEDIA_SET: 'MEDIA/SET',
PEER_SET: 'PEER/SET',
REMOTE_PEER_SET: 'REMOTE_PEER/SET',
}
export const actions = {
@ -54,6 +55,10 @@ export const actions = { @@ -54,6 +55,10 @@ export const actions = {
type: types.PEER_SET,
peer: peer
}),
setRemotePeer: (peer) => ({
type: types.REMOTE_PEER_SET,
peer: peer
}),
}
const PanelReducer = (state = initialState, action) => {
@ -69,6 +74,10 @@ const PanelReducer = (state = initialState, action) => { @@ -69,6 +74,10 @@ const PanelReducer = (state = initialState, action) => {
case types.MEDIA_SET:
return { ...state, media: action.media }
case types.PEER_SET:
console.log("PanelReducer local peer --------->", state, action)
return { ...state, peer: action.peer }
case types.REMOTE_PEER_SET:
console.log("PanelReducer remote peer --------->", state, action)
return { ...state, peer: action.peer }
default:
return state

Loading…
Cancel
Save