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.
This commit is contained in:
CloudWebRTC
2023-04-27 10:14:03 +08:00
committed by GitHub
parent f07afc10be
commit 3407221fc4
5 changed files with 92 additions and 19 deletions
+14
View File
@@ -155,6 +155,20 @@ class VideoPublishOptions {
this.screenShareSimulcastLayers = const [], this.screenShareSimulcastLayers = const [],
}); });
VideoPublishOptions copyWith({
VideoEncoding? videoEncoding,
bool? simulcast,
List<VideoParameters>? videoSimulcastLayers,
List<VideoParameters>? screenShareSimulcastLayers,
}) =>
VideoPublishOptions(
videoEncoding: videoEncoding ?? this.videoEncoding,
simulcast: simulcast ?? this.simulcast,
videoSimulcastLayers: videoSimulcastLayers ?? this.videoSimulcastLayers,
screenShareSimulcastLayers:
screenShareSimulcastLayers ?? this.screenShareSimulcastLayers,
);
@override @override
String toString() => String toString() =>
'${runtimeType}(videoEncoding: ${videoEncoding}, simulcast: ${simulcast})'; '${runtimeType}(videoEncoding: ${videoEncoding}, simulcast: ${simulcast})';
+45 -15
View File
@@ -113,6 +113,13 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
publishOptions = publishOptions =
publishOptions ?? room.roomOptions.defaultVideoPublishOptions; 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 // use constraints passed to getUserMedia by default
VideoDimensions dimensions = track.currentOptions.params.dimensions; VideoDimensions dimensions = track.currentOptions.params.dimensions;
@@ -175,6 +182,15 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
init: transceiverInit, 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(); await room.engine.negotiate();
final pub = LocalTrackPublication<LocalVideoTrack>( final pub = LocalTrackPublication<LocalVideoTrack>(
@@ -314,27 +330,36 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
.toList(); .toList();
/// Shortcut for publishing a [TrackSource.camera] /// Shortcut for publishing a [TrackSource.camera]
Future<LocalTrackPublication?> setCameraEnabled(bool enabled) async { Future<LocalTrackPublication?> setCameraEnabled(bool enabled,
return setSourceEnabled(TrackSource.camera, enabled); {CameraCaptureOptions? cameraCaptureOptions}) async {
return setSourceEnabled(TrackSource.camera, enabled,
cameraCaptureOptions: cameraCaptureOptions);
} }
/// Shortcut for publishing a [TrackSource.microphone] /// Shortcut for publishing a [TrackSource.microphone]
Future<LocalTrackPublication?> setMicrophoneEnabled(bool enabled) async { Future<LocalTrackPublication?> setMicrophoneEnabled(bool enabled,
return setSourceEnabled(TrackSource.microphone, enabled); {AudioCaptureOptions? audioCaptureOptions}) async {
return setSourceEnabled(TrackSource.microphone, enabled,
audioCaptureOptions: audioCaptureOptions);
} }
/// Shortcut for publishing a [TrackSource.screenShareVideo] /// Shortcut for publishing a [TrackSource.screenShareVideo]
Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled, Future<LocalTrackPublication?> setScreenShareEnabled(bool enabled,
{bool? captureScreenAudio}) async { {bool? captureScreenAudio,
ScreenShareCaptureOptions? screenShareCaptureOptions}) async {
return setSourceEnabled(TrackSource.screenShareVideo, enabled, return setSourceEnabled(TrackSource.screenShareVideo, enabled,
captureScreenAudio: captureScreenAudio); captureScreenAudio: captureScreenAudio,
screenShareCaptureOptions: screenShareCaptureOptions);
} }
/// 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, TrackSource source, bool enabled,
{bool? captureScreenAudio}) async { {bool? captureScreenAudio,
AudioCaptureOptions? audioCaptureOptions,
CameraCaptureOptions? cameraCaptureOptions,
ScreenShareCaptureOptions? screenShareCaptureOptions}) 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) {
@@ -351,21 +376,26 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
return publication; return publication;
} else if (enabled) { } else if (enabled) {
if (source == TrackSource.camera) { if (source == TrackSource.camera) {
final track = await LocalVideoTrack.createCameraTrack( CameraCaptureOptions captureOptions = cameraCaptureOptions ??
room.roomOptions.defaultCameraCaptureOptions); room.roomOptions.defaultCameraCaptureOptions;
final track = await LocalVideoTrack.createCameraTrack(captureOptions);
return await publishVideoTrack(track); return await publishVideoTrack(track);
} else if (source == TrackSource.microphone) { } else if (source == TrackSource.microphone) {
final track = await LocalAudioTrack.create( AudioCaptureOptions captureOptions =
room.roomOptions.defaultAudioCaptureOptions); audioCaptureOptions ?? room.roomOptions.defaultAudioCaptureOptions;
final track = await LocalAudioTrack.create(captureOptions);
return await publishAudioTrack(track); return await publishAudioTrack(track);
} else if (source == TrackSource.screenShareVideo) { } else if (source == TrackSource.screenShareVideo) {
ScreenShareCaptureOptions captureOptions = screenShareCaptureOptions ??
room.roomOptions.defaultScreenShareCaptureOptions;
/// When capturing chrome table audio, we can't capture audio/video /// When capturing chrome table audio, we can't capture audio/video
/// track separately, it has to be returned once in getDisplayMedia, /// track separately, it has to be returned once in getDisplayMedia,
/// so we publish it twice here, but only return videoTrack to user. /// so we publish it twice here, but only return videoTrack to user.
if (captureScreenAudio != null) { if (captureScreenAudio != null) {
captureOptions = captureOptions.copyWith(captureScreenAudio: true);
final tracks = await LocalVideoTrack.createScreenShareTracksWithAudio( final tracks = await LocalVideoTrack.createScreenShareTracksWithAudio(
ScreenShareCaptureOptions( captureOptions);
captureScreenAudio: captureScreenAudio));
LocalTrackPublication<LocalVideoTrack>? publication; LocalTrackPublication<LocalVideoTrack>? publication;
for (final track in tracks) { for (final track in tracks) {
if (track is LocalVideoTrack) { if (track is LocalVideoTrack) {
@@ -378,8 +408,8 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
/// just return the video track publication /// just return the video track publication
return publication; return publication;
} }
final track = await LocalVideoTrack.createScreenShareTrack( final track =
room.roomOptions.defaultScreenShareCaptureOptions); await LocalVideoTrack.createScreenShareTrack(captureOptions);
return await publishVideoTrack(track); return await publishVideoTrack(track);
} }
} }
+15 -2
View File
@@ -28,7 +28,7 @@ class CameraCaptureOptions extends VideoCaptureOptions {
this.cameraPosition = CameraPosition.front, this.cameraPosition = CameraPosition.front,
String? deviceId, String? deviceId,
double? maxFrameRate, double? maxFrameRate,
VideoParameters params = VideoParametersPresets.h540_169, VideoParameters params = VideoParametersPresets.h720_169,
}) : super(params: params, deviceId: deviceId, maxFrameRate: maxFrameRate); }) : super(params: params, deviceId: deviceId, maxFrameRate: maxFrameRate);
CameraCaptureOptions.from({required VideoCaptureOptions captureOptions}) CameraCaptureOptions.from({required VideoCaptureOptions captureOptions})
@@ -89,7 +89,7 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions {
this.captureScreenAudio = false, this.captureScreenAudio = false,
String? sourceId, String? sourceId,
double? maxFrameRate, double? maxFrameRate,
VideoParameters params = VideoParametersPresets.screenShareH720FPS15, VideoParameters params = VideoParametersPresets.screenShareH1080FPS15,
}) : super(params: params, deviceId: sourceId, maxFrameRate: maxFrameRate); }) : super(params: params, deviceId: sourceId, maxFrameRate: maxFrameRate);
ScreenShareCaptureOptions.from( ScreenShareCaptureOptions.from(
@@ -98,6 +98,19 @@ class ScreenShareCaptureOptions extends VideoCaptureOptions {
required VideoCaptureOptions captureOptions}) required VideoCaptureOptions captureOptions})
: super(params: captureOptions.params); : 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 @override
Map<String, dynamic> toMediaConstraintsMap() { Map<String, dynamic> toMediaConstraintsMap() {
var constraints = super.toMediaConstraintsMap(); var constraints = super.toMediaConstraintsMap();
+16
View File
@@ -288,4 +288,20 @@ extension VideoParametersPresets on VideoParameters {
maxFramerate: 30, 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,
),
);
} }
+2 -2
View File
@@ -23,10 +23,10 @@ dependencies:
uuid: ^3.0.6 uuid: ^3.0.6
synchronized: ^3.0.0+3 synchronized: ^3.0.0+3
protobuf: ^2.1.0 protobuf: ^2.1.0
flutter_webrtc: 0.9.25 flutter_webrtc: 0.9.26
flutter_window_close: ^0.2.2 flutter_window_close: ^0.2.2
device_info_plus: ^8.0.0 device_info_plus: ^8.0.0
webrtc_interface: 1.0.12 webrtc_interface: 1.0.13
dart_webrtc: 1.0.16 dart_webrtc: 1.0.16
platform_detect: ^2.0.7 platform_detect: ^2.0.7