From 57e11a105897f4027d1438e04244ec536c980aeb Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Thu, 28 May 2026 12:18:33 +0800 Subject: [PATCH 1/6] fix: Maintain video quality for live streaming. --- lib/src/participant/local.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index e785b72fa..fff92276f 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -598,7 +598,7 @@ class LocalParticipant extends Participant { // 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` final VideoDimensions dimensions = track.currentOptions.params.dimensions; - if (track.source == TrackSource.screenShareVideo || dimensions.height >= 1080) { + if (track.source == TrackSource.screenShareVideo || dimensions.height >= 720) { return DegradationPreference.maintainResolution; } return DegradationPreference.balanced; From dc563a28bde43573489f528d29f5fe8191d7d549 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Thu, 28 May 2026 14:39:50 +0800 Subject: [PATCH 2/6] update. --- lib/src/participant/local.dart | 4 +++- lib/src/track/options.dart | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index fff92276f..12c136286 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -599,7 +599,9 @@ class LocalParticipant extends Participant { // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` final VideoDimensions dimensions = track.currentOptions.params.dimensions; if (track.source == TrackSource.screenShareVideo || dimensions.height >= 720) { - return DegradationPreference.maintainResolution; + return track.currentOptions.liveStreaming == true + ? DegradationPreference.maintainFramerateAndResolution + : DegradationPreference.maintainResolution; } return DegradationPreference.balanced; } diff --git a/lib/src/track/options.dart b/lib/src/track/options.dart index d5bfc5bbf..8bc2648db 100644 --- a/lib/src/track/options.dart +++ b/lib/src/track/options.dart @@ -62,12 +62,14 @@ class CameraCaptureOptions extends VideoCaptureOptions { double? maxFrameRate, VideoParameters params = VideoParametersPresets.h720_169, this.stopCameraCaptureOnMute = true, + bool? liveStreaming, TrackProcessor? processor, }) : super( params: params, deviceId: deviceId, maxFrameRate: maxFrameRate, processor: processor, + liveStreaming: liveStreaming, ); CameraCaptureOptions.from({required VideoCaptureOptions captureOptions}) @@ -231,11 +233,15 @@ abstract class VideoCaptureOptions extends LocalTrackOptions { /// A processor to apply to the video track. final TrackProcessor? processor; + /// Maintain high framerate and bitrate. + final bool? liveStreaming; + const VideoCaptureOptions({ this.params = VideoParametersPresets.h540_169, this.deviceId, this.maxFrameRate, this.processor, + this.liveStreaming, }); @override From 8532227155ff4ab7068bab00733b30da399a7bb9 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Thu, 28 May 2026 15:26:17 +0800 Subject: [PATCH 3/6] update. --- lib/src/participant/local.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index 12c136286..3f799c02b 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -598,7 +598,9 @@ class LocalParticipant extends Participant { // 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` final VideoDimensions dimensions = track.currentOptions.params.dimensions; - if (track.source == TrackSource.screenShareVideo || dimensions.height >= 720) { + if (track.source == TrackSource.screenShareVideo || + dimensions.height >= 720 || + track.currentOptions.liveStreaming == true) { return track.currentOptions.liveStreaming == true ? DegradationPreference.maintainFramerateAndResolution : DegradationPreference.maintainResolution; From 5d593313d9e5e2e7a21cf54340573d3c45fb8044 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Thu, 28 May 2026 15:29:19 +0800 Subject: [PATCH 4/6] changes. --- .changes/maintain-video-quality-for-live-streaming | 1 + lib/src/participant/local.dart | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .changes/maintain-video-quality-for-live-streaming diff --git a/.changes/maintain-video-quality-for-live-streaming b/.changes/maintain-video-quality-for-live-streaming new file mode 100644 index 000000000..fbb40f1c1 --- /dev/null +++ b/.changes/maintain-video-quality-for-live-streaming @@ -0,0 +1 @@ +patch type="fixed" "Maintain video quality for live streaming via new liveStreaming capture option" diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index 3f799c02b..6125ac31a 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -594,16 +594,16 @@ class LocalParticipant extends Participant { } DegradationPreference getDefaultDegradationPreference(LocalVideoTrack track) { + // keep both framerate and resolution for live streaming. + if (track.currentOptions.liveStreaming == true) { + return DegradationPreference.maintainFramerateAndResolution; + } // a few of reasons we have different default paths: // 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` final VideoDimensions dimensions = track.currentOptions.params.dimensions; - if (track.source == TrackSource.screenShareVideo || - dimensions.height >= 720 || - track.currentOptions.liveStreaming == true) { - return track.currentOptions.liveStreaming == true - ? DegradationPreference.maintainFramerateAndResolution - : DegradationPreference.maintainResolution; + if (track.source == TrackSource.screenShareVideo || dimensions.height >= 720) { + return DegradationPreference.maintainResolution; } return DegradationPreference.balanced; } From e2d10b48e3b92a07d91c1d2f9b00309a5d91af78 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Fri, 29 May 2026 10:21:21 +0800 Subject: [PATCH 5/6] Add live streaming switch and bitrate option to the prejoin page. --- example/lib/pages/prejoin.dart | 58 ++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/example/lib/pages/prejoin.dart b/example/lib/pages/prejoin.dart index 8579db86a..9334d31b9 100644 --- a/example/lib/pages/prejoin.dart +++ b/example/lib/pages/prejoin.dart @@ -60,6 +60,8 @@ class _PreJoinPageState extends State { MediaDevice? _selectedVideoDevice; MediaDevice? _selectedAudioDevice; VideoParameters _selectedVideoParameters = VideoParametersPresets.h720_169; + int _videoBitrate = 3 * 1000 * 1000; + bool _liveStreaming = true; @override void initState() { @@ -183,6 +185,7 @@ class _PreJoinPageState extends State { _videoTrack = await LocalVideoTrack.createCameraTrack(CameraCaptureOptions( deviceId: _selectedVideoDevice!.deviceId, params: _selectedVideoParameters, + liveStreaming: _liveStreaming, )); await _videoTrack!.start(); } @@ -203,8 +206,8 @@ class _PreJoinPageState extends State { try { //create new room - const cameraEncoding = VideoEncoding( - maxBitrate: 5 * 1000 * 1000, + final cameraEncoding = VideoEncoding( + maxBitrate: _videoBitrate, maxFramerate: 30, ); @@ -449,6 +452,57 @@ class _PreJoinPageState extends State { ), ), ), + if (_enableVideo) + Padding( + padding: const EdgeInsets.only(bottom: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Live Streaming:'), + Switch( + value: _liveStreaming, + onChanged: (value) async { + setState(() { + _liveStreaming = value; + }); + await _changeLocalVideoTrack(); + if (mounted) setState(() {}); + }, + ), + ], + ), + ), + if (_enableVideo) + Padding( + padding: const EdgeInsets.only(bottom: 25), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Video Bitrate:'), + SizedBox( + width: 140, + height: 40, + child: TextFormField( + initialValue: (_videoBitrate ~/ 1000).toString(), + keyboardType: TextInputType.number, + textAlign: TextAlign.right, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + border: OutlineInputBorder(), + suffixText: 'kbps', + ), + onChanged: (value) { + final kbps = int.tryParse(value); + if (kbps != null && kbps > 0) { + _videoBitrate = kbps * 1000; + } + }, + ), + ), + ], + ), + ), Padding( padding: const EdgeInsets.only(bottom: 5), child: Row( From 7e401bf244e119c3acd905c706d22bc5b52b5e04 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Fri, 29 May 2026 11:30:03 +0800 Subject: [PATCH 6/6] revert changes for test. --- lib/src/participant/local.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index 6125ac31a..5611dd212 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -602,7 +602,7 @@ class LocalParticipant extends Participant { // 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` final VideoDimensions dimensions = track.currentOptions.params.dimensions; - if (track.source == TrackSource.screenShareVideo || dimensions.height >= 720) { + if (track.source == TrackSource.screenShareVideo || dimensions.height >= 1080) { return DegradationPreference.maintainResolution; } return DegradationPreference.balanced;