flutter-webrtc / flutter-webrtc

WebRTC plugin for Flutter Mobile/Desktop/Web
MIT License
4.09k stars 1.11k forks source link

Connection with phone tethering not working. #433

Closed danteCarvalho closed 3 years ago

danteCarvalho commented 3 years ago

I am trying to test somethings with webrtc, and using the https://github.com/meshenger-app/meshenger-android app the connection works just fine with the phone therering. But when i try with the https://pub.dev/packages/flutter_webrtc it only works with wifi. Is there a why to this?

cloudwebrtc commented 3 years ago

You need an available turn server.

danteCarvalho commented 3 years ago

Well in may case, i need the app to work without internet, and for a turn server i need internet, isn't? I just tryed with the Meshenger app and it works. phone 1 is with: tethering on wifi off mobile data off. phone 2 is with: tethering off wifi on (connected on phone 1) mobile data off. with this config, video and audio works. but with the flutter webrtc it does not work.

danteCarvalho commented 3 years ago

Ok my code basicaly is: send and receive offer, send and receive answer, start rtc and the local server


  Map<String, dynamic> configuration = {
    "iceServers": [
      // {"url": "stun:stun.l.google.com:19302"},
//      {"url": "stun:stun1.l.google.com:19302"},
//      {"url": "stun:stun2.l.google.com:19302"},
//      {"url": "stun:stun3.l.google.com:19302"},
//      {"url": "stun:stun4.l.google.com:19302"},
    ],
    "iceTransportPolicy": "all",
    "bundlePolicy": "max-bundle",
//    "rtcpMuxPolicy": "negotiate",
    "iceCandidatePoolSize": 50,
    // "tcpCandidatePolicy": "disabled",
    "candidateNetworkPolicy": "all",
//    "keyType": "RSA",
  };

  final Map<String, dynamic> constraintsTrue = {
    'mandatory': {
      'OfferToReceiveAudio': false,
      'OfferToReceiveVideo': true,
    },
    'optional': [
//      {'DtlsSrtpKeyAgreement': false}
    ],
  };

  final Map<String, dynamic> mediaConstraints = {
    'audio': false,
    'video': {
      'mandatory': {
        'minWidth': '640',
        // Provide your own width, height and frame rate here
        'minHeight': '480',
        'minFrameRate': '30',
      },
      'facingMode': 'user',
      'optional': [],
    }
  };

