首页 > 解决方案 > 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': [],
  };

日志

网页前端

巴斯宾

应用程序

巴斯宾

标签: node.jswebrtckurento

解决方案


推荐阅读