Skip to content

Commit 300f9a3

Browse files
feat(egfx): add MS-RDPEGFX Graphics Pipeline Extension (#1057)
Co-authored-by: lamco-office <office@lamco.io>
1 parent 4f5fdd3 commit 300f9a3

17 files changed

Lines changed: 4358 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ironrdp-egfx/CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Initial release
13+
- MS-RDPEGFX PDU types (all 23 PDUs)
14+
- Client-side DVC processor
15+
- Server-side implementation with:
16+
- Multi-surface management (Offscreen Surfaces ADM element)
17+
- Frame tracking with flow control (Unacknowledged Frames ADM element)
18+
- V8/V8.1/V10/V10.1-V10.7 capability negotiation
19+
- AVC420 and AVC444 frame sending
20+
- QoE metrics processing
21+
- Cache import handling
22+
- Resize coordination

crates/ironrdp-egfx/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "ironrdp-egfx"
3+
version = "0.1.0"
4+
readme = "README.md"
5+
description = "Graphics pipeline dynamic channel extension implementation"
6+
edition.workspace = true
7+
license.workspace = true
8+
homepage.workspace = true
9+
repository.workspace = true
10+
authors.workspace = true
11+
keywords.workspace = true
12+
categories.workspace = true
13+
14+
[lib]
15+
doctest = false
16+
# test = false
17+
18+
[dependencies]
19+
bit_field = "0.10"
20+
bitflags = "2.4"
21+
ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
22+
ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.5" } # public
23+
ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public
24+
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.7" } # public
25+
tracing = { version = "0.1", features = ["log"] }
26+
27+
[lints]
28+
workspace = true

crates/ironrdp-egfx/LICENSE-APACHE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE-APACHE

crates/ironrdp-egfx/LICENSE-MIT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE-MIT

crates/ironrdp-egfx/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# ironrdp-egfx
2+
3+
Graphics Pipeline Extension ([MS-RDPEGFX]) implementation for IronRDP.
4+
5+
Provides PDU types and client/server processors for the Display Pipeline Virtual
6+
Channel Extension, including H.264/AVC420 and AVC444 video streaming support.
7+
8+
[MS-RDPEGFX]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0

crates/ironrdp-egfx/src/client.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use ironrdp_core::{impl_as_any, ReadCursor};
2+
use ironrdp_dvc::{DvcClientProcessor, DvcMessage, DvcProcessor};
3+
use ironrdp_graphics::zgfx;
4+
use ironrdp_pdu::{decode_cursor, decode_err, PduResult};
5+
use tracing::trace;
6+
7+
use crate::{
8+
pdu::{CapabilitiesAdvertisePdu, CapabilitiesV8Flags, CapabilitySet, GfxPdu},
9+
CHANNEL_NAME,
10+
};
11+
12+
/// Max capacity to keep for decompressed buffer when cleared.
13+
const MAX_DECOMPRESSED_BUFFER_CAPACITY: usize = 16384; // 16 KiB
14+
15+
pub trait GraphicsPipelineHandler: Send {
16+
fn capabilities(&self) -> Vec<CapabilitySet> {
17+
vec![CapabilitySet::V8 {
18+
flags: CapabilitiesV8Flags::empty(),
19+
}]
20+
}
21+
22+
fn handle_pdu(&mut self, pdu: GfxPdu) {
23+
trace!(?pdu);
24+
}
25+
}
26+
27+
/// A client for the Graphics Pipeline Virtual Channel.
28+
pub struct GraphicsPipelineClient {
29+
handler: Box<dyn GraphicsPipelineHandler>,
30+
decompressor: zgfx::Decompressor,
31+
decompressed_buffer: Vec<u8>,
32+
}
33+
34+
impl GraphicsPipelineClient {
35+
pub fn new(handler: Box<dyn GraphicsPipelineHandler>) -> Self {
36+
Self {
37+
handler,
38+
decompressor: zgfx::Decompressor::new(),
39+
decompressed_buffer: Vec::new(),
40+
}
41+
}
42+
}
43+
44+
impl_as_any!(GraphicsPipelineClient);
45+
46+
impl DvcProcessor for GraphicsPipelineClient {
47+
fn channel_name(&self) -> &str {
48+
CHANNEL_NAME
49+
}
50+
51+
fn start(&mut self, _channel_id: u32) -> PduResult<Vec<DvcMessage>> {
52+
let pdu = GfxPdu::CapabilitiesAdvertise(CapabilitiesAdvertisePdu(self.handler.capabilities()));
53+
54+
Ok(vec![Box::new(pdu)])
55+
}
56+
57+
fn process(&mut self, _channel_id: u32, payload: &[u8]) -> PduResult<Vec<DvcMessage>> {
58+
self.decompressed_buffer.clear();
59+
self.decompressed_buffer.shrink_to(MAX_DECOMPRESSED_BUFFER_CAPACITY);
60+
self.decompressor
61+
.decompress(payload, &mut self.decompressed_buffer)
62+
.map_err(|e| decode_err!(e))?;
63+
64+
let mut cursor = ReadCursor::new(self.decompressed_buffer.as_slice());
65+
while !cursor.is_empty() {
66+
let pdu = decode_cursor(&mut cursor).map_err(|e| decode_err!(e))?;
67+
self.handler.handle_pdu(pdu);
68+
}
69+
70+
Ok(vec![])
71+
}
72+
}
73+
74+
impl DvcClientProcessor for GraphicsPipelineClient {}

crates/ironrdp-egfx/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![cfg_attr(doc, doc = include_str!("../README.md"))]
2+
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
3+
4+
pub(crate) const CHANNEL_NAME: &str = "Microsoft::Windows::RDS::Graphics";
5+
6+
pub mod client;
7+
pub mod pdu;
8+
pub mod server;

0 commit comments

Comments
 (0)