Skip to content

Commit 0e5616b

Browse files
committed
feat: add QOIZ image codec
Add a new QOIZ codec (UUID 229cc6dc-a860-4b52-b4d8-053a22b3892b) for SetSurface command. The PDU data contains the same data as the QOI codec, with zstd compression. Some benchmarks showing interesting results (using ironrdp/perfenc) QOI: 10s user CPU, 96.20% compression QOIZ: 11s user CPU, 99.76% compression Signed-off-by: Marc-André Lureau <[email protected]>
1 parent 6951387 commit 0e5616b

File tree

14 files changed

+252
-39
lines changed

14 files changed

+252
-39
lines changed

Cargo.lock

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ironrdp-client/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ default = ["rustls"]
2828
rustls = ["ironrdp-tls/rustls", "tokio-tungstenite/rustls-tls-native-roots"]
2929
native-tls = ["ironrdp-tls/native-tls", "tokio-tungstenite/native-tls"]
3030
qoi = ["ironrdp/qoi"]
31+
qoiz = ["ironrdp/qoiz"]
3132

3233
[dependencies]
3334
# Protocols

crates/ironrdp-connector/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ test = false
1919
default = []
2020
arbitrary = ["dep:arbitrary"]
2121
qoi = ["ironrdp-pdu/qoi"]
22+
qoiz = ["ironrdp-pdu/qoiz"]
2223

2324
[dependencies]
2425
ironrdp-svc = { path = "../ironrdp-svc", version = "0.3" } # public

crates/ironrdp-pdu/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ default = []
2020
std = ["alloc", "ironrdp-error/std", "ironrdp-core/std"]
2121
alloc = ["ironrdp-core/alloc", "ironrdp-error/alloc"]
2222
qoi = []
23+
qoiz = ["qoi"]
2324

2425
[dependencies]
2526
bitflags = "2.4"

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub use self::bitmap_cache::{
3434
pub use self::bitmap_codecs::{
3535
client_codecs_capabilities, server_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty,
3636
EntropyBits, Guid, NsCodec, RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags,
37-
CODEC_ID_NONE, CODEC_ID_QOI, CODEC_ID_REMOTEFX,
37+
CODEC_ID_NONE, CODEC_ID_QOI, CODEC_ID_QOIZ, 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

+41
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ const GUID_IGNORE: Guid = Guid(0x9c43_51a6, 0x3535, 0x42ae, 0x91, 0x0c, 0xcd, 0x
4343
#[rustfmt::skip]
4444
#[cfg(feature="qoi")]
4545
const GUID_QOI: Guid = Guid(0x4dae_9af8, 0xb399, 0x4df6, 0xb4, 0x3a, 0x66, 0x2f, 0xd9, 0xc0, 0xf5, 0xd6);
46+
#[rustfmt::skip]
47+
#[cfg(feature="qoiz")]
48+
const GUID_QOIZ: Guid = Guid(0x229c_c6dc, 0xa860, 0x4b52, 0xb4, 0xd8, 0x05, 0x3a, 0x22, 0xb3, 0x89, 0x2b);
4649

4750
#[derive(Debug, PartialEq, Eq)]
4851
pub struct Guid(u32, u16, u16, u8, u8, u8, u8, u8, u8, u8, u8);
@@ -172,6 +175,8 @@ impl Encode for Codec {
172175
CodecProperty::Ignore => GUID_IGNORE,
173176
#[cfg(feature = "qoi")]
174177
CodecProperty::Qoi => GUID_QOI,
178+
#[cfg(feature = "qoiz")]
179+
CodecProperty::QoiZ => GUID_QOIZ,
175180
_ => return Err(other_err!("invalid codec")),
176181
};
177182
guid.encode(dst)?;
@@ -211,6 +216,8 @@ impl Encode for Codec {
211216
}
212217
#[cfg(feature = "qoi")]
213218
CodecProperty::Qoi => dst.write_u16(0),
219+
#[cfg(feature = "qoiz")]
220+
CodecProperty::QoiZ => dst.write_u16(0),
214221
CodecProperty::Ignore => dst.write_u16(0),
215222
CodecProperty::None => dst.write_u16(0),
216223
};
@@ -236,6 +243,8 @@ impl Encode for Codec {
236243
},
237244
#[cfg(feature = "qoi")]
238245
CodecProperty::Qoi => 0,
246+
#[cfg(feature = "qoiz")]
247+
CodecProperty::QoiZ => 0,
239248
CodecProperty::Ignore => 0,
240249
CodecProperty::None => 0,
241250
}
@@ -280,6 +289,13 @@ impl<'de> Decode<'de> for Codec {
280289
}
281290
CodecProperty::Qoi
282291
}
292+
#[cfg(feature = "qoiz")]
293+
GUID_QOIZ => {
294+
if !property_buffer.is_empty() {
295+
return Err(invalid_field_err!("qoi property", "must be empty"));
296+
}
297+
CodecProperty::QoiZ
298+
}
283299
_ => CodecProperty::None,
284300
};
285301

