Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
50 changes: 50 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
group 'io.livekit.plugin'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 30

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

defaultConfig {
minSdkVersion 16
}
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
3 changes: 3 additions & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
5 changes: 5 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
1 change: 1 addition & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'livekit'
3 changes: 3 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.livekit.plugin">
</manifest>
32 changes: 32 additions & 0 deletions android/src/main/kotlin/io/livekit/plugin/LiveKitPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.livekit.plugin

import androidx.annotation.NonNull

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** LiveKitPlugin */
class LiveKitPlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "livekit_client")
channel.setMethodCallHandler(this)
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
// no-op for now
result.notImplemented()
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
6 changes: 4 additions & 2 deletions example/lib/exts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ extension LKExampleExt on BuildContext {
context: this,
builder: (ctx) => AlertDialog(
title: const Text('UnPublish'),
content: const Text('Would you like to un-publish your Camera & Mic ?'),
content:
const Text('Would you like to un-publish your Camera & Mic ?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
Expand Down Expand Up @@ -92,7 +93,8 @@ extension LKExampleExt on BuildContext {
context: this,
builder: (ctx) => AlertDialog(
title: const Text('Send data'),
content: const Text('This will send a sample data to all participants in the room'),
content: const Text(
'This will send a sample data to all participants in the room'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
Expand Down
6 changes: 4 additions & 2 deletions example/lib/pages/connect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class _ConnectPageState extends State<ConnectPage> {
// Save for next time
await _writePrefs();

print('Connecting with url: ${_uriCtrl.text}, token: ${_tokenCtrl.text}...');
print(
'Connecting with url: ${_uriCtrl.text}, token: ${_tokenCtrl.text}...');

final room = await LiveKitClient.connect(
_uriCtrl.text,
Expand Down Expand Up @@ -113,7 +114,8 @@ class _ConnectPageState extends State<ConnectPage> {
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Theme.of(context).colorScheme.secondary),
border:
Border.all(color: Theme.of(context).colorScheme.secondary),
),
constraints: const BoxConstraints(
maxWidth: 320,
Expand Down
14 changes: 9 additions & 5 deletions example/lib/pages/room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class _RoomPageState extends State<RoomPage> {

void _setUpListeners() => _listener
..on<RoomDisconnectedEvent>((_) async {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) => Navigator.pop(context));
WidgetsBinding.instance
?.addPostFrameCallback((timeStamp) => Navigator.pop(context));
})
..on<DataReceivedEvent>((event) {
String decoded = 'Failed to decode';
Expand Down Expand Up @@ -120,7 +121,8 @@ class _RoomPageState extends State<RoomPage> {
}

// joinedAt
return a.joinedAt.millisecondsSinceEpoch - b.joinedAt.millisecondsSinceEpoch;
return a.joinedAt.millisecondsSinceEpoch -
b.joinedAt.millisecondsSinceEpoch;
});

if (participants.length > 1) {
Expand All @@ -138,8 +140,9 @@ class _RoomPageState extends State<RoomPage> {
body: Column(
children: [
Expanded(
child:
participants.isNotEmpty ? ParticipantWidget(participants.first) : Container()),
child: participants.isNotEmpty
? ParticipantWidget(participants.first)
: Container()),
SizedBox(
height: 100,
child: ListView.builder(
Expand All @@ -149,7 +152,8 @@ class _RoomPageState extends State<RoomPage> {
width: 100,
height: 100,
padding: const EdgeInsets.all(2),
child: ParticipantWidget(participants[index + 1], quality: VideoQuality.LOW),
child: ParticipantWidget(participants[index + 1],
quality: VideoQuality.LOW),
),
),
),
Expand Down
3 changes: 2 additions & 1 deletion example/lib/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class LiveKitTheme {
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
// backgroundColor: MaterialStateProperty.all<Color>(accentColor),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.disabled)) return accentColor.withOpacity(0.5);
if (states.contains(MaterialState.disabled))
return accentColor.withOpacity(0.5);
return accentColor;
}),
),
Expand Down
10 changes: 7 additions & 3 deletions example/lib/widgets/controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ class _ControlsWidgetState extends State<ControlsWidget> {

void _toggleCamera() async {
//
final track = participant.videoTracks.firstOrNull?.track as LocalVideoTrack?;
final track =
participant.videoTracks.firstOrNull?.track as LocalVideoTrack?;
if (track == null) return;

try {
Expand All @@ -113,7 +114,8 @@ class _ControlsWidgetState extends State<ControlsWidget> {
}

try {
final screenTrack = await LocalVideoTrack.createScreenTrack(); // Defaults to camera
final screenTrack =
await LocalVideoTrack.createScreenTrack(); // Defaults to camera
await widget.room.localParticipant.publishVideoTrack(
screenTrack,
);
Expand Down Expand Up @@ -177,7 +179,9 @@ class _ControlsWidgetState extends State<ControlsWidget> {
icon: const Icon(EvaIcons.videoOff),
),
IconButton(
icon: Icon(position == CameraPosition.back ? EvaIcons.camera : EvaIcons.person),
icon: Icon(position == CameraPosition.back
? EvaIcons.camera
: EvaIcons.person),
onPressed: () => _toggleCamera(),
),
IconButton(
Expand Down
3 changes: 2 additions & 1 deletion example/lib/widgets/participant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
// register for change so Flutter will re-build the widget upon change
void _onParticipantChanged() {
//
final firstAudio = widget.participant.audioTracks.firstWhereOrNull((pub) => pub.subscribed);
final firstAudio = widget.participant.audioTracks
.firstWhereOrNull((pub) => pub.subscribed);
final firstVideo = widget.participant.videoTracks
.firstWhereOrNull((pub) => !pub.isScreenShare && pub.subscribed);

Expand Down
34 changes: 34 additions & 0 deletions lib/livekit_client_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:async';
// In order to *not* need this ignore, consider extracting the "web" version
// of your plugin as a separate package, instead of inlining it in the same
// package as the core of your plugin.
// ignore: avoid_web_libraries_in_flutter
// import 'dart:html' as html show window;

import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

/// A web implementation of the Livekit plugin.
class LiveKitWebPlugin {
static void registerWith(Registrar registrar) {
final MethodChannel channel = MethodChannel(
'livekit_client',
const StandardMethodCodec(),
registrar,
);

final pluginInstance = LiveKitWebPlugin();
channel.setMethodCallHandler(pluginInstance.handleMethodCall);
}

/// Handles method calls over the MethodChannel of this plugin.
/// Note: Check the "federated" architecture for a new way of doing this:
/// https://flutter.dev/go/federated-plugins
Future<dynamic> handleMethodCall(MethodCall call) async {
// no-op for now
throw PlatformException(
code: 'Unimplemented',
details: 'livekit for web doesn\'t implement \'${call.method}\'',
);
}
}
6 changes: 4 additions & 2 deletions lib/src/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ class ConnectException extends LiveKitException {
}

class UnexpectedStateException extends LiveKitException {
UnexpectedStateException([String msg = 'Unexpected connection state']) : super._(msg);
UnexpectedStateException([String msg = 'Unexpected connection state'])
: super._(msg);
}

class TrackCreateException extends LiveKitException {
TrackCreateException([String msg = 'Failed to create track']) : super._(msg);
}

class TrackPublishException extends LiveKitException {
TrackPublishException([String msg = 'Failed to publish track']) : super._(msg);
TrackPublishException([String msg = 'Failed to publish track'])
: super._(msg);
}

class DataPublishException extends LiveKitException {
Expand Down
3 changes: 2 additions & 1 deletion lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ extension ICEServerExt on lk_rtc.ICEServer {
}

extension IterableExt<E> on Iterable<E> {
E? elementAtOrNull(int index) => (index >= 0 && index < length) ? elementAt(index) : null;
E? elementAtOrNull(int index) =>
(index >= 0 && index < length) ? elementAt(index) : null;
}

extension ObjectExt on Object {
Expand Down
3 changes: 2 additions & 1 deletion lib/src/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ class TrackPublishOptions {
});

@override
String toString() => '${runtimeType}(videoEncoding: ${videoEncoding}, simulcast: ${simulcast})';
String toString() =>
'${runtimeType}(videoEncoding: ${videoEncoding}, simulcast: ${simulcast})';
}
9 changes: 6 additions & 3 deletions lib/src/participant/local_participant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class LocalParticipant extends Participant {

/// publish an audio track to the room
Future<TrackPublication> publishAudioTrack(LocalAudioTrack track) async {
if (audioTracks.any((e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
if (audioTracks.any(
(e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
throw TrackPublishException('track already exists');
}

Expand Down Expand Up @@ -82,7 +83,8 @@ class LocalParticipant extends Participant {
LocalVideoTrack track, {
TrackPublishOptions? options,
}) async {
if (videoTracks.any((e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
if (videoTracks.any(
(e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
throw TrackPublishException('track already exists');
}

Expand Down Expand Up @@ -117,7 +119,8 @@ class LocalParticipant extends Participant {
}
}

logger.fine('Compute encodings with resolution: ${width}x${height}, options: ${options}');
logger.fine(
'Compute encodings with resolution: ${width}x${height}, options: ${options}');

final encodings = Utils.computeVideoEncodings(
width: width,
Expand Down
16 changes: 10 additions & 6 deletions lib/src/participant/participant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import 'remote_participant.dart';

/// Base for [RemoteParticipant] and [LocalParticipant],
/// can not be instantiated directly.
abstract class Participant extends DisposableChangeNotifier with EventsEmittable<ParticipantEvent> {
abstract class Participant extends DisposableChangeNotifier
with EventsEmittable<ParticipantEvent> {
/// map of track sid => published track
final trackPublications = <String, TrackPublication>{};

Expand Down Expand Up @@ -49,7 +50,8 @@ abstract class Participant extends DisposableChangeNotifier with EventsEmittable
DateTime get joinedAt {
final pi = _participantInfo;
if (pi != null) {
return DateTime.fromMillisecondsSinceEpoch(pi.joinedAt.toInt() * 1000, isUtc: true);
return DateTime.fromMillisecondsSinceEpoch(pi.joinedAt.toInt() * 1000,
isUtc: true);
}
return DateTime.now();
}
Expand Down Expand Up @@ -160,9 +162,11 @@ abstract class Participant extends DisposableChangeNotifier with EventsEmittable

// Convenience extension
extension ParticipantExt on Participant {
List<TrackPublication> get videoTracks =>
trackPublications.values.where((e) => e.kind == lk_models.TrackType.VIDEO).toList();
List<TrackPublication> get videoTracks => trackPublications.values
.where((e) => e.kind == lk_models.TrackType.VIDEO)
.toList();

List<TrackPublication> get audioTracks =>
trackPublications.values.where((e) => e.kind == lk_models.TrackType.AUDIO).toList();
List<TrackPublication> get audioTracks => trackPublications.values
.where((e) => e.kind == lk_models.TrackType.AUDIO)
.toList();
}
Loading