@ -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 )
/ / i f ( n u l l ! = t h i s . p r o p s . p e e r . r e m o t e P e e r ) {
/ / t h i s . p r o p s . p e e r . r e m o t e P e e r . a d d I c e C a n d i d a t e ( e . c a n d i d a t e )
/ / }
/ / r t c T y p e 参 数 默 认 是 对 端 值 为 a n s w e r , 如 果 是 发 起 端 , 会 将 值 设 置 为 o f f e r
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 . remoteP eer . onicecandidate = ( e ) => {
if ( e . candidate ) {
console . log ( "B get candidate " , e . candidate )
/ / i f ( n u l l ! = t h i s . p r o p s . p e e r . l o c a l P e e r ) {
/ / t h i s . p r o p s . p e e r . l o c a l P e e r . a d d I c e C a n d i d a t e ( e . c a n d i d a t e )
/ / }
/ / r t c T y p e 参 数 默 认 是 对 端 值 为 a n s w e r , 如 果 是 发 起 端 , 会 将 值 设 置 为 o f f e r
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 . remoteP eer . 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 )
/ / l e t d a t a = {
/ / c o n t e n t T y p e : C o n s t a n t . D I A L _ V I D E O _ O N L I N E ,
/ / t y p e : C o n s t a n t . M E S S A G E _ T R A N S _ T Y P E ,
/ / }
/ / t h i s . s e n d M e s s a g e ( d a t a ) ;
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 ) ;
} ) ;
/ / 一 定 注 意 : 需 要 将 该 动 作 , 放 在 这 里 面 , 即 流 获 取 成 功 后 , 再 进 行 o f f e r 创 建 。 不 然 不 能 获 取 到 流 , 从 而 不 能 播 放 视 频 。
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 ) {
/ / c o n s o l e . l o g ( " A d e a l W e b R t c M e s s a g e g e t a n s w e r m e s s a g e a r e " , t y p e , s d p , i c e C a n d i d a t e )
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 ) {
/ / c o n s o l e . l o g ( " A d e a l W e b R t c M e s s a g e g e t a n s w e r _ i c e m e s s a g e a r e " , t y p e , s d p , i c e C a n d i d a t e )
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 ) {
/ / c o n s o l e . l o g ( " B d e a l W e b R t c M e s s a g e g e t o f f e r _ i c e m e s s a g e a r e " , t y p e , s d p , i c e C a n d i d a t e )
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" ) {
/ / c o n s o l e . l o g ( " B d e a l W e b R t c M e s s a g e g e t o f f e r m e s s a g e a r e " , t y p e , s d p , i c e C a n d i d a t e )
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 ) ;
useP eer. addTrack ( track , stream ) ;
} ) ;
/ / 一 定 注 意 : 需 要 将 该 动 作 , 放 在 这 里 面 , 即 流 获 取 成 功 后 , 再 进 行 a n s w e r 创 建 。 不 然 不 能 获 取 到 流 , 从 而 不 能 播 放 视 频 。
const offerSdp = new RTCSessionDescription ( { type , sdp } ) ;
peer . setRemoteDescription ( offerSdp )
useP eer. 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 ) ) ,
}
}