@@ -301,6 +317,8 @@ pub enum CodecProperty {
301317
Ignore,
302318
#[cfg(feature = "qoi")]
303319
Qoi,
320+
#[cfg(feature = "qoiz")]
321+
QoiZ,
304322
None,
305323
}
306324

@@ -639,13 +657,15 @@ pub struct CodecId(u8);
639657
pub const CODEC_ID_NONE: CodecId = CodecId(0);
640658
pub const CODEC_ID_REMOTEFX: CodecId = CodecId(3);
641659
pub const CODEC_ID_QOI: CodecId = CodecId(0x0A);
660+
pub const CODEC_ID_QOIZ: CodecId = CodecId(0x0B);
642661

643662
impl Debug for CodecId {
644663
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645664
let name = match self.0 {
646665
0 => "None",
647666
3 => "RemoteFx",
648667
0x0A => "QOI",
668+
0x0B => "QOIZ",
649669
_ => "unknown",
650670
};
651671
write!(f, "CodecId({})", name)
@@ -658,6 +678,7 @@ impl CodecId {
658678
0 => Some(CODEC_ID_NONE),
659679
3 => Some(CODEC_ID_REMOTEFX),
660680
0x0A => Some(CODEC_ID_QOI),
681+
0x0B => Some(CODEC_ID_QOIZ),
661682
_ => None,
662683
}
663684
}
@@ -700,6 +721,7 @@ fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result<HashMap<&'a str, boo
700721
///
701722
/// * `remotefx` (on by default)
702723
/// * `qoi` (on by default, when feature "qoi")
724+
/// * `qoiz` (on by default, when feature "qoiz")
703725
///
704726
/// # Returns
705727
///
@@ -711,6 +733,7 @@ pub fn client_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, Strin
711733
List of codecs:
712734
- `remotefx` (on by default)
713735
- `qoi` (on by default, when feature "qoi")
736+
- `qoiz` (on by default, when feature "qoiz")
714737
"#
715738
.to_owned());
716739
}
@@ -739,6 +762,14 @@ List of codecs:
739762
});
740763
}
741764

765+
#[cfg(feature = "qoiz")]
766+
if config.remove("qoiz").unwrap_or(true) {
767+
codecs.push(Codec {
768+
id: CODEC_ID_QOIZ.0,
769+
property: CodecProperty::QoiZ,
770+
});
771+
}
772+
742773
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
743774
if !codec_names.is_empty() {
744775
return Err(format!("Unknown codecs: {}", codec_names));
@@ -761,6 +792,7 @@ List of codecs:
761792
///
762793
/// * `remotefx` (on by default)
763794
/// * `qoi` (on by default, when feature "qoi")
795+
/// * `qoiz` (on by default, when feature "qoiz")
764796
///
765797
/// # Returns
766798
///
@@ -771,6 +803,7 @@ pub fn server_codecs_capabilities(config: &[&str]) -> Result<BitmapCodecs, Strin
771803
List of codecs:
772804
- `remotefx` (on by default)
773805
- `qoi` (on by default, when feature "qoi")
806+
- `qoiz` (on by default, when feature "qoiz")
774807
"#
775808
.to_owned());
776809
}
@@ -797,6 +830,14 @@ List of codecs:
797830
});
798831
}
799832

