Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 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