Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and update flutter example to be null-safe and work with flutter3. #136

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions sfu-ws/flutter/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
154 changes: 74 additions & 80 deletions sfu-ws/flutter/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter_webrtc/flutter_webrtc.dart';

import 'package:flutter/material.dart';
import 'package:websocket/websocket.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

void main() => runApp(MyApp());
void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
_GetMyAppState createState() => _GetMyAppState();
State<MyApp> createState() => _MyAppState();
}

class _GetMyAppState extends State<MyApp> {
class _MyAppState extends State<MyApp> {
static const _textStyle = TextStyle(fontSize: 24);

// Local media
final _localRenderer = RTCVideoRenderer();
List _remoteRenderers = [];
List<RTCVideoRenderer> _remoteRenderers = [];

WebSocketChannel? _socket;
late final RTCPeerConnection _peerConnection;

WebSocket _socket;
RTCPeerConnection _peerConnection;
_MyAppState();

@override
void initState() {
Expand All @@ -29,7 +36,7 @@ class _GetMyAppState extends State<MyApp> {
_peerConnection = await createPeerConnection({}, {});

await _localRenderer.initialize();
var localStream = await navigator.mediaDevices
final localStream = await navigator.mediaDevices
.getUserMedia({'audio': true, 'video': true});
_localRenderer.srcObject = localStream;

Expand All @@ -38,14 +45,10 @@ class _GetMyAppState extends State<MyApp> {
});

_peerConnection.onIceCandidate = (candidate) {
if (candidate == null) {
return;
}

_socket.add(JsonEncoder().convert({
_socket?.sink.add(jsonEncode({
"event": "candidate",
"data": JsonEncoder().convert({
'sdpMLineIndex': candidate.sdpMlineIndex,
"data": jsonEncode({
'sdpMLineIndex': candidate.sdpMLineIndex,
'sdpMid': candidate.sdpMid,
'candidate': candidate.candidate,
})
Expand All @@ -54,48 +57,54 @@ class _GetMyAppState extends State<MyApp> {

_peerConnection.onTrack = (event) async {
if (event.track.kind == 'video' && event.streams.isNotEmpty) {
var renderer = RTCVideoRenderer();
await renderer.initialize();
renderer.srcObject = event.streams[0];
var renderer = RTCVideoRenderer();
await renderer.initialize();
renderer.srcObject = event.streams[0];

setState(() { _remoteRenderers.add(renderer); });
setState(() {
_remoteRenderers.add(renderer);
});
}
};

_peerConnection.onRemoveStream = (stream) {
var rendererToRemove;
var newRenderList = [];
RTCVideoRenderer? rendererToRemove;
final newRenderList = <RTCVideoRenderer>[];

// Filter existing renderers for the stream that has been stopped
_remoteRenderers.forEach((r) {
if (r.srcObject.id == stream.id) {
rendererToRemove = r;
} else {
newRenderList.add(r);
}
});
for (final r in _remoteRenderers) {
if (r.srcObject?.id == stream.id) {
rendererToRemove = r;
} else {
newRenderList.add(r);
}
}

// Set the new renderer list
setState(() { _remoteRenderers = newRenderList; });
setState(() {
_remoteRenderers = newRenderList;
});

// Dispose the renderer we are done with
if (rendererToRemove != null) {
rendererToRemove.dispose();
}
};

_socket = await WebSocket.connect('ws://localhost:8080/websocket');
_socket.stream.listen((raw) async {
final socket =
WebSocketChannel.connect(Uri.parse('ws://localhost:8080/websocket'));
_socket = socket;
socket.stream.listen((raw) async {
Map<String, dynamic> msg = jsonDecode(raw);

switch (msg['event']) {
case 'candidate':
Map<String, dynamic> parsed = jsonDecode(msg['data']);
final parsed = jsonDecode(msg['data']);
_peerConnection
.addCandidate(RTCIceCandidate(parsed['candidate'], null, 0));
return;
case 'offer':
Map<String, dynamic> offer = jsonDecode(msg['data']);
final offer = jsonDecode(msg['data']);

// SetRemoteDescription and create answer
await _peerConnection.setRemoteDescription(
Expand All @@ -104,10 +113,9 @@ class _GetMyAppState extends State<MyApp> {
await _peerConnection.setLocalDescription(answer);

// Send answer over WebSocket
_socket.add(JsonEncoder().convert({
_socket?.sink.add(jsonEncode({
'event': 'answer',
'data':
JsonEncoder().convert({'type': answer.type, 'sdp': answer.sdp})
'data': jsonEncode({'type': answer.type, 'sdp': answer.sdp}),
}));
return;
}
Expand All @@ -119,49 +127,35 @@ class _GetMyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'sfu-ws',
home: Scaffold(
appBar: AppBar(
title: Text('sfu-ws'),
title: 'sfu-ws',
home: Scaffold(
appBar: AppBar(
title: const Text('sfu-ws'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Local Video', style: _textStyle),
SizedBox(
width: 160,
height: 120,
child: RTCVideoView(_localRenderer, mirror: true),
),
const Text('Remote Video', style: _textStyle),
Row(
children: [
..._remoteRenderers.map((remoteRenderer) {
return SizedBox(
width: 160,
height: 120,
child: RTCVideoView(remoteRenderer));
}).toList(),
],
),
body: OrientationBuilder(builder: (context, orientation) {
return Column(
children: [
Row(
children: [
Text('Local Video', style: TextStyle(fontSize: 50.0))
],
),
Row(
children: [
SizedBox(
width: 160,
height: 120,
child: RTCVideoView(_localRenderer, mirror: true))
],
),
Row(
children: [
Text('Remote Video', style: TextStyle(fontSize: 50.0))
],
),
Row(
children: [
..._remoteRenderers.map((remoteRenderer) {
return SizedBox(
width: 160,
height: 120,
child: RTCVideoView(remoteRenderer));
}).toList(),
],
),
Row(
children: [
Text('Logs Video', style: TextStyle(fontSize: 50.0))
],
),
],
);
})));
const Text('Logs Video', style: _textStyle),
],
),
),
);
}
}
13 changes: 5 additions & 8 deletions sfu-ws/flutter/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
name: flutter_sfu_ws
description: A new Flutter project.

description: A SFU example project.
version: 1.0.0+1

environment:
sdk: ">=2.1.0 <3.0.0"
sdk: ">=3.0.0 <4.0.0"

dependencies:
websocket: ^0.0.5
flutter:
sdk: flutter

cupertino_icons: ^0.1.2
flutter_webrtc: ^0.5.7
sdp_transform:
shared_preferences:
flutter_webrtc: ^0.9.36
web_socket_channel: ^2.4.0

dev_dependencies:
flutter_test:
sdk: flutter

flutter_lints: ^2.0.1

flutter:
uses-material-design: true