833+
#[cfg(feature = "qoiz")]
834+
if config.remove("qoiz").unwrap_or(true) {
835+
codecs.push(Codec {
836+
id: 0,
837+
property: CodecProperty::QoiZ,
838+
});
839+
}
840+
800841
let codec_names = config.keys().copied().collect::<Vec<_>>().join(", ");
801842
if !codec_names.is_empty() {
802843
return Err(format!("Unknown codecs: {}", codec_names));

crates/ironrdp-server/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ name = "perfenc"
2020
required-features = ["__bench"]
2121

2222
[features]
23-
default = ["rayon", "qoi"]
23+
default = ["rayon", "qoi", "qoiz"]
2424
helper = ["dep:x509-cert", "dep:rustls-pemfile"]
2525
rayon = ["dep:rayon"]
2626
qoi = ["dep:qoi", "ironrdp-pdu/qoi"]
27+
qoiz = ["dep:zstd-safe", "qoi", "ironrdp-pdu/qoiz"]
2728

2829
# Internal (PRIVATE!) features used to aid testing.
2930
# Don't rely on these whatsoever. They may disappear at any time.
@@ -53,6 +54,7 @@ rayon = { version = "1.10.0", optional = true }
5354
bytes = "1"
5455
visibility = { version = "0.1", optional = true }
5556
qoi = { version = "0.4", optional = true }
57+
zstd-safe = { version = "7.2", optional = true }
5658

5759
[dev-dependencies]
5860
tokio = { version = "1", features = ["sync", "fs"] }

crates/ironrdp-server/examples/perfenc.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use ironrdp_server::{
1313
};
1414
use tokio::{fs::File, io::AsyncReadExt, time::sleep};
1515

16+
#[allow(clippy::similar_names)]
1617
#[tokio::main(flavor = "current_thread")]
1718
async fn main() -> Result<(), anyhow::Error> {
1819
setup_logging()?;
@@ -27,7 +28,7 @@ async fn main() -> Result<(), anyhow::Error> {
2728
println!(" --width <WIDTH> Width of the display (default: 3840)");
2829
println!(" --height <HEIGHT> Height of the display (default: 2400)");
2930
println!(" --codec <CODEC> Codec to use (default: remotefx)");
30-
println!(" Valid values: qoi, remotefx, bitmap, none");
31+
println!(" Valid values: qoi, qoiz, remotefx, bitmap, none");
3132
println!(" --fps <FPS> Frames per second (default: none)");
3233
std::process::exit(0);
3334
}
@@ -53,6 +54,8 @@ async fn main() -> Result<(), anyhow::Error> {
5354
OptCodec::None => {}
5455
#[cfg(feature = "qoi")]
5556
OptCodec::Qoi => update_codecs.set_qoi(Some(0)),
57+
#[cfg(feature = "qoiz")]
58+
OptCodec::QoiZ => update_codecs.set_qoiz(Some(0)),
5659
};
5760

5861
let mut encoder = UpdateEncoder::new(DesktopSize { width, height }, flags, update_codecs);
@@ -175,6 +178,8 @@ enum OptCodec {
175178
None,
176179
#[cfg(feature = "qoi")]
177180
Qoi,
181+
#[cfg(feature = "qoiz")]
182+
QoiZ,
178183
}
179184

180185
impl Default for OptCodec {
@@ -193,6 +198,8 @@ impl core::str::FromStr for OptCodec {
193198
"none" => Ok(Self::None),
194199
#[cfg(feature = "qoi")]
195200
"qoi" => Ok(Self::Qoi),
201+
#[cfg(feature = "qoiz")]
202+
"qoiz" => Ok(Self::QoiZ),
196203
_ => Err(anyhow::anyhow!("unknown codec: {}", s)),
197204
}
198205
}

0 commit comments

Comments
 (0)