hamere
hamere

Reputation: 135

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

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?

Upvotes: 2

Views: 752

Answers (1)

nigale
nigale

Reputation: 116

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!

Upvotes: 1

Related Questions