android screenshare example and touch ups (#31)

This commit is contained in:
davidliu
2021-11-17 17:11:46 +09:00
committed by GitHub
parent 1835675650
commit 4a59a1e6d5
6 changed files with 88 additions and 14 deletions
@@ -1,13 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.livekit.example"> package="io.livekit.example">
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application <application
android:label="LiveKit Example" android:label="LiveKit Example"
android:icon="@mipmap/livekit_ic_launcher"> android:icon="@mipmap/livekit_ic_launcher">
@@ -40,6 +44,11 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name="de.julianassmann.flutter_background.IsolateHolderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data
+40 -7
View File
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart'; import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
import '../exts.dart'; import '../exts.dart';
@@ -114,7 +115,6 @@ class _ControlsWidgetState extends State<ControlsWidget> {
} }
void _shareScreen() async { void _shareScreen() async {
//
final lp = widget.room.localParticipant; final lp = widget.room.localParticipant;
for (final track in lp.videoTracks) { for (final track in lp.videoTracks) {
@@ -122,6 +122,17 @@ class _ControlsWidgetState extends State<ControlsWidget> {
} }
try { try {
// Required for android screenshare.
const androidConfig = FlutterBackgroundAndroidConfig(
notificationTitle: 'Screen Sharing',
notificationText: 'LiveKit Example is sharing the screen.',
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon:
AndroidResource(name: 'livekit_ic_launcher', defType: 'mipmap'),
);
await FlutterBackground.initialize(androidConfig: androidConfig);
await FlutterBackground.enableBackgroundExecution();
final screenTrack = final screenTrack =
await LocalVideoTrack.createScreenTrack(); // Defaults to camera await LocalVideoTrack.createScreenTrack(); // Defaults to camera
await widget.room.localParticipant.publishVideoTrack( await widget.room.localParticipant.publishVideoTrack(
@@ -132,6 +143,17 @@ class _ControlsWidgetState extends State<ControlsWidget> {
} }
} }
void _unshareScreen() async {
final lp = widget.room.localParticipant;
try {
await lp.setScreenShareEnabled(false);
await FlutterBackground.disableBackgroundExecution();
} catch (e) {
print('error disabling screen share: $e');
}
}
void _onTapDisconnect() async { void _onTapDisconnect() async {
final result = await context.showDisconnectDialog(); final result = await context.showDisconnectDialog();
if (result == true) await widget.room.disconnect(); if (result == true) await widget.room.disconnect();
@@ -156,8 +178,12 @@ class _ControlsWidgetState extends State<ControlsWidget> {
// mute audio // mute audio
final canMute = participant.hasAudio && !participant.isMuted; final canMute = participant.hasAudio && !participant.isMuted;
final videoPub = participant.videoTracks.firstOrNull; final videoPub =
participant.getTrackPublicationBySource(TrackSource.camera);
final videoEnabled = videoPub != null && !videoPub.muted; final videoEnabled = videoPub != null && !videoPub.muted;
final screenSharePub =
participant.getTrackPublicationBySource(TrackSource.screenShareVideo);
final screenShareEnabled = screenSharePub != null && !screenSharePub.muted;
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@@ -205,11 +231,18 @@ class _ControlsWidgetState extends State<ControlsWidget> {
onPressed: () => _toggleCamera(), onPressed: () => _toggleCamera(),
tooltip: 'toggle camera', tooltip: 'toggle camera',
), ),
IconButton( if (screenShareEnabled)
icon: const Icon(EvaIcons.monitor), IconButton(
onPressed: () => _shareScreen(), icon: const Icon(EvaIcons.monitorOutline),
tooltip: 'share screen (experimental)', onPressed: () => _unshareScreen(),
), tooltip: 'unshare screen (experimental)',
)
else
IconButton(
icon: const Icon(EvaIcons.monitor),
onPressed: () => _shareScreen(),
tooltip: 'share screen (experimental)',
),
IconButton( IconButton(
onPressed: _onTapDisconnect, onPressed: _onTapDisconnect,
icon: const Icon(EvaIcons.closeCircle), icon: const Icon(EvaIcons.closeCircle),
+26 -5
View File
@@ -90,6 +90,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_background:
dependency: "direct main"
description:
name: flutter_background
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2+2"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -218,6 +225,20 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.7"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6" version: "2.0.6"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
@@ -225,7 +246,7 @@ packages:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
path_provider_macos: path_provider_macos:
dependency: transitive dependency: transitive
description: description:
@@ -246,7 +267,7 @@ packages:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@@ -302,7 +323,7 @@ packages:
name: shared_preferences_linux name: shared_preferences_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shared_preferences_macos: shared_preferences_macos:
dependency: transitive dependency: transitive
description: description:
@@ -330,7 +351,7 @@ packages:
name: shared_preferences_windows name: shared_preferences_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -419,7 +440,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.10" version: "2.3.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
+1
View File
@@ -11,6 +11,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_background: 1.0.2+2
provider: ^6.0.1 provider: ^6.0.1
logging: ^1.0.1 logging: ^1.0.1
shared_preferences: ^2.0.7 shared_preferences: ^2.0.7
+7 -1
View File
@@ -242,6 +242,10 @@ extension LocalParticipantTrackSourceExt on LocalParticipant {
return setSourceEnabled(TrackSource.microphone, enabled); return setSourceEnabled(TrackSource.microphone, enabled);
} }
Future<void> setScreenShareEnabled(bool enabled) async {
return setSourceEnabled(TrackSource.screenShareVideo, enabled);
}
Future<void> setSourceEnabled(TrackSource source, bool enabled) async { Future<void> setSourceEnabled(TrackSource source, bool enabled) async {
final pub = getTrackPublicationBySource(source); final pub = getTrackPublicationBySource(source);
if (pub != null) { if (pub != null) {
@@ -261,8 +265,10 @@ extension LocalParticipantTrackSourceExt on LocalParticipant {
} else if (source == TrackSource.microphone) { } else if (source == TrackSource.microphone) {
final track = await LocalAudioTrack.create(); final track = await LocalAudioTrack.create();
await publishAudioTrack(track); await publishAudioTrack(track);
} else if (source == TrackSource.screenShareVideo) {
final track = await LocalVideoTrack.createScreenTrack();
await publishVideoTrack(track);
} }
// TODO: Screen share
} }
} }
} }
+4
View File
@@ -71,6 +71,10 @@ class LocalVideoTrack extends VideoTrack {
); );
} }
/// Creates a LocalVideoTrack from the display.
///
/// Note: Android requires a foreground service to be started prior to
/// creating a screen track. Refer to the example app for an implementation.
static Future<LocalVideoTrack> createScreenTrack([ static Future<LocalVideoTrack> createScreenTrack([
ScreenTrackOptions? options, ScreenTrackOptions? options,
]) async { ]) async {