startRtc() async {
    _peerConnection = await createPeerConnection(configuration, constraintsTrue);
    _peerConnection.onSignalingState = _onSignalingState;
    _peerConnection.onIceGatheringState = _onIceGatheringState;
    _peerConnection.onIceConnectionState = _onIceConnectionState;
    _peerConnection.onAddStream = _onAddStream;
    _peerConnection.onRemoveStream = _onRemoveStream;
    _peerConnection.onIceCandidate = _onCandidate;
    _peerConnection.onRenegotiationNeeded = _onRenegotiationNeeded;

    localStream = await navigator.getDisplayMedia(mediaConstraints);
    await _peerConnection.addStream(localStream);

    RTCSessionDescription rtcSessionDescription = await _peerConnection.createOffer(constraintsTrue);
    await _peerConnection.setLocalDescription(rtcSessionDescription);
    sdp = rtcSessionDescription.sdp;
  }

  sendOffer() async {
    if (sdp == null) {
      await startRtc();
    }
    String ips = await platform.invokeMethod('getIps');
    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
    AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
    var split = ips.split(" ");
    var repo = Modular.get<EnviarOfertaRepository>();
    for (var ip in split) {
      if (ip.contains(".")) {
        var ipRange = ip.substring(0, ip.lastIndexOf(".") + 1);
        for (int i = 0; i < 255; i++) {
          var ip2 = ipRange + i.toString();
          if (ip == ip2) {
            continue;
          }
          Map map = Map();
          map["sdp"] = sdp;
          map["modelo"] = androidInfo.model;
          map["ip"] = ip;
          map["candidatos"] = candidates;
          repo.fetchPost(ip2, map);
        }
      } else {
        Map map = Map();
        map["sdp"] = sdp;
        map["modelo"] = androidInfo.model;

        map["ip"] = ip;
        map["candidatos"] = candidates;
        repo.fetchPost("[$ip]", map);
      }
    }
  }

  receiveOffer(request) async {
    String s = await utf8.decoder.bind(request).join();
    Map map = json.decode(s);
    if (!lista.contains(map)) {
      lista.add(map);
    }
  }

  sendAnswer(index) async {
    await close1();
    print('WWWWWW RECEIVING VIDEO');
    var item = lista[index];
    _peerConnection = await createPeerConnection(configuration, constraintsTrue);
    _peerConnection.onSignalingState = _onSignalingState;
    _peerConnection.onIceGatheringState = _onIceGatheringState;
    _peerConnection.onIceConnectionState = _onIceConnectionState;
    _peerConnection.onAddStream = _onAddStream;
    _peerConnection.onRemoveStream = _onRemoveStream;
    _peerConnection.onIceCandidate = _onCandidate;
    _peerConnection.onRenegotiationNeeded = _onRenegotiationNeeded;

    var rtcSessionDescription = new RTCSessionDescription(item['sdp'], "offer");

    await _peerConnection.setRemoteDescription(rtcSessionDescription);
    List candidatos = item["candidatos"];
    for (var candidato in candidatos) {
      RTCIceCandidate candidate = new RTCIceCandidate(candidato['candidate'], candidato['sdpMid'], candidato['sdpMLineIndex']);
      await _peerConnection.addCandidate(candidate);
    }

    RTCSessionDescription rtcSessionDescription2 = await _peerConnection.createAnswer(constraintsTrue);
    await _peerConnection.setLocalDescription(rtcSessionDescription2);

    var remoteIp = item["ip"];

    Future.delayed(Duration(seconds: 3), () async {
      Map map = Map();
      map["sdp"] = rtcSessionDescription2.sdp;

      map["candidatos"] = candidatos;

      var repo = Modular.get<EnviarRespostaRepository>();

      repo.fetchPost(remoteIp, map);
    });
  }

  receiveAnswer(request) async {
    print('WWWWWW SENDING VIDEO');
    String s = await utf8.decoder.bind(request).join();
    Map map = json.decode(s);

    var rtcSessionDescription = new RTCSessionDescription(map["sdp"], "answer");
    await _peerConnection.setRemoteDescription(rtcSessionDescription);

    List candidatos = map["candidatos"];
    for (var candidato in candidatos) {
      RTCIceCandidate candidate = new RTCIceCandidate(candidato['candidate'], candidato['sdpMid'], candidato['sdpMLineIndex']);
      await _peerConnection.addCandidate(candidate);
    }

    cameras = await availableCameras();
    cameraController = CameraController(cameras[0], ResolutionPreset.medium);
    await cameraController.initialize().then((_) {});
    showCamera = true;

  }

await for (HttpRequest request in server) {
      try {
        print("WWWWWW " + request.uri.path);
        if (request.method == "POST") {
          request.response.headers.add("Access-Control-Allow-Origin", "*");
          if (request.uri.path == "/webserver/receberOferta") {
            await receiveOffer(request);
          } else if (request.uri.path == "/webserver/receberResposta") {
            await receiveAnswer(request);
          }
        } 
      } catch (e) {
        print(e);
        Map obj = Map();
        obj["mensagem"] = e.toString();
        String myJson = json.encode(obj);
        request.response.write(myJson);
      } finally {
        await request.response.close();
      }
    }

what is weird is that the _onAddStream method woks, it receives the stream, but its like there is no data on the stream when is on tethering, but works fine on wifi, and i see no error/exception.

working exemple here https://bitbucket.org/dantecarvalho/testewebrtc/src/master/

cloudwebrtc commented 3 years ago

A key point, you need to ensure that the candidate packet is the timing of receiving and sending. PeerA’s candidate sometimes arrives earlier than its own offer sdp, so you may need to store the candidate in a queue in PeerB first, wait until setRemoteDescription is completed, and then addCandidate, so as to ensure connectivity.

danteCarvalho commented 3 years ago

