mirror of
https://github.com/netbirdio/IronRDP.git
synced 2026-05-22 18:43:12 -07:00
feat: add QOI image codec
The Quite OK Image format ([1]) losslessly compresses images to a similar size of PNG, while offering 20x-50x faster encoding and 3x-4x faster decoding. Add a new QOI codec (UUID 4dae9af8-b399-4df6-b43a-662fd9c0f5d6) for SetSurface command. The PDU data contains the QOI header (14 bytes) + data "chunks" and the end marker (8 bytes). Some benchmarks showing interesting results (using ironrdp/perfenc) Bitmap: 74s user CPU, 92.5% compression RemoteFx (lossy): 201s user CPU, 96.72% compression QOI: 10s user CPU, 96.20% compression Note: the "qoicoubeh" crate is my own fork of "qoi-rust" project. The plan is to switch back to it as soon as the maintainer resume its activites (https://github.com/aldanor/qoi-rust/issues/14). [1]: https://qoiformat.org/ Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
committed by
Benoît Cortier
parent
d3aaa43c23
commit
613fd51f26
Generated
+11
@@ -2715,6 +2715,7 @@ dependencies = [
|
||||
"ironrdp-rdpsnd",
|
||||
"ironrdp-svc",
|
||||
"ironrdp-tokio",
|
||||
"qoicoubeh",
|
||||
"rayon",
|
||||
"rustls-pemfile",
|
||||
"tokio",
|
||||
@@ -2736,6 +2737,7 @@ dependencies = [
|
||||
"ironrdp-graphics",
|
||||
"ironrdp-pdu",
|
||||
"ironrdp-svc",
|
||||
"qoicoubeh",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -4161,6 +4163,15 @@ dependencies = [
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoicoubeh"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9b82aa3fef8a980075775b8c46f874823b5b4a15de327d2dbb3b6fd818480ba"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
|
||||
@@ -9,6 +9,10 @@ edition.workspace = true
|
||||
name = "perfenc"
|
||||
path = "src/perfenc.rs"
|
||||
|
||||
[features]
|
||||
default = ["qoi"]
|
||||
qoi = ["ironrdp/qoi"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
async-trait = "0.1.88"
|
||||
|
||||
@@ -28,7 +28,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
println!(" --width <WIDTH> Width of the display (default: 3840)");
|
||||
println!(" --height <HEIGHT> Height of the display (default: 2400)");
|
||||
println!(" --codec <CODEC> Codec to use (default: remotefx)");
|
||||
println!(" Valid values: remotefx, bitmap, none");
|
||||
println!(" Valid values: qoi, remotefx, bitmap, none");
|
||||
println!(" --fps <FPS> Frames per second (default: none)");
|
||||
std::process::exit(0);
|
||||
}
|
||||
@@ -52,6 +52,8 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
flags -= CmdFlags::SET_SURFACE_BITS;
|
||||
}
|
||||
OptCodec::None => {}
|
||||
#[cfg(feature = "qoi")]
|
||||
OptCodec::Qoi => update_codecs.set_qoi(Some(0)),
|
||||
};
|
||||
|
||||
let mut encoder = UpdateEncoder::new(DesktopSize { width, height }, flags, update_codecs);
|
||||
@@ -172,6 +174,8 @@ enum OptCodec {
|
||||
RemoteFX,
|
||||
Bitmap,
|
||||
None,
|
||||
#[cfg(feature = "qoi")]
|
||||
Qoi,
|
||||
}
|
||||
|
||||
impl Default for OptCodec {
|
||||
@@ -188,6 +192,8 @@ impl core::str::FromStr for OptCodec {
|
||||
"remotefx" => Ok(Self::RemoteFX),
|
||||
"bitmap" => Ok(Self::Bitmap),
|
||||
"none" => Ok(Self::None),
|
||||
#[cfg(feature = "qoi")]
|
||||
"qoi" => Ok(Self::Qoi),
|
||||
_ => Err(anyhow::anyhow!("unknown codec: {}", s)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ test = false
|
||||
default = ["rustls"]
|
||||
rustls = ["ironrdp-tls/rustls", "tokio-tungstenite/rustls-tls-native-roots"]
|
||||
native-tls = ["ironrdp-tls/native-tls", "tokio-tungstenite/native-tls"]
|
||||
qoi = ["ironrdp/qoi"]
|
||||
|
||||
[dependencies]
|
||||
# Protocols
|
||||
|
||||
@@ -16,7 +16,9 @@ doctest = false
|
||||
test = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
arbitrary = ["dep:arbitrary"]
|
||||
qoi = ["ironrdp-pdu/qoi"]
|
||||
|
||||
[dependencies]
|
||||
ironrdp-svc = { path = "../ironrdp-svc", version = "0.4" } # public
|
||||
|
||||
@@ -19,6 +19,7 @@ doctest = false
|
||||
default = []
|
||||
std = ["alloc", "ironrdp-error/std", "ironrdp-core/std"]
|
||||
alloc = ["ironrdp-core/alloc", "ironrdp-error/alloc"]
|
||||
qoi = []
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.9"
|
||||
|
||||
@@ -34,7 +34,7 @@ pub use self::bitmap_cache::{
|
||||
pub use self::bitmap_codecs::{
|
||||
client_codecs_capabilities, server_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty,
|
||||
EntropyBits, Guid, NsCodec, RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags,
|
||||
CODEC_ID_NONE, CODEC_ID_REMOTEFX,
|
||||
CODEC_ID_NONE, CODEC_ID_QOI, CODEC_ID_REMOTEFX,
|
||||
};
|
||||
pub use self::brush::{Brush, SupportLevel};
|
||||
pub use self::frame_acknowledge::FrameAcknowledge;
|
||||
|
||||
@@ -40,6 +40,9 @@ const GUID_REMOTEFX: Guid = Guid(0x7677_2f12, 0xbd72, 0x4463, 0xaf, 0xb3, 0xb7,
|
||||
const GUID_IMAGE_REMOTEFX: Guid = Guid(0x2744_ccd4, 0x9d8a, 0x4e74, 0x80, 0x3c, 0x0e, 0xcb, 0xee, 0xa1, 0x9c, 0x54);
|
||||
#[rustfmt::skip]
|
||||
const GUID_IGNORE: Guid = Guid(0x9c43_51a6, 0x3535, 0x42ae, 0x91, 0x0c, 0xcd, 0xfc, 0xe5, 0x76, 0x0b, 0x58);
|
||||
#[rustfmt::skip]
|
||||
#[cfg(feature="qoi")]
|
||||
const GUID_QOI: Guid = Guid(0x4dae_9af8, 0xb399, 0x4df6, 0xb4, 0x3a, 0x66, 0x2f, 0xd9, 0xc0, 0xf5, 0xd6);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Guid(u32, u16, u16, u8, u8, u8, u8, u8, u8, u8, u8);
|
||||
@@ -167,6 +170,8 @@ impl Encode for Codec {
|
||||
CodecProperty::RemoteFx(_) => GUID_REMOTEFX,
|
||||
CodecProperty::ImageRemoteFx(_) => GUID_IMAGE_REMOTEFX,
|
||||
CodecProperty::Ignore => GUID_IGNORE,
|
||||
#[cfg(feature = "qoi")]
|
||||
CodecProperty::Qoi => GUID_QOI,
|
||||
_ => return Err(other_err!("invalid codec")),
|
||||
};
|
||||
guid.encode(dst)?;
|
||||
@@ -204,6 +209,8 @@ impl Encode for Codec {
|
||||
}
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "qoi")]
|
||||
CodecProperty::Qoi => dst.write_u16(0),
|
||||
CodecProperty::Ignore => dst.write_u16(0),
|
||||
CodecProperty::None => dst.write_u16(0),
|
||||
};
|
||||
@@ -227,6 +234,8 @@ impl Encode for Codec {
|
||||
RemoteFxContainer::ClientContainer(container) => container.size(),
|
||||
RemoteFxContainer::ServerContainer(size) => *size,
|
||||
},
|
||||
#[cfg(feature = "qoi")]
|
||||
CodecProperty::Qoi => 0,
|
||||
CodecProperty::Ignore => 0,
|
||||
CodecProperty::None => 0,
|
||||
}
|
||||
@@ -264,6 +273,13 @@ impl<'de> Decode<'de> for Codec {
|
||||
}
|
||||
}
|
||||
GUID_IGNORE => CodecProperty::Ignore,
|
||||
#[cfg(feature = "qoi")]
|
||||
GUID_QOI => {
|
||||
if !property_buffer.is_empty() {
|
||||
return Err(invalid_field_err!("qoi property", "must be empty"));
|
||||
}
|
||||
CodecProperty::Qoi
|
||||
}
|
||||
_ => CodecProperty::None,
|
||||
};
|
||||
|
||||
@@ -283,6 +299,8 @@ pub enum CodecProperty {
|
||||
RemoteFx(RemoteFxContainer),
|
||||
ImageRemoteFx(RemoteFxContainer),
|
||||
Ignore,
|
||||
#[cfg(feature = "qoi")]
|
||||
Qoi,
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -620,12 +638,14 @@ pub struct CodecId(u8);
|
||||
|
||||
pub const CODEC_ID_NONE: CodecId = CodecId(0);
|
||||
pub const CODEC_ID_REMOTEFX: CodecId = CodecId(3);
|
||||
pub const CODEC_ID_QOI: CodecId = CodecId(0x0A);
|
||||
|
||||
impl Debug for CodecId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let name = match self.0 {
|
||||
0 => "None",
|
||||
3 => "RemoteFx",
|
||||
0x0A => "QOI",
|
||||
_ => "unknown",
|
||||
};
|
||||
write!(f, "CodecId({name})")
|
||||
@@ -637,6 +657,7 @@ impl CodecId {
|
||||
match value {
|
||||
0 => Some(CODEC_ID_NONE),
|
||||
3 => Some(CODEC_ID_REMOTEFX),
|
||||
0x0A => Some(CODEC_ID_QOI),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -678,6 +699,7 @@ fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result<HashMap<&'a str, boo
|
||||
/// # List of codecs
|
||||
///
|
||||
/// * `remotefx` (on by default)
|
||||
/// * `qoi` (on by default, when feature "qoi")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -688,6 +710,7 @@ pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, Strin
|
||||
return Err(r#"
|
||||
List of codecs:
|
||||
- `remotefx` (on by default)
|
||||
- `qoi` (on by default, when feature "qoi")
|
||||
"#
|
||||
.to_owned());
|
||||
}
|
||||
@@ -708,6 +731,14 @@ List of codecs:
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
if config.remove("qoi").unwrap_or(true) {
|
||||
codecs.push(Codec {
|
||||
id: CODEC_ID_QOI.0,
|
||||
property: CodecProperty::Qoi,
|
||||
});
|
||||
}
|
||||
|
||||
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
|
||||
if !codec_names.is_empty() {
|
||||
return Err(format!("Unknown codecs: {codec_names}"));
|
||||
@@ -728,6 +759,7 @@ List of codecs:
|
||||
/// # List of codecs
|
||||
///
|
||||
/// * `remotefx` (on by default)
|
||||
/// * `qoi` (on by default, when feature "qoi")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -738,6 +770,7 @@ pub fn server_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, Strin
|
||||
return Err(r#"
|
||||
List of codecs:
|
||||
- `remotefx` (on by default)
|
||||
- `qoi` (on by default, when feature "qoi")
|
||||
"#
|
||||
.to_owned());
|
||||
}
|
||||
@@ -756,6 +789,14 @@ List of codecs:
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
if config.remove("qoi").unwrap_or(true) {
|
||||
codecs.push(Codec {
|
||||
id: 0,
|
||||
property: CodecProperty::Qoi,
|
||||
});
|
||||
}
|
||||
|
||||
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
|
||||
if !codec_names.is_empty() {
|
||||
return Err(format!("Unknown codecs: {codec_names}"));
|
||||
|
||||
@@ -16,9 +16,10 @@ doctest = true
|
||||
test = false
|
||||
|
||||
[features]
|
||||
default = ["rayon"]
|
||||
default = ["rayon", "qoi"]
|
||||
helper = ["dep:x509-cert", "dep:rustls-pemfile"]
|
||||
rayon = ["dep:rayon"]
|
||||
qoi = ["dep:qoicoubeh", "ironrdp-pdu/qoi"]
|
||||
|
||||
# Internal (PRIVATE!) features used to aid testing.
|
||||
# Don't rely on these whatsoever. They may disappear at any time.
|
||||
@@ -47,6 +48,7 @@ rustls-pemfile = { version = "2.2.0", optional = true }
|
||||
rayon = { version = "1.10.0", optional = true }
|
||||
bytes = "1"
|
||||
visibility = { version = "0.1", optional = true }
|
||||
qoicoubeh = { version = "0.5", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["sync"] }
|
||||
|
||||
@@ -33,18 +33,30 @@ enum CodecId {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UpdateEncoderCodecs {
|
||||
remotefx: Option<(EntropyBits, u8)>,
|
||||
#[cfg(feature = "qoi")]
|
||||
qoi: Option<u8>,
|
||||
}
|
||||
|
||||
impl UpdateEncoderCodecs {
|
||||
#[cfg_attr(feature = "__bench", visibility::make(pub))]
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { remotefx: None }
|
||||
Self {
|
||||
remotefx: None,
|
||||
#[cfg(feature = "qoi")]
|
||||
qoi: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "__bench", visibility::make(pub))]
|
||||
pub(crate) fn set_remotefx(&mut self, remotefx: Option<(EntropyBits, u8)>) {
|
||||
self.remotefx = remotefx
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
#[cfg_attr(feature = "__bench", visibility::make(pub))]
|
||||
pub(crate) fn set_qoi(&mut self, qoi: Option<u8>) {
|
||||
self.qoi = qoi
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UpdateEncoderCodecs {
|
||||
@@ -78,6 +90,11 @@ impl UpdateEncoder {
|
||||
bitmap = BitmapUpdater::RemoteFx(RemoteFxHandler::new(algo, id, desktop_size));
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
if let Some(id) = codecs.qoi {
|
||||
bitmap = BitmapUpdater::Qoi(QoiHandler::new(id));
|
||||
}
|
||||
|
||||
bitmap
|
||||
} else {
|
||||
BitmapUpdater::Bitmap(BitmapHandler::new())
|
||||
@@ -287,6 +304,8 @@ enum BitmapUpdater {
|
||||
None(NoneHandler),
|
||||
Bitmap(BitmapHandler),
|
||||
RemoteFx(RemoteFxHandler),
|
||||
#[cfg(feature = "qoi")]
|
||||
Qoi(QoiHandler),
|
||||
}
|
||||
|
||||
impl BitmapUpdater {
|
||||
@@ -295,6 +314,8 @@ impl BitmapUpdater {
|
||||
Self::None(up) => up.handle(bitmap),
|
||||
Self::Bitmap(up) => up.handle(bitmap),
|
||||
Self::RemoteFx(up) => up.handle(bitmap),
|
||||
#[cfg(feature = "qoi")]
|
||||
Self::Qoi(up) => up.handle(bitmap),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +429,44 @@ impl BitmapUpdateHandler for RemoteFxHandler {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
#[derive(Clone, Debug)]
|
||||
struct QoiHandler {
|
||||
codec_id: u8,
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
impl QoiHandler {
|
||||
fn new(codec_id: u8) -> Self {
|
||||
Self { codec_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
impl BitmapUpdateHandler for QoiHandler {
|
||||
fn handle(&mut self, bitmap: &BitmapUpdate) -> Result<UpdateFragmenter> {
|
||||
use ironrdp_graphics::image_processing::PixelFormat::*;
|
||||
|
||||
let raw_channels = match bitmap.format {
|
||||
ARgb32 => qoi::RawChannels::Argb,
|
||||
XRgb32 => qoi::RawChannels::Xrgb,
|
||||
ABgr32 => qoi::RawChannels::Abgr,
|
||||
XBgr32 => qoi::RawChannels::Xbgr,
|
||||
BgrA32 => qoi::RawChannels::Bgra,
|
||||
BgrX32 => qoi::RawChannels::Bgrx,
|
||||
RgbA32 => qoi::RawChannels::Rgba,
|
||||
RgbX32 => qoi::RawChannels::Rgbx,
|
||||
};
|
||||
|
||||
let enc = qoi::EncoderBuilder::new(&bitmap.data, bitmap.width.get().into(), bitmap.height.get().into())
|
||||
.stride(bitmap.stride)
|
||||
.raw_channels(raw_channels)
|
||||
.build()?;
|
||||
let data = enc.encode_to_vec()?;
|
||||
set_surface(bitmap, self.codec_id, &data)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_surface(bitmap: &BitmapUpdate, codec_id: u8, data: &[u8]) -> Result<UpdateFragmenter> {
|
||||
let destination = ExclusiveRectangle {
|
||||
left: bitmap.x,
|
||||
|
||||
@@ -55,6 +55,14 @@ impl RdpServerOptions {
|
||||
.iter()
|
||||
.any(|codec| matches!(codec.property, CodecProperty::RemoteFx(_)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "qoi")]
|
||||
fn has_qoi(&self) -> bool {
|
||||
self.codecs
|
||||
.0
|
||||
.iter()
|
||||
.any(|codec| matches!(codec.property, CodecProperty::Qoi))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -742,6 +750,10 @@ impl RdpServer {
|
||||
}
|
||||
}
|
||||
CodecProperty::NsCodec(_) => (),
|
||||
#[cfg(feature = "qoi")]
|
||||
CodecProperty::Qoi if self.opts.has_qoi() => {
|
||||
update_codecs.set_qoi(Some(codec.id));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ categories.workspace = true
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
qoi = ["dep:qoicoubeh", "ironrdp-pdu/qoi"]
|
||||
|
||||
[dependencies]
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.6" } # public # TODO: at some point, this dependency could be removed (good for compilation speed)
|
||||
@@ -25,7 +29,7 @@ ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.4" } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.5", features = ["std"] } # public
|
||||
ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.3" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
qoicoubeh = { version = "0.5", optional = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
||||
@@ -361,6 +361,23 @@ impl Processor {
|
||||
.or(Some(rectangle));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "qoi")]
|
||||
ironrdp_pdu::rdp::capability_sets::CODEC_ID_QOI => {
|
||||
let (header, decoded) = qoi::decode_to_vec(bits.extended_bitmap_data.data)
|
||||
.map_err(|e| reason_err!("QOI decode", "{}", e))?;
|
||||
match header.channels {
|
||||
qoi::Channels::Rgb => {
|
||||
let rectangle = image.apply_rgb24(&decoded, &destination, false)?;
|
||||
|
||||
update_rectangle = update_rectangle
|
||||
.map(|rect: InclusiveRectangle| rect.union(&rectangle))
|
||||
.or(Some(rectangle));
|
||||
}
|
||||
qoi::Channels::Rgba => {
|
||||
warn!("Unsupported RGBA QOI data");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Unsupported codec ID: {}", bits.extended_bitmap_data.codec_id);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ ironrdp-graphics.path = "../ironrdp-graphics"
|
||||
ironrdp-input.path = "../ironrdp-input"
|
||||
ironrdp-rdcleanpath.path = "../ironrdp-rdcleanpath"
|
||||
ironrdp-rdpsnd.path = "../ironrdp-rdpsnd"
|
||||
ironrdp-session.path = "../ironrdp-session"
|
||||
ironrdp-session = { path = "../ironrdp-session", features = ["qoi"] }
|
||||
ironrdp-propertyset.path = "../ironrdp-propertyset"
|
||||
ironrdp-rdpfile.path = "../ironrdp-rdpfile"
|
||||
png = "0.17"
|
||||
|
||||
@@ -14,11 +14,23 @@ mod tests {
|
||||
|
||||
let config = &["remotefx:on"];
|
||||
let capabilities = client_codecs_capabilities(config).unwrap();
|
||||
assert_eq!(capabilities.0.len(), 1);
|
||||
assert!(matches!(capabilities.0[0].property, CodecProperty::RemoteFx(_)));
|
||||
assert!(capabilities
|
||||
.0
|
||||
.iter()
|
||||
.any(|cap| matches!(cap.property, CodecProperty::RemoteFx(_))));
|
||||
|
||||
let config = &["remotefx:off"];
|
||||
let capabilities = client_codecs_capabilities(config).unwrap();
|
||||
assert_eq!(capabilities.0.len(), 0);
|
||||
assert!(!capabilities
|
||||
.0
|
||||
.iter()
|
||||
.any(|cap| matches!(cap.property, CodecProperty::RemoteFx(_))));
|
||||
|
||||
let config = &["qoi:on"];
|
||||
let capabilities = client_codecs_capabilities(config).unwrap();
|
||||
assert!(capabilities
|
||||
.0
|
||||
.iter()
|
||||
.any(|cap| matches!(cap.property, CodecProperty::Qoi)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
[features]
|
||||
default = ["panic_hook"]
|
||||
panic_hook = ["iron-remote-desktop/panic_hook"]
|
||||
qoi = ["ironrdp/qoi"]
|
||||
|
||||
[dependencies]
|
||||
# Protocols
|
||||
@@ -31,7 +32,7 @@ ironrdp = { path = "../ironrdp", features = [
|
||||
"dvc",
|
||||
"cliprdr",
|
||||
"svc",
|
||||
"displaycontrol"
|
||||
"displaycontrol",
|
||||
] }
|
||||
ironrdp-core.path = "../ironrdp-core"
|
||||
ironrdp-cliprdr-format.path = "../ironrdp-cliprdr-format"
|
||||
|
||||
@@ -31,6 +31,7 @@ dvc = ["dep:ironrdp-dvc"]
|
||||
rdpdr = ["dep:ironrdp-rdpdr"]
|
||||
rdpsnd = ["dep:ironrdp-rdpsnd"]
|
||||
displaycontrol = ["dep:ironrdp-displaycontrol"]
|
||||
qoi = ["ironrdp-server?/qoi", "ironrdp-pdu?/qoi", "ironrdp-connector?/qoi", "ironrdp-session?/qoi"]
|
||||
# Internal (PRIVATE!) features used to aid testing.
|
||||
# Don't rely on these whatsoever. They may disappear at any time.
|
||||
__bench = ["ironrdp-server/__bench"]
|
||||
|
||||
Reference in New Issue
Block a user