Skip to content

Commit 9f4f633

Browse files
committed
feature(server)!: replace with_remote_fx option with codecs
Teach the server to support customizable codecs set. Use the same logic/parsing as the client codecs configuration. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
1 parent 9728e0e commit 9f4f633

File tree

5 files changed

+101
-62
lines changed

5 files changed

+101
-62
lines changed

crates/ironrdp-pdu/src/rdp/capability_sets.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ pub use self::bitmap_cache::{
3232
BitmapCache, BitmapCacheRev2, CacheEntry, CacheFlags, CellInfo, BITMAP_CACHE_ENTRIES_NUM,
3333
};
3434
pub use self::bitmap_codecs::{
35-
client_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty, EntropyBits, Guid, NsCodec,
36-
RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags,
35+
client_codecs_capabilities, server_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty,
36+
EntropyBits, Guid, NsCodec, RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags,
3737
};
3838
pub use self::brush::{Brush, SupportLevel};
3939
pub use self::frame_acknowledge::FrameAcknowledge;

crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#[cfg(test)]
22
mod tests;
33

4+
use std::collections::HashMap;
5+
46
use bitflags::bitflags;
57
use ironrdp_core::{
68
cast_length, decode, ensure_fixed_part_size, ensure_size, invalid_field_err, other_err, Decode, DecodeResult,
@@ -637,6 +639,30 @@ impl CodecId {
637639
}
638640
}
639641

642+
fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> HashMap<&'a str, bool> {
643+
let mut result = HashMap::new();
644+
645+
for &codec_str in codecs {
646+
if let Some(colon_index) = codec_str.find(':') {
647+
let codec_name = &codec_str[0..colon_index];
648+
let state_str = &codec_str[colon_index + 1..];
649+
650+
let state = match state_str {
651+
"on" => true,
652+
"off" => false,
653+
_ => continue, // Skip entries with unknown states
654+
};
655+
656+
result.insert(codec_name, state);
657+
} else {
658+
// No colon found, assume it's "on"
659+
result.insert(codec_str, true);
660+
}
661+
}
662+
663+
result
664+
}
665+
640666
/// This function generates a list of client codec capabilities based on the
641667
/// provided configuration.
642668
///
@@ -654,32 +680,6 @@ impl CodecId {
654680
///
655681
/// A vector of `Codec` structs representing the codec capabilities.
656682
pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
657-
use std::collections::HashMap;
658-
659-
fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> HashMap<&'a str, bool> {
660-
let mut result = HashMap::new();
661-
662-
for &codec_str in codecs {
663-
if let Some(colon_index) = codec_str.find(':') {
664-
let codec_name = &codec_str[0..colon_index];
665-
let state_str = &codec_str[colon_index + 1..];
666-
667-
let state = match state_str {
668-
"on" => true,
669-
"off" => false,
670-
_ => continue, // Skip entries with unknown states
671-
};
672-
673-
result.insert(codec_name, state);
674-
} else {
675-
// No colon found, assume it's "on"
676-
result.insert(codec_str, true);
677-
}
678-
}
679-
680-
result
681-
}
682-
683683
let mut config = parse_codecs_config(config);
684684
let mut codecs = vec![];
685685

@@ -703,3 +703,43 @@ pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, Strin
703703

