Skip to content

Commit d8147d2

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 4666efb commit d8147d2

File tree

5 files changed

+110
-63
lines changed

5 files changed

+110
-63
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ 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, CODEC_ID_NONE,
37-
CODEC_ID_REMOTEFX,
35+
client_codecs_capabilities, server_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty,
36+
EntropyBits, Guid, NsCodec, RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags,
37+
CODEC_ID_NONE, CODEC_ID_REMOTEFX,
3838
};
3939
pub use self::brush::{Brush, SupportLevel};
4040
pub use self::frame_acknowledge::FrameAcknowledge;

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

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod tests;
33

44
use core::fmt::{self, Debug};
5+
use std::collections::HashMap;
56

67
use bitflags::bitflags;
78
use ironrdp_core::{
@@ -649,6 +650,30 @@ impl CodecId {
649650
}
650651
}
651652

653+
fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result<HashMap<&'a str, bool>, String> {
654+
let mut result = HashMap::new();
655+
656+
for &codec_str in codecs {
657+
if let Some(colon_index) = codec_str.find(':') {
658+
let codec_name = &codec_str[0..colon_index];
659+
let state_str = &codec_str[colon_index + 1..];
660+
661+
let state = match state_str {
662+
"on" => true,
663+
"off" => false,
664+
_ => return Err(format!("Unhandled configuration: {}", state_str)),
665+
};
666+
667+
result.insert(codec_name, state);
668+
} else {
669+
// No colon found, assume it's "on"
670+
result.insert(codec_str, true);
671+
}
672+
}
673+
674+
Ok(result)
675+
}
676+
652677
/// This function generates a list of client codec capabilities based on the
653678
/// provided configuration.
654679
///
@@ -667,39 +692,14 @@ impl CodecId {
667692
/// A vector of `Codec` structs representing the codec capabilities, or an error
668693
/// suitable for CLI.
669694
pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
670-
use std::collections::HashMap;
671-
672-
fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result<HashMap<&'a str, bool>, String> {
673-
let mut result = HashMap::new();
674-
675-
for &codec_str in codecs {
676-
if let Some(colon_index) = codec_str.find(':') {
677-
let codec_name = &codec_str[0..colon_index];
678-
let state_str = &codec_str[colon_index + 1..];
679-
680-
let state = match state_str {
681-
"on" => true,
682-
"off" => false,
683-
_ => return Err(format!("Unhandled configuration: {}", state_str)),
684-
};
685-
686-
result.insert(codec_name, state);
687-
} else {
688-
// No colon found, assume it's "on"
689-
result.insert(codec_str, true);
690-
}
691-
}
692-
693-
Ok(result)
694-
}
695-
696695
if config.contains(&"help") {
697696
return Err(r#"
698697
List of codecs:
699698
- `remotefx` (on by default)
700699
"#
701700
.to_owned());
702701
}
702+
703703
let mut config = parse_codecs_config(config)?;
704704
let mut codecs = vec![];
705705

@@ -723,3 +723,51 @@ List of codecs:
723723

724724
Ok(BitmapCodecs(codecs))
725725
}
726+
727+
///
728+
/// This function generates a list of server codec capabilities based on the
729+
/// provided configuration.
730+
///
731+
/// # Arguments
732+
///
733+
/// * `config` - A slice of string slices that specifies which codecs to include
734+
/// in the capabilities. Codecs can be explicitly turned on ("codec:on") or
735+
/// off ("codec:off").
736+
///
737+
/// # List of codecs
738+
///
739+
/// * `remotefx` (on by default)
740+
///
741+
/// # Returns
742+
///
743+
/// A vector of `Codec` structs representing the codec capabilities.
744+
pub fn server_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, String> {
745+
if config.contains(&"help") {
746+
return Err(r#"
747+
List of codecs:
748+
- `remotefx` (on by default)
749+
"#
750+
.to_owned());
751+
}
752+
753+
let mut config = parse_codecs_config(config)?;
754+
let mut codecs = vec![];
755+
756+
if config.remove("remotefx").unwrap_or(true) {
757+
codecs.push(Codec {
758+
id: 0,
759+
property: CodecProperty::RemoteFx(RemoteFxContainer::ServerContainer(1)),
760+
});
761+
codecs.push(Codec {
762+
id: 0,
763+
property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(1)),
764+
});
765+
}
766+
767+
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
768+
if !codec_names.is_empty() {
769+
return Err(format!("Unknown codecs: {}", codec_names));
770+
}
771+
772+
Ok(BitmapCodecs(codecs))
773+
}

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)]
@@ -710,21 +725,21 @@ impl RdpServer {
710725
// We should distinguish parameters for both modes,
711726
// and somehow choose the "best", instead of picking
712727
// the last parsed here.
713-
rdp::capability_sets::CodecProperty::RemoteFx(
714-
rdp::capability_sets::RemoteFxContainer::ClientContainer(c),
715-
) if self.opts.with_remote_fx => {
728+
CodecProperty::RemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer(c))
729+
if self.opts.has_remote_fx() =>
730+
{
716731
for caps in c.caps_data.0 .0 {
717732
update_codecs.set_remotefx(Some((caps.entropy_bits, codec.id)));
718733
}
719734
}
720-
rdp::capability_sets::CodecProperty::ImageRemoteFx(
721-
rdp::capability_sets::RemoteFxContainer::ClientContainer(c),
722-
) if self.opts.with_remote_fx => {
735+
CodecProperty::ImageRemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer(
736+
c,
737+
)) if self.opts.has_image_remote_fx() => {
723738
for caps in c.caps_data.0 .0 {
724739
update_codecs.set_remotefx(Some((caps.entropy_bits, codec.id)));
725740
}
726741
}
727-
rdp::capability_sets::CodecProperty::NsCodec(_) => (),
742+
CodecProperty::NsCodec(_) => (),
728743
_ => (),
729744
}
730745
}

0 commit comments

Comments
 (0)