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