704704
Ok(BitmapCodecs(codecs))
705705
}
706+
707+
///
708+
/// This function generates a list of server codec capabilities based on the
709+
/// provided configuration.
710+
///
711+
/// # Arguments
712+
///
713+
/// * `config` - A slice of string slices that specifies which codecs to include
714+
/// in the capabilities. Codecs can be explicitly turned on ("codec:on") or
715+
/// off ("codec:off").
716+
///
717+
/// # List of codecs
718+
///
719+
/// * `remotefx` (on by default)
720+
///
721+
/// # Returns
722+
///
723+
/// A vector of `Codec` structs representing the codec capabilities.
724+
pub fn server_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
725+
let mut config = parse_codecs_config(config);
726+
let mut codecs = vec![];
727+
728+
if config.remove("remotefx").unwrap_or(true) {
729+
codecs.push(Codec {
730+
id: 0,
731+
property: CodecProperty::RemoteFx(RemoteFxContainer::ServerContainer(1)),
732+
});
733+
codecs.push(Codec {
734+
id: 0,
735+
property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(1)),
736+
});
737+
}
738+
739+
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
740+
if !codec_names.is_empty() {
741+
return Err(format!("Unknown codecs: {}", codec_names));
742+
}
743+
744+
Ok(BitmapCodecs(codecs))
745+
}

crates/ironrdp-server/src/builder.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::net::SocketAddr;
22

33
use anyhow::Result;
4+
use ironrdp_pdu::rdp::capability_sets::{server_codecs_capabilities, BitmapCodecs};
45
use tokio_rustls::TlsAcceptor;
56

67
use super::clipboard::CliprdrServerFactory;
@@ -25,7 +26,7 @@ pub struct WantsDisplay {
2526
pub struct BuilderDone {
2627
addr: SocketAddr,
2728
security: RdpServerSecurity,
28-
with_remote_fx: bool,
29+
codecs: BitmapCodecs,
2930
handler: Box<dyn RdpServerInputHandler>,
3031
display: Box<dyn RdpServerDisplay>,
3132
cliprdr_factory: Option<Box<dyn CliprdrServerFactory>>,
@@ -124,7 +125,7 @@ impl RdpServerBuilder<WantsDisplay> {
124125
display: Box::new(display),
125126
sound_factory: None,
126127
cliprdr_factory: None,
127-
with_remote_fx: true,
128+
codecs: server_codecs_capabilities(&[]).unwrap(),
128129
},
129130
}
130131
}
@@ -138,7 +139,7 @@ impl RdpServerBuilder<WantsDisplay> {
138139
display: Box::new(NoopDisplay),
139140
sound_factory: None,
140141
cliprdr_factory: None,
141-
with_remote_fx: true,
142+
codecs: server_codecs_capabilities(&[]).unwrap(),
142143
},
143144
}
144145
}
@@ -155,17 +156,17 @@ impl RdpServerBuilder<BuilderDone> {
155156
self
156157
}
157158

158-
pub fn with_remote_fx(mut self, enabled: bool) -> Self {
159-
self.state.with_remote_fx = enabled;
160-
self
159+
pub fn with_codecs_config(mut self, config: &[&str]) -> Result<Self, String> {
160+
self.state.codecs = server_codecs_capabilities(config)?;
161+
Ok(self)
161162
}
162163

163164
pub fn build(self) -> RdpServer {
164165
RdpServer::new(
165166
RdpServerOptions {
166167
addr: self.state.addr,
167168
security: self.state.security,
168-
with_remote_fx: self.state.with_remote_fx,
169+
codecs: self.state.codecs,
169170
},
170171
self.state.handler,
171172
self.state.display,

crates/ironrdp-server/src/capabilities.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub(crate) fn capabilities(opts: &RdpServerOptions, size: DesktopSize) -> Vec<ca
1212
capability_sets::CapabilitySet::Input(input_capabilities()),
1313
capability_sets::CapabilitySet::VirtualChannel(virtual_channel_capabilities()),
1414
capability_sets::CapabilitySet::MultiFragmentUpdate(multifragment_update()),
15-
capability_sets::CapabilitySet::BitmapCodecs(bitmap_codecs(opts.with_remote_fx)),
15+
capability_sets::CapabilitySet::BitmapCodecs(opts.codecs.clone()),
1616
]
1717
}
1818

@@ -87,20 +87,3 @@ fn multifragment_update() -> capability_sets::MultifragmentUpdate {
8787
max_request_size: 16_777_215,
8888
}
8989
}
90-
91-
fn bitmap_codecs(with_remote_fx: bool) -> capability_sets::BitmapCodecs {
92-
let mut codecs = Vec::new();
93-
if with_remote_fx {
94-
codecs.push(capability_sets::Codec {
95-
id: 0,
96-
property: capability_sets::CodecProperty::RemoteFx(capability_sets::RemoteFxContainer::ServerContainer(1)),
97-
});
98-
codecs.push(capability_sets::Codec {
99-
id: 0,
100-
property: capability_sets::CodecProperty::ImageRemoteFx(
101-
capability_sets::RemoteFxContainer::ServerContainer(1),
102-
),
103-
});
104-
}
105-
capability_sets::BitmapCodecs(codecs)
106-
}

