WebRTC setRemoteDescription Session is Null error in flutter

863 views Asked by At

So, I was trying to write a webRTC mobile app. Caught some error.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sdp_transform/sdp_transform.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;

  _createOfferSdp() async {
    RTCSessionDescription description =
        await _peerConnection.createOffer({'offerToReceiveAudio': 1});
    var session = parse(description.sdp);
    Map<String, dynamic> sdp = {
      "sdp": session.toString(),
    };
    var k = firebaseInstance.doc().collection("sdp").doc("offersdp").set(sdp);
    print("K val: $k");
    _peerConnection.setLocalDescription(description);
    // print(session);
  }

  bool remotesaved = false;
  _createAnswerSdp() async {
    firebaseInstance
        .doc(docId.text)
        .collection("sdp")
        .doc("offersdp")
        .get()
        .then((value) async {
      print("here");
      var temp = (value.data()["sdp"]).toString();

      // var session = await jsonDecode(temp.toString());
      // print("here3");
      // print("session: $session");
      // String sdp = write(session, null);
      print(temp);
      if (temp != null) {
        RTCSessionDescription description1 =
            RTCSessionDescription(temp, "offer");

        await _peerConnection.setRemoteDescription(description1).then((value) {
          setState(() {
            remotesaved = true;
          });
        });
      }
    });

    if (remotesaved) {
      RTCSessionDescription description = await _peerConnection
          .createAnswer({'offerToReceiveVideo': 0, 'offerToReceiveAudio': 1});

      var session = parse(description.sdp);
      firebaseInstance
          .doc(docId.text)
          .collection("sdp")
          .doc("answersdp")
          .set(session);
      if (l != null) {
        firebaseInstance
            .doc(docId.text)
            .collection("icecandidate")
            .doc("answerice")
            .set(l);
      }
    }
  }

  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;
      },
    );
  }

  @override
  void initState() {
    _createPeerConnection().then((pc) {
      _peerConnection = pc;
    });
    super.initState();
  }

  @override
  void 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"},
      ]
    };

    final Map<String, dynamic> offerSdpConstraints = {
      "mandatory": {
        "OfferToReceiveAudio": true,
        "OfferToReceiveVideo": false,
      },
      "optional": [],
    };

    _localStream = await _getUserMedia();

    RTCPeerConnection pc =
        await createPeerConnection(configuration, offerSdpConstraints);
    // if (pc != null) print(pc);
    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(l);
      }
    };

    pc.onIceConnectionState = (e) {
      print(e);
    };

    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: [
              ElevatedButton(
                child: Text("Create"),
                onPressed: _createOfferSdp,
              ),
              ElevatedButton(
                onPressed: () {
                  showAlertDialog(context);
                },
                child: Text("Join"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

So, In my code from a mobile an offer will be created and stored in firestore.

dmeet->(AUTO ID)->sdp->offersdp->sdp:sdpvaluesStored

so after that I given the documentId manually in join button and when I try to fetch the offersdp and to set the remote description. I came up with some error

D/FlutterWebRTCPlugin( 7232): peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Unable to RTCPeerConnection::setRemoteDescription: peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): #0      RTCPeerConnectionNative.setRemoteDescription
package:flutter_webrtc/…/native/rtc_peerconnection_impl.dart:328
E/flutter ( 7232): <asynchronous suspension>
E/flutter ( 7232): #1      _MyHomePageState._createAnswerSdp.<anonymous closure>.<anonymous closure> (package:flutter_voip_calling/main.dart)

I'm using the tutorial code where it uses the same concept as that I will paste the sdp in textbox and set the remote description and the tutorial code is here

0

There are 0 answers