From 3407221fc454d1f690594c21c16c5f3d1a3922b7 Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Thu, 27 Apr 2023 10:14:03 +0800 Subject: [PATCH] Fix resolution/framerate/bitrate issue for publishVideoTrack (#270) * Add screenShareCaptureOptions for setScreenShareEnabled. * update. * bump version for flutter-webrtc. * chore: add maintainResolution setting for publish screen sharing track. * chore: add CaptureOptions for setCameraEnabled/setMicrophoneEnabled/setScreenShareEnabled. * update. * revert changes for videoRids. * chore: set the default sending bitrate. --- lib/src/options.dart | 14 +++++++ lib/src/participant/local.dart | 60 +++++++++++++++++++++-------- lib/src/track/options.dart | 17 +++++++- lib/src/types/video_parameters.dart | 16 ++++++++ pubspec.yaml | 4 +- 5 files changed, 92 insertions(+), 19 deletions(-) diff --git a/lib/src/options.dart b/lib/src/options.dart index dfd4f8b..5ceebaa 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -155,6 +155,20 @@ class VideoPublishOptions { this.screenShareSimulcastLayers = const [], }); + VideoPublishOptions copyWith({ + VideoEncoding? videoEncoding, + bool? simulcast, + List? videoSimulcastLayers, + List? screenShareSimulcastLayers, + }) => + VideoPublishOptions( + videoEncoding: videoEncoding ?? this.videoEncoding, + simulcast: simulcast ?? this.simulcast, + videoSimulcastLayers: videoSimulcastLayers ?? this.videoSimulcastLayers, + screenShareSimulcastLayers: + screenShareSimulcastLayers ?? this.screenShareSimulcastLayers, + ); + @override String toString() => '${runtimeType}(videoEncoding: ${videoEncoding}, simulcast: ${simulcast})'; diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index 033483e..c82ec12 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -113,6 +113,13 @@ class LocalParticipant extends Participant { publishOptions = publishOptions ?? room.roomOptions.defaultVideoPublishOptions; + // set the default sending bitrate + if (publishOptions.videoEncoding == null) { + publishOptions = publishOptions.copyWith( + videoEncoding: track.currentOptions.params.encoding, + ); + } + // use constraints passed to getUserMedia by default VideoDimensions dimensions = track.currentOptions.params.dimensions; @@ -175,6 +182,15 @@ class LocalParticipant extends Participant { init: transceiverInit, ); + // prefer to maintainResolution for screen share + if (track.source == TrackSource.screenShareVideo) { + var sender = track.transceiver!.sender; + var parameters = sender.parameters; + parameters.degradationPreference = + rtc.RTCDegradationPreference.MAINTAIN_RESOLUTION; + await sender.setParameters(parameters); + } + await room.engine.negotiate(); final pub = LocalTrackPublication( @@ -314,27 +330,36 @@ class LocalParticipant extends Participant { .toList(); /// Shortcut for publishing a [TrackSource.camera] - Future setCameraEnabled(bool enabled) async { - return setSourceEnabled(TrackSource.camera, enabled); + Future setCameraEnabled(bool enabled, + {CameraCaptureOptions? cameraCaptureOptions}) async { + return setSourceEnabled(TrackSource.camera, enabled, + cameraCaptureOptions: cameraCaptureOptions); } /// Shortcut for publishing a [TrackSource.microphone] - Future setMicrophoneEnabled(bool enabled) async { - return setSourceEnabled(TrackSource.microphone, enabled); + Future setMicrophoneEnabled(bool enabled, + {AudioCaptureOptions? audioCaptureOptions}) async { + return setSourceEnabled(TrackSource.microphone, enabled, + audioCaptureOptions: audioCaptureOptions); } /// Shortcut for publishing a [TrackSource.screenShareVideo] Future setScreenShareEnabled(bool enabled, - {bool? captureScreenAudio}) async { + {bool? captureScreenAudio, + ScreenShareCaptureOptions? screenShareCaptureOptions}) async { return setSourceEnabled(TrackSource.screenShareVideo, enabled, - captureScreenAudio: captureScreenAudio); + captureScreenAudio: captureScreenAudio, + screenShareCaptureOptions: screenShareCaptureOptions); } /// A convenience method to publish a track for a specific [TrackSource]. /// This is the recommended method to publish tracks. Future setSourceEnabled( TrackSource source, bool enabled, - {bool? captureScreenAudio}) async { + {bool? captureScreenAudio, + AudioCaptureOptions? audioCaptureOptions, + CameraCaptureOptions? cameraCaptureOptions, + ScreenShareCaptureOptions? screenShareCaptureOptions}) async { logger.fine('setSourceEnabled(source: $source, enabled: $enabled)'); final publication = getTrackPublicationBySource(source); if (publication != null) { @@ -351,21 +376,26 @@ class LocalParticipant extends Participant { return publication; } else if (enabled) { if (source == TrackSource.camera) { - final track = await LocalVideoTrack.createCameraTrack( - room.roomOptions.defaultCameraCaptureOptions); + CameraCaptureOptions captureOptions = cameraCaptureOptions ?? + room.roomOptions.defaultCameraCaptureOptions; + final track = await LocalVideoTrack.createCameraTrack(captureOptions); return await publishVideoTrack(track); } else if (source == TrackSource.microphone) { - final track = await LocalAudioTrack.create( - room.roomOptions.defaultAudioCaptureOptions); + AudioCaptureOptions captureOptions = + audioCaptureOptions ?? room.roomOptions.defaultAudioCaptureOptions; + final track = await LocalAudioTrack.create(captureOptions); return await publishAudioTrack(track); } else if (source == TrackSource.screenShareVideo) { + ScreenShareCaptureOptions captureOptions = screenShareCaptureOptions ?? + room.roomOptions.defaultScreenShareCaptureOptions; + /// 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) { + captureOptions = captureOptions.copyWith(captureScreenAudio: true); final tracks = await LocalVideoTrack.createScreenShareTracksWithAudio( - ScreenShareCaptureOptions( - captureScreenAudio: captureScreenAudio)); + captureOptions); LocalTrackPublication? publication; for (final track in tracks) { if (track is LocalVideoTrack) { @@ -378,8 +408,8 @@ class LocalParticipant extends Participant { /// just return the video track publication return publication; } - final track = await LocalVideoTrack.createScreenShareTrack( - room.roomOptions.defaultScreenShareCaptureOptions); + final track = + await LocalVideoTrack.createScreenShareTrack(captureOptions); return await publishVideoTrack(track); } } diff --git a/lib/src/track/options.dart b/lib/src/track/options.dart index 8a78cea..47abe8e 100644 --- a/lib/src/track/options.dart +++ b/lib/src/track/options.dart @@ -28,7 +28,7 @@ class CameraCaptureOptions extends VideoCaptureOptions { this.cameraPosition = CameraPosition.front, String? deviceId, double? maxFrameRate, - VideoParameters params = VideoParametersPresets.h540_169, + VideoParameters params = VideoParametersPresets.h720_169, }) : super(params: params, deviceId: deviceId, maxFrameRate: maxFrameRate); CameraCaptureOptions.from({required VideoCaptureOptions captureOptions}) @@ -89,7 +89,7 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions { this.captureScreenAudio = false, String? sourceId, double? maxFrameRate, - VideoParameters params = VideoParametersPresets.screenShareH720FPS15, + VideoParameters params = VideoParametersPresets.screenShareH1080FPS15, }) : super(params: params, deviceId: sourceId, maxFrameRate: maxFrameRate); ScreenShareCaptureOptions.from( @@ -98,6 +98,19 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions { required VideoCaptureOptions captureOptions}) : super(params: captureOptions.params); + ScreenShareCaptureOptions copyWith({ + bool? captureScreenAudio, + VideoParameters? params, + String? sourceId, + double? maxFrameRate, + }) => + ScreenShareCaptureOptions( + captureScreenAudio: captureScreenAudio ?? this.captureScreenAudio, + params: params ?? this.params, + sourceId: sourceId ?? deviceId, + maxFrameRate: maxFrameRate ?? this.maxFrameRate, + ); + @override Map toMediaConstraintsMap() { var constraints = super.toMediaConstraintsMap(); diff --git a/lib/src/types/video_parameters.dart b/lib/src/types/video_parameters.dart index 3213d69..6697e86 100644 --- a/lib/src/types/video_parameters.dart +++ b/lib/src/types/video_parameters.dart @@ -288,4 +288,20 @@ extension VideoParametersPresets on VideoParameters { maxFramerate: 30, ), ); + + static const screenShareH1440FPS30 = VideoParameters( + dimensions: VideoDimensionsPresets.h1440_169, + encoding: VideoEncoding( + maxBitrate: 5 * 1000 * 1000, + maxFramerate: 30, + ), + ); + + static const screenShareH2160FPS30 = VideoParameters( + dimensions: VideoDimensionsPresets.h2160_169, + encoding: VideoEncoding( + maxBitrate: 8 * 1000 * 1000, + maxFramerate: 30, + ), + ); } diff --git a/pubspec.yaml b/pubspec.yaml index 54dcb66..ed17116 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,10 +23,10 @@ dependencies: uuid: ^3.0.6 synchronized: ^3.0.0+3 protobuf: ^2.1.0 - flutter_webrtc: 0.9.25 + flutter_webrtc: 0.9.26 flutter_window_close: ^0.2.2 device_info_plus: ^8.0.0 - webrtc_interface: 1.0.12 + webrtc_interface: 1.0.13 dart_webrtc: 1.0.16 platform_detect: ^2.0.7