feat: Support for capturing audio for chrome tab. (#158)
* feat: Support for capturing audio for chrome tab. * Update code comments.
This commit is contained in:
@@ -83,3 +83,6 @@ _build
|
|||||||
|
|
||||||
# FVM
|
# FVM
|
||||||
.fvm/
|
.fvm/
|
||||||
|
|
||||||
|
# flutter web
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
print('could not publish video: $e');
|
print('could not publish video: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await participant.setScreenShareEnabled(true);
|
await participant.setScreenShareEnabled(true, captureScreenAudio: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _disableScreenShare() async {
|
void _disableScreenShare() async {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../publication/local.dart';
|
|||||||
import '../track/local/audio.dart';
|
import '../track/local/audio.dart';
|
||||||
import '../track/local/local.dart';
|
import '../track/local/local.dart';
|
||||||
import '../track/local/video.dart';
|
import '../track/local/video.dart';
|
||||||
|
import '../track/options.dart';
|
||||||
import '../types/other.dart';
|
import '../types/other.dart';
|
||||||
import '../types/participant_permissions.dart';
|
import '../types/participant_permissions.dart';
|
||||||
import '../types/video_dimensions.dart';
|
import '../types/video_dimensions.dart';
|
||||||
@@ -283,14 +284,17 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut for publishing a [TrackSource.screenShareVideo]
|
/// Shortcut for publishing a [TrackSource.screenShareVideo]
|
||||||
Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled) async {
|
Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled,
|
||||||
return setSourceEnabled(TrackSource.screenShareVideo, enabled);
|
{bool? captureScreenAudio}) async {
|
||||||
|
return setSourceEnabled(TrackSource.screenShareVideo, enabled,
|
||||||
|
captureScreenAudio: captureScreenAudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience method to publish a track for a specific [TrackSource].
|
/// A convenience method to publish a track for a specific [TrackSource].
|
||||||
/// This is the recommended method to publish tracks.
|
/// This is the recommended method to publish tracks.
|
||||||
Future<LocalTrackPublication?> setSourceEnabled(
|
Future<LocalTrackPublication?> setSourceEnabled(
|
||||||
TrackSource source, bool enabled) async {
|
TrackSource source, bool enabled,
|
||||||
|
{bool? captureScreenAudio}) async {
|
||||||
logger.fine('setSourceEnabled(source: $source, enabled: $enabled)');
|
logger.fine('setSourceEnabled(source: $source, enabled: $enabled)');
|
||||||
final publication = getTrackPublicationBySource(source);
|
final publication = getTrackPublicationBySource(source);
|
||||||
if (publication != null) {
|
if (publication != null) {
|
||||||
@@ -314,6 +318,25 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
room.roomOptions.defaultAudioCaptureOptions);
|
room.roomOptions.defaultAudioCaptureOptions);
|
||||||
return await publishAudioTrack(track);
|
return await publishAudioTrack(track);
|
||||||
} else if (source == TrackSource.screenShareVideo) {
|
} else if (source == TrackSource.screenShareVideo) {
|
||||||
|
/// When capturing chrome table audio, we can't capture audio/video
|
||||||
|
/// track separately, it has to be returned once in getDisplayMedia,
|
||||||
|
/// so we publish it twice here, but only return videoTrack to user.
|
||||||
|
if (captureScreenAudio != null) {
|
||||||
|
final tracks = await LocalVideoTrack.createScreenShareTracksWithAudio(
|
||||||
|
ScreenShareCaptureOptions(
|
||||||
|
captureScreenAudio: captureScreenAudio));
|
||||||
|
LocalTrackPublication<LocalVideoTrack>? publication;
|
||||||
|
for (final track in tracks) {
|
||||||
|
if (track is LocalVideoTrack) {
|
||||||
|
publication = await publishVideoTrack(track);
|
||||||
|
} else if (track is LocalAudioTrack) {
|
||||||
|
await publishAudioTrack(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// just return the video track publication
|
||||||
|
return publication;
|
||||||
|
}
|
||||||
final track = await LocalVideoTrack.createScreenShareTrack(
|
final track = await LocalVideoTrack.createScreenShareTrack(
|
||||||
room.roomOptions.defaultScreenShareCaptureOptions);
|
room.roomOptions.defaultScreenShareCaptureOptions);
|
||||||
return await publishVideoTrack(track);
|
return await publishVideoTrack(track);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../../proto/livekit_models.pb.dart' as lk_models;
|
import '../../proto/livekit_models.pb.dart' as lk_models;
|
||||||
import '../../types/other.dart';
|
import '../../types/other.dart';
|
||||||
@@ -15,7 +16,8 @@ class LocalAudioTrack extends LocalTrack
|
|||||||
covariant AudioCaptureOptions currentOptions;
|
covariant AudioCaptureOptions currentOptions;
|
||||||
|
|
||||||
// private constructor
|
// private constructor
|
||||||
LocalAudioTrack._(
|
@internal
|
||||||
|
LocalAudioTrack(
|
||||||
String name,
|
String name,
|
||||||
TrackSource source,
|
TrackSource source,
|
||||||
rtc.MediaStream stream,
|
rtc.MediaStream stream,
|
||||||
@@ -36,7 +38,7 @@ class LocalAudioTrack extends LocalTrack
|
|||||||
options ??= const AudioCaptureOptions();
|
options ??= const AudioCaptureOptions();
|
||||||
final stream = await LocalTrack.createStream(options);
|
final stream = await LocalTrack.createStream(options);
|
||||||
|
|
||||||
return LocalAudioTrack._(
|
return LocalAudioTrack(
|
||||||
'',
|
'',
|
||||||
TrackSource.microphone,
|
TrackSource.microphone,
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ abstract class LocalTrack extends Track {
|
|||||||
final constraints = <String, dynamic>{
|
final constraints = <String, dynamic>{
|
||||||
'audio': options is AudioCaptureOptions
|
'audio': options is AudioCaptureOptions
|
||||||
? options.toMediaConstraintsMap()
|
? options.toMediaConstraintsMap()
|
||||||
|
: options is ScreenShareCaptureOptions
|
||||||
|
? (options).captureScreenAudio
|
||||||
: false,
|
: false,
|
||||||
'video': options is VideoCaptureOptions
|
'video': options is VideoCaptureOptions
|
||||||
? options.toMediaConstraintsMap()
|
? options.toMediaConstraintsMap()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../proto/livekit_models.pb.dart' as lk_models;
|
|||||||
import '../../types/other.dart';
|
import '../../types/other.dart';
|
||||||
import '../options.dart';
|
import '../options.dart';
|
||||||
import '../track.dart';
|
import '../track.dart';
|
||||||
|
import 'audio.dart';
|
||||||
import 'local.dart';
|
import 'local.dart';
|
||||||
|
|
||||||
/// A video track from the local device. Use static methods in this class to create
|
/// 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 {
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a LocalTracks(audio/video) from the display.
|
||||||
|
///
|
||||||
|
/// The current API is mainly used to capture audio when chrome captures tab,
|
||||||
|
/// but in the future it can also be used for flutter native to open audio
|
||||||
|
/// capture device when capturing screen
|
||||||
|
static Future<List<LocalTrack>> createScreenShareTracksWithAudio([
|
||||||
|
ScreenShareCaptureOptions? options,
|
||||||
|
]) async {
|
||||||
|
options ??= const ScreenShareCaptureOptions(captureScreenAudio: true);
|
||||||
|
|
||||||
|
final stream = await LocalTrack.createStream(options);
|
||||||
|
|
||||||
|
List<LocalTrack> tracks = [
|
||||||
|
LocalVideoTrack._(
|
||||||
|
Track.screenShareName,
|
||||||
|
TrackSource.screenShareVideo,
|
||||||
|
stream,
|
||||||
|
stream.getVideoTracks().first,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
if (stream.getAudioTracks().isNotEmpty) {
|
||||||
|
tracks.add(LocalAudioTrack(
|
||||||
|
Track.screenShareName,
|
||||||
|
TrackSource.screenShareAudio,
|
||||||
|
stream,
|
||||||
|
stream.getAudioTracks().first,
|
||||||
|
const AudioCaptureOptions()));
|
||||||
|
}
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -82,8 +82,11 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions {
|
|||||||
/// See instructions on how to setup your Broadcast Extension here:
|
/// See instructions on how to setup your Broadcast Extension here:
|
||||||
/// https://github.com/flutter-webrtc/flutter-webrtc/wiki/iOS-Screen-Sharing#broadcast-extension-quick-setup
|
/// https://github.com/flutter-webrtc/flutter-webrtc/wiki/iOS-Screen-Sharing#broadcast-extension-quick-setup
|
||||||
final bool useiOSBroadcastExtension;
|
final bool useiOSBroadcastExtension;
|
||||||
|
final bool captureScreenAudio;
|
||||||
|
|
||||||
const ScreenShareCaptureOptions({
|
const ScreenShareCaptureOptions({
|
||||||
this.useiOSBroadcastExtension = false,
|
this.useiOSBroadcastExtension = false,
|
||||||
|
this.captureScreenAudio = false,
|
||||||
String? sourceId,
|
String? sourceId,
|
||||||
double? maxFrameRate,
|
double? maxFrameRate,
|
||||||
VideoParameters params = VideoParametersPresets.screenShareH720FPS15,
|
VideoParameters params = VideoParametersPresets.screenShareH720FPS15,
|
||||||
@@ -91,6 +94,7 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions {
|
|||||||
|
|
||||||
ScreenShareCaptureOptions.from(
|
ScreenShareCaptureOptions.from(
|
||||||
{this.useiOSBroadcastExtension = false,
|
{this.useiOSBroadcastExtension = false,
|
||||||
|
this.captureScreenAudio = false,
|
||||||
required VideoCaptureOptions captureOptions})
|
required VideoCaptureOptions captureOptions})
|
||||||
: super(params: captureOptions.params);
|
: super(params: captureOptions.params);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user