Ok i changed the code, now on PeerB i set remote and local description

    var rtcSessionDescription = new RTCSessionDescription(item['sdp'], "offer");
    await _peerConnection.setRemoteDescription(rtcSessionDescription);

    RTCSessionDescription rtcSessionDescription2 = await _peerConnection.createAnswer(constraintsTrue);
    await _peerConnection.setLocalDescription(rtcSessionDescription2);

then wait for the gathering state to be completed

_peerConnection.iceGatheringState == RTCIceGatheringState.RTCIceGatheringStateComplete

then i add the candidates received from the GatheringState and the cadidates received from PeerA, and start rendering the stream received on the onAddStream.

and finaly i send the answer to PeerA.

But again it only works on the wifi, on the tethering nothing shows.

Could you give it a try with this code? https://bitbucket.org/dantecarvalho/testewebrtc/src/master/

danteCarvalho commented 3 years ago

I noticed that in one peer the ice connection and the peer connection don't change from checking and connecting

I/flutter (15029): WWWWW Ice Connection I/flutter (15029): RTCIceConnectionState.RTCIceConnectionStateChecking I/flutter (15029): WWWWW Peer connection I/flutter (15029): RTCPeerConnectionState.RTCPeerConnectionStateConnecting I/flutter (15029): WWWWW Ice Connection I/flutter (15029): RTCIceConnectionState.RTCIceConnectionStateChecking I/flutter (15029): WWWWW Peer connection I/flutter (15029): RTCPeerConnectionState.RTCPeerConnectionStateConnecting

and on the other peer

I/flutter (17595): WWWWW Ice Connection I/flutter (17595): RTCIceConnectionState.RTCIceConnectionStateChecking I/flutter (17595): WWWWW Peer connection I/flutter (17595): null I/flutter (17595): WWWWW Ice Connection I/flutter (17595): RTCIceConnectionState.RTCIceConnectionStateChecking I/flutter (17595): WWWWW Peer connection I/flutter (17595): null

danteCarvalho commented 3 years ago

Yeess i finally solve it. After alot of digging i saw this post https://www.talkend.net/post/52763.html, it says that you need to turn off the network monitor when the PeerConnectionFactory is created because the monitor throws this error:

android_network_monitor.cc Get an unknown type for the interface

I gues this is because the interface names for tethering on android phones varys depending on phone, model, brand etc..

So after changing the factory initialization like this, it worked:

    PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder();
    PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
    options.disableNetworkMonitor = true;
    builder.setOptions(options);

    mFactory = builder.setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglContext, false, true))
        .setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglContext))
        .setAudioDeviceModule(audioDeviceModule)
        .createPeerConnectionFactory();

You should make an option to turn off the network monitor for the people that don't need it.

pravinkumarputta commented 3 years ago

Yeess i finally solve it. After alot of digging i saw this post https://www.talkend.net/post/52763.html, it says that you need to turn off the network monitor when the PeerConnectionFactory is created because the monitor throws this error:

android_network_monitor.cc Get an unknown type for the interface

I gues this is because the interface names for tethering on android phones varys depending on phone, model, brand etc..

So after changing the factory initialization like this, it worked:

    PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder();
    PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
    options.disableNetworkMonitor = true;
    builder.setOptions(options);

    mFactory = builder.setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglContext, false, true))
        .setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglContext))
        .setAudioDeviceModule(audioDeviceModule)
        .createPeerConnectionFactory();

You should make an option to turn off the network monitor for the people that don't need it.

My issue is similar to yours. In my case, it works only with hotspot/tethering on android and ios. It's not working on WIFI and Mobile data.

Can you guide me to configure the above solution and where?

danteCarvalho commented 3 years ago

For now you need to have a copy of the flutter-webrtc package on your computer and change the file .../flutter_webrtc-0.5.7/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java on the ensureInitialized method where the factory is created. Thats it, you don't need to change anything on your code. Even VPN works with this change.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

danteCarvalho commented 3 years ago

@cloudwebrtc can you make an option to configure this?

options.disableNetworkMonitor = true;

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Stumblinbear commented 1 year ago

This is still an issue--the patch mentioned does work, but I have to maintain my own fork. Is there any way to get this in a release?

zhubinsheng commented 3 days ago

but “options.disableNetworkMonitor = true;” i test not work