Files
client-sdk-flutter/example/lib/pages/room.dart
T
Hiroshi Horie 50f56c5e1e Simulcast, Screen sharing & Various improvements (#4)
* Respect `RTCIceTransportPolicy` enum and organize

* Simplify syntax where possible etc.

* Combine `VideoPreset` and `VideoPresets`

* Default values for `ConnectOptions`

* Build URI instead of String manipulation

* Slight modifications to Exception

* `LiveKitTheme` for example

* `VideoEncoding` class

* Organize imports

* First simulcast implementation

* Remove unnecessary try-catches

* Update Android settings

* Remember uri and token

* example improvements

* `fit` parameter for VideoTrackRenderer

* Simulcast option for example

* Pass `defaultPublishOptions`

* Show only `VideoQuality`

* Pass tests

* Better buildUri logic

* Named parameter to positional

* Explicit imports

* `VideoParameter` instead of `VideoPreset`

* Use `mediaTrack.getSettings` when possible

* Safer dispose logic

* Safer `PCTransport`

Update transport.dart

* Synchronized events for `SignalClient`

* Use logger instead of print

* Make example compile for iOS

* First screen share implementation

* Make example work with screen share

* Example improvement

* Code optimization

* Don't depend on web_socket_channel

* Fix: Unpublish track bug

* Show participant mute state & identity

* Update protos

* Remote mute/unmute

* iOS Background mode

* Separate `createCameraTrack` and `createScreenTrack`

* Clean up

* PB fix

* format

* Fix analyzer warning

* Android clean up

* Update README.md

* Clean up
2021-09-11 03:14:14 +09:00

153 lines
4.0 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:provider/provider.dart';
import '../widgets/controls.dart';
import '../widgets/participant.dart';
class RoomPage extends StatefulWidget {
//
final Room room;
const RoomPage(
this.room, {
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _RoomPageState();
}
}
class _RoomPageState extends State<RoomPage> with RoomDelegate {
// BuildContext? _lastContext;
//
List<Participant> participants = [];
@override
void initState() {
super.initState();
widget.room.delegate = this;
widget.room.addListener(_onChange);
_onConnected();
}
@override
void dispose() {
widget.room.delegate = null;
widget.room.removeListener(_onChange);
super.dispose();
}
void _onConnected() async {
// video will fail when running in ios simulator
try {
final localVideo = await LocalVideoTrack.createCameraTrack(); // Defaults to camera
await widget.room.localParticipant.publishVideoTrack(
localVideo,
// options: TrackPublishOptions(
// // simulcast: true,
// videoEncoding: VideoParameters.presetQVGA169.encoding,
// ),
);
} catch (e) {
print('could not publish video: $e');
}
final localAudio = await LocalAudioTrack.create();
await widget.room.localParticipant.publishAudioTrack(localAudio);
sortParticipants();
}
void _onChange() {
sortParticipants();
}
void sortParticipants() {
List<Participant> participants = [];
participants.addAll(widget.room.participants.values);
// sort speakers for the grid
participants.sort((a, b) {
// loudest speaker first
if (a.isSpeaking && b.isSpeaking) {
if (a.audioLevel > b.audioLevel) {
return -1;
} else {
return 1;
}
}
// last spoken at
final aSpokeAt = a.lastSpokeAt?.millisecondsSinceEpoch ?? 0;
final bSpokeAt = b.lastSpokeAt?.millisecondsSinceEpoch ?? 0;
if (aSpokeAt != bSpokeAt) {
return aSpokeAt > bSpokeAt ? -1 : 1;
}
// video on
if (a.hasVideo != b.hasVideo) {
return a.hasVideo ? -1 : 1;
}
// joinedAt
return a.joinedAt.millisecondsSinceEpoch - b.joinedAt.millisecondsSinceEpoch;
});
if (participants.length > 1) {
participants.insert(1, widget.room.localParticipant);
} else {
participants.add(widget.room.localParticipant);
}
setState(() {
this.participants = participants;
});
}
@override
void onDisconnected() {
// final context = _lastContext;
print('disconnected: $context');
// if (context != null) {
Navigator.pop(context);
// }
}
@override
Widget build(BuildContext context) => Scaffold(
// with a provider, any child/descendent widget can be updated if they
// are a Consumer of Room.
body: ChangeNotifierProvider.value(
value: widget.room,
child: Column(
children: [
Expanded(
child: participants.isNotEmpty
? ParticipantWidget(participants.first)
: Container()),
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participants.length - 1),
itemBuilder: (BuildContext context, int index) => Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(2),
child: ParticipantWidget(participants[index + 1], quality: VideoQuality.LOW),
),
),
),
SafeArea(
top: false,
child: ControlsWidget(widget.room),
),
],
),
),
);
}