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:
@@ -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})';
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user