Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/maintain-video-quality-for-live-streaming
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
patch type="fixed" "Maintain video quality for live streaming via new liveStreaming capture option"
58 changes: 56 additions & 2 deletions example/lib/pages/prejoin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class _PreJoinPageState extends State<PreJoinPage> {
MediaDevice? _selectedVideoDevice;
MediaDevice? _selectedAudioDevice;
VideoParameters _selectedVideoParameters = VideoParametersPresets.h720_169;
int _videoBitrate = 3 * 1000 * 1000;
bool _liveStreaming = true;

@override
void initState() {
Expand Down Expand Up @@ -183,6 +185,7 @@ class _PreJoinPageState extends State<PreJoinPage> {
_videoTrack = await LocalVideoTrack.createCameraTrack(CameraCaptureOptions(
deviceId: _selectedVideoDevice!.deviceId,
params: _selectedVideoParameters,
liveStreaming: _liveStreaming,
));
await _videoTrack!.start();
}
Expand All @@ -203,8 +206,8 @@ class _PreJoinPageState extends State<PreJoinPage> {

try {
//create new room
const cameraEncoding = VideoEncoding(
maxBitrate: 5 * 1000 * 1000,
final cameraEncoding = VideoEncoding(
maxBitrate: _videoBitrate,
maxFramerate: 30,
);

Expand Down Expand Up @@ -449,6 +452,57 @@ class _PreJoinPageState extends State<PreJoinPage> {
),
),
),
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(
Expand Down
4 changes: 4 additions & 0 deletions lib/src/participant/local.dart
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,10 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
}

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`
Expand Down
6 changes: 6 additions & 0 deletions lib/src/track/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ class CameraCaptureOptions extends VideoCaptureOptions {
double? maxFrameRate,
VideoParameters params = VideoParametersPresets.h720_169,
this.stopCameraCaptureOnMute = true,
bool? liveStreaming,
TrackProcessor<VideoProcessorOptions>? processor,
}) : super(
params: params,
deviceId: deviceId,
maxFrameRate: maxFrameRate,
processor: processor,
liveStreaming: liveStreaming,
);

CameraCaptureOptions.from({required VideoCaptureOptions captureOptions})
Expand Down Expand Up @@ -231,11 +233,15 @@ abstract class VideoCaptureOptions extends LocalTrackOptions {
/// A processor to apply to the video track.
final TrackProcessor<VideoProcessorOptions>? 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
Expand Down
Loading