crates/ironrdp-server/src/server.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use ironrdp_displaycontrol::server::{DisplayControlHandler, DisplayControlServer
1313
use ironrdp_pdu::input::fast_path::{FastPathInput, FastPathInputEvent};
1414
use ironrdp_pdu::input::InputEventPdu;
1515
use ironrdp_pdu::mcs::{SendDataIndication, SendDataRequest};
16-
use ironrdp_pdu::rdp::capability_sets::{BitmapCodecs, CapabilitySet, CmdFlags, GeneralExtraFlags};
16+
use ironrdp_pdu::rdp::capability_sets::{BitmapCodecs, CapabilitySet, CmdFlags, CodecProperty, GeneralExtraFlags};
1717
pub use ironrdp_pdu::rdp::client_info::Credentials;
1818
use ironrdp_pdu::rdp::headers::{ServerDeactivateAll, ShareControlPdu};
1919
use ironrdp_pdu::x224::X224;
@@ -38,7 +38,22 @@ use crate::{builder, capabilities, SoundServerFactory};
3838
pub struct RdpServerOptions {
3939
pub addr: SocketAddr,
4040
pub security: RdpServerSecurity,
41-
pub with_remote_fx: bool,
41+
pub codecs: BitmapCodecs,
42+
}
43+
impl RdpServerOptions {
44+
fn has_image_remote_fx(&self) -> bool {
45+
self.codecs
46+
.0
47+
.iter()
48+
.any(|codec| matches!(codec.property, CodecProperty::ImageRemoteFx(_)))
49+
}
50+
51+
fn has_remote_fx(&self) -> bool {
52+
self.codecs
53+
.0
54+
.iter()
55+
.any(|codec| matches!(codec.property, CodecProperty::RemoteFx(_)))
56+
}
4257
}
4358

4459
#[derive(Clone)]
@@ -709,21 +724,21 @@ impl RdpServer {
709724
// We should distinguish parameters for both modes,
710725
// and somehow choose the "best", instead of picking
711726
// the last parsed here.
712-
rdp::capability_sets::CodecProperty::RemoteFx(
713-
rdp::capability_sets::RemoteFxContainer::ClientContainer(c),
714-
) if self.opts.with_remote_fx => {
727+
CodecProperty::RemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer(c))
728+
if self.opts.has_remote_fx() =>
729+
{
715730
for caps in c.caps_data.0 .0 {
716731
rfxcodec = Some((caps.entropy_bits, codec.id));
717732
}
718733
}
719-
rdp::capability_sets::CodecProperty::ImageRemoteFx(
720-
rdp::capability_sets::RemoteFxContainer::ClientContainer(c),
721-
) if self.opts.with_remote_fx => {
734+
CodecProperty::ImageRemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer(
735+
c,
736+
)) if self.opts.has_image_remote_fx() => {
722737
for caps in c.caps_data.0 .0 {
723738
rfxcodec = Some((caps.entropy_bits, codec.id));
724739
}
725740
}
726-
rdp::capability_sets::CodecProperty::NsCodec(_) => (),
741+
CodecProperty::NsCodec(_) => (),
727742
_ => (),
728743
}
729744
}

0 commit comments

Comments
 (0)