Webrtc example



import 'dart:convert';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';

typedef void StreamStateCallback(MediaStream stream);

class Signaling {
Map<String, dynamic> configuration = {
'iceServers': [
{
'urls': [
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302'
]
}
]
};

RTCPeerConnection? peerConnection;
MediaStream? localStream;
MediaStream? remoteStream;
String? roomId;
String? currentRoomText;
StreamStateCallback? onAddRemoteStream;

Future<String> createRoom(RTCVideoRenderer remoteRenderer) async {
FirebaseFirestore db = FirebaseFirestore.instance;
DocumentReference roomRef = db.collection('rooms').doc();

print('Create PeerConnection with configuration: $configuration');

peerConnection = await createPeerConnection(configuration);

registerPeerConnectionListeners();

localStream?.getTracks().forEach((track) {
peerConnection?.addTrack(track, localStream!);
});

// Code for collecting ICE candidates below
var callerCandidatesCollection = roomRef.collection('callerCandidates');

peerConnection?.onIceCandidate = (RTCIceCandidate candidate) {
print('Got candidate: ${candidate.toMap()}');
callerCandidatesCollection.add(candidate.toMap());
};
// Finish Code for collecting ICE candidate

// Add code for creating a room
RTCSessionDescription offer = await peerConnection!.createOffer();
await peerConnection!.setLocalDescription(offer);
print('Created offer: $offer');

Map<String, dynamic> roomWithOffer = {'offer': offer.toMap()};

await roomRef.set(roomWithOffer);
var roomId = roomRef.id;
print('New room created with SDK offer. Room ID: $roomId');
currentRoomText = 'Current room is $roomId - You are the caller!';
// Created a Room

peerConnection?.onTrack = (RTCTrackEvent event) {
print('Got remote track: ${event.streams[0]}');

event.streams[0].getTracks().forEach((track) {
print('Add a track to the remoteStream $track');
remoteStream?.addTrack(track);
});
};

// Listening for remote session description below
roomRef.snapshots().listen((snapshot) async {
print('Got updated room: ${snapshot.data()}');

Map<String, dynamic> data = snapshot.data() as Map<String, dynamic>;
if (peerConnection?.getRemoteDescription() != null &&
data['answer'] != null) {
var answer = RTCSessionDescription(
data['answer']['sdp'],
data['answer']['type'],
);

print("Someone tried to connect");
await peerConnection?.setRemoteDescription(answer);
}
});
// Listening for remote session description above

// Listen for remote Ice candidates below
roomRef.collection('calleeCandidates').snapshots().listen((snapshot) {
snapshot.docChanges.forEach((change) {
if (change.type == DocumentChangeType.added) {
Map<String, dynamic> data = change.doc.data() as Map<String, dynamic>;
print('Got new remote ICE candidate: ${jsonEncode(data)}');
peerConnection!.addCandidate(
RTCIceCandidate(
data['candidate'],
data['sdpMid'],
data['sdpMLineIndex'],
),
);
}
});
});
// Listen for remote ICE candidates above

return roomId;
}

Future<void> joinRoom(String roomId, RTCVideoRenderer remoteVideo) async {
FirebaseFirestore db = FirebaseFirestore.instance;
print(roomId);
DocumentReference roomRef = db.collection('rooms').doc('$roomId');
var roomSnapshot = await roomRef.get();
print('Got room ${roomSnapshot.exists}');

if (roomSnapshot.exists) {
print('Create PeerConnection with configuration: $configuration');
peerConnection = await createPeerConnection(configuration);

registerPeerConnectionListeners();

localStream?.getTracks().forEach((track) {
peerConnection?.addTrack(track, localStream!);
});

// Code for collecting ICE candidates below
var calleeCandidatesCollection = roomRef.collection('calleeCandidates');
peerConnection!.onIceCandidate = (RTCIceCandidate? candidate) {
if (candidate == null) {
print('onIceCandidate: complete!');
return;
}
print('onIceCandidate: ${candidate.toMap()}');
calleeCandidatesCollection.add(candidate.toMap());
};
// Code for collecting ICE candidate above

peerConnection?.onTrack = (RTCTrackEvent event) {
print('Got remote track: ${event.streams[0]}');
event.streams[0].getTracks().forEach((track) {
print('Add a track to the remoteStream: $track');
remoteStream?.addTrack(track);
});
};

// Code for creating SDP answer below
var data = roomSnapshot.data() as Map<String, dynamic>;
print('Got offer $data');
var offer = data['offer'];
await peerConnection?.setRemoteDescription(
RTCSessionDescription(offer['sdp'], offer['type']),
);
var answer = await peerConnection!.createAnswer();
print('Created Answer $answer');

await peerConnection!.setLocalDescription(answer);

Map<String, dynamic> roomWithAnswer = {
'answer': {'type': answer.type, 'sdp': answer.sdp}
};

await roomRef.update(roomWithAnswer);
// Finished creating SDP answer

// Listening for remote ICE candidates below
roomRef.collection('callerCandidates').snapshots().listen((snapshot) {
snapshot.docChanges.forEach((document) {
var data = document.doc.data() as Map<String, dynamic>;
print(data);
print('Got new remote ICE candidate: $data');
peerConnection!.addCandidate(
RTCIceCandidate(
data['candidate'],
data['sdpMid'],
data['sdpMLineIndex'],
),
);
});
});
}
}

Future<void> openUserMedia(
RTCVideoRenderer localVideo,
RTCVideoRenderer remoteVideo,
) async {
var stream = await navigator.mediaDevices
.getUserMedia({'video': true, 'audio': false});

localVideo.srcObject = stream;
localStream = stream;

remoteVideo.srcObject = await createLocalMediaStream('key');
}

Future<void> hangUp(RTCVideoRenderer localVideo) async {
List<MediaStreamTrack> tracks = localVideo.srcObject!.getTracks();
tracks.forEach((track) {
track.stop();
});

if (remoteStream != null) {
remoteStream!.getTracks().forEach((track) => track.stop());
}
if (peerConnection != null) peerConnection!.close();

if (roomId != null) {
var db = FirebaseFirestore.instance;
var roomRef = db.collection('rooms').doc(roomId);
var calleeCandidates = await roomRef.collection('calleeCandidates').get();
calleeCandidates.docs.forEach((document) => document.reference.delete());

var callerCandidates = await roomRef.collection('callerCandidates').get();
callerCandidates.docs.forEach((document) => document.reference.delete());

await roomRef.delete();
}

localStream!.dispose();
remoteStream?.dispose();
}

void registerPeerConnectionListeners() {
peerConnection?.onIceGatheringState = (RTCIceGatheringState state) {
print('ICE gathering state changed: $state');
};

peerConnection?.onConnectionState = (RTCPeerConnectionState state) {
print('Connection state change: $state');
};

peerConnection?.onSignalingState = (RTCSignalingState state) {
print('Signaling state change: $state');
};

peerConnection?.onIceGatheringState = (RTCIceGatheringState state) {
print('ICE connection state change: $state');
};

peerConnection?.onAddStream = (MediaStream stream) {
print("Add remote stream");
onAddRemoteStream?.call(stream);
remoteStream = stream;
};
}
}



