node.js - WebRTC P2P 与浏览器和 Android (Flutter) 无法正常工作,尽管一切似乎都很好
问题描述
我尝试用flutter建立一个WebRTC P2P视频通信,一个节点后端作为信令服务器和kurento媒体服务器。
当我结合 Kurento Demo 运行该应用程序时,一切正常,但一旦我尝试实现自己的后端,视频流就不会启动,尽管日志消息表明一切正常。
如果需要更多输入来找到解决方案,请告诉我。
相关代码片段
网络前端:
call(username) {
const wrapper = this;
const remoteVideo = this.videoOutputFactory();
if (!remoteVideo) {
console.error('videoOutput not found');
}
const options = {
'remoteVideo': document.getElementById('testVideo'),
onicecandidate(candidate) {
console.debug('onicecandidate', candidate);
wrapper.rpc.onIceCandidate(candidate);
},
mediaConstraints: wrapper.remoteMediaConstraints
};
console.debug('WebRtcWrapper.call: options', options);
return new Promise((resolve, reject) => {
console.log('Creating WebRtcPeer');
this.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, (error) => {
if (error) {
console.error('Error while creating WebRtcPeer', error);
reject(error);
return;
}
console.log('Generating WebRtcPeer offer');
this.webRtcPeer.generateOffer((offerError, offerSdp) => {
if (offerError) {
console.error('Error while generating WebRtcPeer offer', error);
reject(error);
return;
}
this.rpc.call(username, offerSdp).then((res) => {
console.log("Got call answer - Generated-SDPOffer: " + offerSdp);
if (res.response === 'rejected') {
console.log('Call rejected by peer');
reject(res.rejectionMessage);
return;
}
console.log('Processing peer SDP answer', res.sdpAnswer);
this.webRtcPeer.processAnswer(res.sdpAnswer);
});
});
});
});
}
应用程序
TestController._()
: _channel = IOWebSocketChannel.connect('wss://server.marcostephan.at:443') {
_peer = jsonrpc.Peer(_channel.cast<String>());
_peer.registerMethod(
'rtc.incomingCall', (jsonrpc.Parameters message) async => await _onIncomingCall(message));
_peer.registerMethod(
'rtc.offerIceCandidate', (jsonrpc.Parameters message) => _onOfferIceCandidate(message));
_peer.registerMethod(
'rtc.startCommunication', (jsonrpc.Parameters message) => _onStartCommunication(message));
_peer.registerMethod('conn.heartbeat', (jsonrpc.Parameters message) => "");
_peer.registerFallback((jsonrpc.Parameters params) =>
print('Unknown request [${params.method}]: ${params.value}'));
_peer.listen();
_peer.sendRequest("auth.login", {'username': 'john.doe', 'role': 'actor'});
_peer.sendNotification("disp.helpMe", {'category': 'spareParts'});
}
_onIncomingCall(jsonrpc.Parameters message) async {
try{
print('Incoming call from ${message['username'].value}');
if (this.onStateChange != null) {
this.onStateChange(SignalingState.CallStateNew);
}
await _createPeerConnection();
RTCSessionDescription s = await _peerConnection
.createOffer(_constraints);
_peerConnection.setLocalDescription(s);
return {
'from': message['username'].value,
'callResponse': 'accept',
'sdpOffer': s.sdp
};
}
catch(e){
print('TestController._onIncomingCall: ERROR: $e');
}
}
_onOfferIceCandidate(jsonrpc.Parameters message) {
try{
var candidateMap = message['candidate'].value;
print('Received IceCandidate $candidateMap');
if (_peerConnection != null) {
RTCIceCandidate candidate = new RTCIceCandidate(candidateMap['candidate'],
candidateMap['sdpMid'], candidateMap['sdpMLineIndex']);
_peerConnection.addCandidate(candidate);
}
}
catch(e){
print('TestController._onOfferIceCandidate: ERROR: $e');
}
}
_onStartCommunication(jsonrpc.Parameters message) {
try{
_peerConnection.setRemoteDescription(
RTCSessionDescription(message['sdpAnswer'].value, 'answer'));
}
catch(e){
print('TestController._onStartCommunication: ERROR: $e');
}
}
_createPeerConnection() async {
_localStream = await _createStream();
RTCPeerConnection pc = await createPeerConnection(_iceServers, _config);
_peerConnection = pc;
pc.addStream(_localStream);
pc.onAddStream = (stream) {
if (this.onRemoteStream != null) this.onRemoteStream(stream);
//_remoteStreams.add(stream);
};
pc.onIceConnectionState = (state) {
print(
'TestController._createPeerConnection: onIceConnectionState: $state');
};
pc.onIceCandidate = (candidate) {
_peer.sendNotification("rtc.onIceCandidate", {
'candidate': {
'sdpMLineIndex': candidate.sdpMlineIndex,
'sdpMid': candidate.sdpMid,
'candidate': candidate.candidate
}
});
};
}
Future<MediaStream> _createStream() async {
final Map<String, dynamic> mediaConstraints = {
'audio': true,
'video': {
'mandatory': {
'minWidth': '1980',
'minHeight': '1020',
'minFrameRate': '30',
},
'facingMode': 'environment',
'optional': [],
}
};
MediaStream stream = await navigator.getUserMedia(mediaConstraints);
if (this.onLocalStream != null) {
this.onLocalStream(stream);
}
return stream;
}
final Map<String, dynamic> _iceServers = {
'iceServers': [
{'url': 'stun:stun.l.google.com:19302'},
]
};
final Map<String, dynamic> _config = {
'mandatory': {},
'optional': [
{'DtlsSrtpKeyAgreement': true},
],
};
final Map<String, dynamic> _constraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true,
},
'optional': [],
};
日志
网页前端
应用程序
解决方案
推荐阅读
- c++ - 如何将不属于 QObject 的枚举暴露给 QJSEngine?
- oauth-2.0 - SPA + 后端 + Office365 SSO
- qemu - QEMU riscv32 virt machine,如何访问屏幕?
- angular - Angular 7:单击单选按钮时显示值
- java - 需要一个正则表达式“仅在 $ 符号之后我应该接受我给出的特定字符串
- reactjs - 渲染如何与 React 中的一组组件一起工作
- python - conda-build 错误地抱怨 meta.yaml 中不包含依赖项
- php - 创建 apache 别名而不访问 .conf 文件
- javascript - heroku 上上传的前端-后端 docker 图像之间的通信
- c++ - 如何修复 C++ 中的“指针和整数之间的比较”错误?