Skip to content

Commit cbdfa63

Browse files
authored
feat: Support for capturing audio for chrome tab. (#158)
* feat: Support for capturing audio for chrome tab. * Update code comments.
1 parent 7c01c11 commit cbdfa63

File tree

7 files changed

+75
-7
lines changed

7 files changed

+75
-7
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,6 @@ _build
8383

8484
# FVM
8585
.fvm/
86+
87+
# flutter web
88+
lib/generated_plugin_registrant.dart

example/lib/widgets/controls.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
169169
print('could not publish video: $e');
170170
}
171171
}
172-
await participant.setScreenShareEnabled(true);
172+
await participant.setScreenShareEnabled(true, captureScreenAudio: true);
173173
}
174174

175175
void _disableScreenShare() async {

lib/src/participant/local.dart

+26-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import '../publication/local.dart';
1515
import '../track/local/audio.dart';
1616
import '../track/local/local.dart';
1717
import '../track/local/video.dart';
18+
import '../track/options.dart';
1819
import '../types/other.dart';
1920
import '../types/participant_permissions.dart';
2021
import '../types/video_dimensions.dart';
@@ -283,14 +284,17 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
283284
}
284285

285286
/// Shortcut for publishing a [TrackSource.screenShareVideo]
286-
Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled) async {
287-
return setSourceEnabled(TrackSource.screenShareVideo, enabled);
287+
Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled,
288+
{bool? captureScreenAudio}) async {
289+
return setSourceEnabled(TrackSource.screenShareVideo, enabled,
290+
captureScreenAudio: captureScreenAudio);
288291
}
289292

290293
/// A convenience method to publish a track for a specific [TrackSource].
291294
/// This is the recommended method to publish tracks.
292295
Future<LocalTrackPublication?> setSourceEnabled(
293-
TrackSource source, bool enabled) async {
296+
TrackSource source, bool enabled,
297+
{bool? captureScreenAudio}) async {
294298
logger.fine('setSourceEnabled(source: $source, enabled: $enabled)');
295299
final publication = getTrackPublicationBySource(source);
296300
if (publication != null) {
@@ -314,6 +318,25 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
314318
room.roomOptions.defaultAudioCaptureOptions);
315319
return await publishAudioTrack(track);
316320
} else if (source == TrackSource.screenShareVideo) {
321+
/// When capturing chrome table audio, we can't capture audio/video
322+
/// track separately, it has to be returned once in getDisplayMedia,
323+
/// so we publish it twice here, but only return videoTrack to user.
324+
if (captureScreenAudio != null) {
325+
final tracks = await LocalVideoTrack.createScreenShareTracksWithAudio(
326+
ScreenShareCaptureOptions(
327+
captureScreenAudio: captureScreenAudio));
328+
LocalTrackPublication<LocalVideoTrack>? publication;
329+
for (final track in tracks) {
330+
if (track is LocalVideoTrack) {
331+
publication = await publishVideoTrack(track);
332+
} else if (track is LocalAudioTrack) {
333+
await publishAudioTrack(track);
334+
}
335+
}
336+
337+
/// just return the video track publication
338+
return publication;
339+
}
317340
final track = await LocalVideoTrack.createScreenShareTrack(
318341
room.roomOptions.defaultScreenShareCaptureOptions);
319342
return await publishVideoTrack(track);

lib/src/track/local/audio.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22

33
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
4+
import 'package:meta/meta.dart';
45

56
import '../../proto/livekit_models.pb.dart' as lk_models;
67
import '../../types/other.dart';
@@ -15,7 +16,8 @@ class LocalAudioTrack extends LocalTrack
1516
covariant AudioCaptureOptions currentOptions;
1617

1718
// private constructor
18-
LocalAudioTrack._(
19+
@internal
20+
LocalAudioTrack(
1921
String name,
2022
TrackSource source,
2123
rtc.MediaStream stream,
@@ -36,7 +38,7 @@ class LocalAudioTrack extends LocalTrack
3638
options ??= const AudioCaptureOptions();
3739
final stream = await LocalTrack.createStream(options);
3840

39-
return LocalAudioTrack._(
41+
return LocalAudioTrack(
4042
'',
4143
TrackSource.microphone,
4244
stream,

lib/src/track/local/local.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ abstract class LocalTrack extends Track {
119119
final constraints = <String, dynamic>{
120120
'audio': options is AudioCaptureOptions
121121
? options.toMediaConstraintsMap()
122-
: false,
122+
: options is ScreenShareCaptureOptions
123+
? (options).captureScreenAudio
124+
: false,
123125
'video': options is VideoCaptureOptions
124126
? options.toMediaConstraintsMap()
125127
: false,

lib/src/track/local/video.dart

+34
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../../proto/livekit_models.pb.dart' as lk_models;
55
import '../../types/other.dart';
66
import '../options.dart';
77
import '../track.dart';
8+
import 'audio.dart';
89
import 'local.dart';
910

1011
/// A video track from the local device. Use static methods in this class to create
@@ -63,6 +64,39 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
6364
options,
6465
);
6566
}
67+
68+
/// Creates a LocalTracks(audio/video) from the display.
69+
///
70+
/// The current API is mainly used to capture audio when chrome captures tab,
71+
/// but in the future it can also be used for flutter native to open audio
72+
/// capture device when capturing screen
73+
static Future<List<LocalTrack>> createScreenShareTracksWithAudio([
74+
ScreenShareCaptureOptions? options,
75+
]) async {
76+
options ??= const ScreenShareCaptureOptions(captureScreenAudio: true);
77+
78+
final stream = await LocalTrack.createStream(options);
79+
80+
List<LocalTrack> tracks = [
81+
LocalVideoTrack._(
82+
Track.screenShareName,
83+
TrackSource.screenShareVideo,
84+
stream,
85+
stream.getVideoTracks().first,
86+
options,
87+
)
88+
];
89+
90+
if (stream.getAudioTracks().isNotEmpty) {
91+
tracks.add(LocalAudioTrack(
92+
Track.screenShareName,
93+
TrackSource.screenShareAudio,
94+
stream,
95+
stream.getAudioTracks().first,
96+
const AudioCaptureOptions()));
97+
}
98+
return tracks;
99+
}
66100
}
67101

68102
//

lib/src/track/options.dart

+4
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,19 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions {
8282
/// See instructions on how to setup your Broadcast Extension here:
8383
/// https://github.com/flutter-webrtc/flutter-webrtc/wiki/iOS-Screen-Sharing#broadcast-extension-quick-setup
8484
final bool useiOSBroadcastExtension;
85+
final bool captureScreenAudio;
86+
8587
const ScreenShareCaptureOptions({
8688
this.useiOSBroadcastExtension = false,
89+
this.captureScreenAudio = false,
8790
String? sourceId,
8891
double? maxFrameRate,
8992
VideoParameters params = VideoParametersPresets.screenShareH720FPS15,
9093
}) : super(params: params, deviceId: sourceId, maxFrameRate: maxFrameRate);
9194

9295
ScreenShareCaptureOptions.from(
9396
{this.useiOSBroadcastExtension = false,
97+
this.captureScreenAudio = false,
9498
required VideoCaptureOptions captureOptions})
9599
: super(params: captureOptions.params);
96100

0 commit comments

Comments
 (0)