cupertino_icons: ^1.0.3
flutter_webrtc: ^0.9.27
firebase_core: ^2.11.0
cloud_firestore: ^4.6.0
cloud_firestore_web: ^3.4.3


<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />



 import 'package:firebase_core/firebase_core.dart';

import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:webrtc_tutorial/signaling.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
Signaling signaling = Signaling();
RTCVideoRenderer _localRenderer = RTCVideoRenderer();
RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
String? roomId;
TextEditingController textEditingController = TextEditingController(text: '');

@override
void initState() {
_localRenderer.initialize();
_remoteRenderer.initialize();

signaling.onAddRemoteStream = ((stream) {
_remoteRenderer.srcObject = stream;
setState(() {});
});

super.initState();
}

@override
void dispose() {
_localRenderer.dispose();
_remoteRenderer.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Welcome to Flutter Explained - WebRTC"),
),
body: Column(
children: [
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
signaling.openUserMedia(_localRenderer, _remoteRenderer);
},
child: Text("Open camera & microphone"),
),
SizedBox(
width: 8,
),
ElevatedButton(
onPressed: () async {
roomId = await signaling.createRoom(_remoteRenderer);
textEditingController.text = roomId!;
setState(() {});
},
child: Text("Create room"),
),
SizedBox(
width: 8,
),
ElevatedButton(
onPressed: () {
// Add roomId
signaling.joinRoom(
textEditingController.text.trim(),
_remoteRenderer,
);
},
child: Text("Join room"),
),
SizedBox(
width: 8,
),
ElevatedButton(
onPressed: () {
signaling.hangUp(_localRenderer);
},
child: Text("Hangup"),
)
],
),
SizedBox(height: 8),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: RTCVideoView(_localRenderer, mirror: true)),
Expanded(child: RTCVideoView(_remoteRenderer)),
],
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Join the following Room: "),
Flexible(
child: TextFormField(
controller: textEditingController,
),
)
],
),
),
SizedBox(height: 8)
],
),
);
}
}

Rakibul hasan

I am Rakibul hasan,Front and Back-End Developer.I am also good at database like mysql,Firebase,mssql. I'm a Workaholic Person and I love my job as a mobile application developer i never get tired for it. Its been always fasinating to me working for new creation. I am very passionate about my work.I do my job passionately and dedicatedly.I try to do my project with clean code and gather efficiency. The greatest quality of mine is patience.I can code all day long. I never feel bored during coding and i can't take rest until my code is completed.I can adapt anything very quickly. I am also very sincere,responsible,hard working and confident regarding my works.Each and Every Project is important to me and I like to take Challenge.

Post a Comment

Previous Post Next Post