Code doesn't even enter the onIceCandiate() while answering the SDP for webRTC in flutter

178

So, I can clearly see that there you haven't set any local description for the remote user who is going to answer this call.

_peerConnection.setLocalDescription(description2);

Hope this might help!

Share:
178
hamere
Author by

hamere

Updated on December 22, 2022

Comments

  • hamere
    hamere over 1 year

    The code flow doesn't even enter the onIceCandidate function while answering the SDP for webRTC connection. The webRTC is used for Voice calling for VOIP in android and I have also setted up TURN server with viagene website.

    import 'dart:convert';
    
    import 'package:flutter/material.dart';
    import 'package:flutter_webrtc/flutter_webrtc.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'package:cloud_firestore/cloud_firestore.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: MyHomePage(title: 'WebRTC lets learn together'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      CollectionReference firebaseInstance =
          FirebaseFirestore.instance.collection("dmeet");
      RTCPeerConnection _peerConnection;
    
      MediaStream _localStream;
      RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
      var docId = TextEditingController();
      var l;
    
      var document;
    
      _createOfferSdp() async {
        RTCSessionDescription description =
            await _peerConnection.createOffer({'offerToReceiveAudio': 1});
        Map<String, dynamic> session = {"sdp": description.sdp};
        document = firebaseInstance.doc();
        document.collection("sdp").doc("offersdp").set(session);
        await _peerConnection.setLocalDescription(description);
        document.collection("icecandidate").snapshots().listen((result) async {
          dynamic candidate = new RTCIceCandidate(
              result['candidate'], result['sdpMid'], result['sdpMlineIndex']);
          await _peerConnection.addCandidate(candidate);
        });
        print(session);
        _peerConnection.onIceCandidate = (event) {
          if (event.candidate != null) {
            Map<String, dynamic> icecandidate = {
              "candidate": event.candidate,
              "sdpMid": event.sdpMid,
              "sdpMlineIndex": event.sdpMlineIndex
            };
            document.collection("candidate").doc().set(icecandidate);
          }
        };
      }
    
      bool remotesaved = false;
    
      _createAnswerSdp() async {
        _peerConnection.onIceCandidate = (event) {
          print("Candiate ${event.candidate}");
          if (event.candidate != null) {
            // Map<String, dynamic> icecandidate = {
            //   "candidate": event.candidate,
            //   "sdpMid": event.sdpMid,
            //   "sdpMlineIndex": event.sdpMlineIndex
            // };
            // document.collection("candidate").doc().set(icecandidate);
            print("Candidate: ${event.candidate}");
          }
        };
        firebaseInstance
            .doc(docId.text)
            .collection("sdp")
            .doc("offersdp")
            .get()
            .then((value) async {
          var remoteSession = value.data()["sdp"];
          RTCSessionDescription description1 =
              RTCSessionDescription(remoteSession, "offer");
          await _peerConnection
              .setRemoteDescription(description1)
              .then((value) async {
            RTCSessionDescription description2 =
                await _peerConnection.createAnswer({'offerToReceiveAudio': 1});
            Map<String, dynamic> session = {"sdp": description2.sdp};
            firebaseInstance
                .doc(docId.text)
                .collection("sdp")
                .doc("answersdp")
                .set(session);
    
            final iceCandidate = await firebaseInstance
                 .doc(docId.text)
                 .collection("candidate")
                 .get();
            iceCandidate.docs.forEach((element) async {
              print("Candidate ${element.data()["candidate"]}");
              dynamic candidate = RTCIceCandidate(element.data()['candidate'],
                   element.data()['sdpMid'], element.data()['sdpMlineIndex']);
               await _peerConnection.addCandidate(candidate);
             });
          });
        });
      }
    
      showAlertDialog(BuildContext context) {
        // set up the buttons
        Widget cancelButton = FlatButton(
          child: Text("Cancel"),
          onPressed: () {},
        );
        Widget continueButton = FlatButton(
          child: Text("Continue"),
          onPressed: _createAnswerSdp,
        );
    
        // set up the AlertDialog
        AlertDialog alert = AlertDialog(
          title: Text("AlertDialog"),
          content: TextField(
            controller: docId,
          ),
          actions: [
            cancelButton,
            continueButton,
          ],
        );
    
        // show the dialog
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return alert;
          },
        );
      }
    
      initRenderer() async {
        await _remoteRenderer.initialize();
      }
    
      @override
      void initState() {
        _createPeerConnection().then((pc) {
          _peerConnection = pc;
        });
        initRenderer();
        // _localStream.initialize();
        super.initState();
      }
    
      @override
      void dispose() {
        _remoteRenderer.dispose();
        super.dispose();
      }
    
      _getUserMedia() async {
        final Map<String, dynamic> mediaConstraints = {
          'audio': true,
          'video': false,
        };
    
        MediaStream stream = await navigator.getUserMedia(mediaConstraints);
    
        // _localStream = stream;
    
        // _peerConnection.addStream(stream);
    
        return stream;
      }
    
      _createPeerConnection() async {
        Map<String, dynamic> configuration = {
          "iceServers": [
            {"url": "stun:stun.l.google.com:19302"},
            {
              "url": "turn:numb.viagenie.ca",
              "username": "******@gmail.com",
              "credential": "*****",
            }
          ]
        };
    
        final Map<String, dynamic> offerSdpConstraints = {
          "mandatory": {
            "OfferToReceiveAudio": true,
            "OfferToReceiveVideo": false,
          },
          "optional": [],
        };
    
        _localStream = await _getUserMedia();
    
        RTCPeerConnection pc =
            await createPeerConnection(configuration, offerSdpConstraints);
        pc.addStream(_localStream);
    
        pc.onIceCandidate = (e) {
          if (e.candidate != null) {
            l = json.encode({
              'candidate': e.candidate.toString(),
              'sdpMid': e.sdpMid.toString(),
              'sdpMlineIndex': e.sdpMlineIndex,
            });
            print("Her $l");
          }
        };
    
        pc.onAddStream = (stream) {
          print('addStream: ' + stream.id);
          _remoteRenderer.srcObject = stream;
        };
    
        return pc;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            child: Center(
              child: Row(
                children: [
                  Flexible(child: RTCVideoView(_remoteRenderer)),
                  ElevatedButton(
                    child: Text("Create"),
                    onPressed: _createOfferSdp,
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showAlertDialog(context);
                    },
                    child: Text("Join"),
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    
    

    The Line that does not even entered is the function _createAnwerSdp() and next line to it! The createAnswerSdp function is used for answering the call while getting the ice candidate.

    What may be cause for the issue?