Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ironrdp-dvc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use alloc::vec::Vec;
use core::any::TypeId;
use core::fmt;

use crate::alloc::borrow::ToOwned as _;
use ironrdp_core::{Decode as _, DecodeResult, ReadCursor, impl_as_any};
use ironrdp_pdu::{self as pdu, decode_err, encode_err, pdu_other_err};
use ironrdp_svc::{ChannelFlags, CompressionCondition, SvcClientProcessor, SvcMessage, SvcProcessor};
use pdu::PduResult;
use pdu::gcc::ChannelName;
use tracing::debug;

use crate::alloc::borrow::ToOwned as _;
use crate::pdu::{
CapabilitiesResponsePdu, CapsVersion, ClosePdu, CreateResponsePdu, CreationStatus, DrdynvcClientPdu,
DrdynvcServerPdu,
Expand Down
3 changes: 1 addition & 2 deletions crates/ironrdp-dvc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

extern crate alloc;

use core::any::TypeId;

use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::any::TypeId;

use pdu::DrdynvcDataPdu;

Expand Down
3 changes: 2 additions & 1 deletion crates/ironrdp-egfx/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,10 @@ pub trait H264Decoder: Send {

#[cfg(feature = "openh264")]
mod openh264_impl {
use super::{DecodedFrame, DecoderError, DecoderResult, H264Decoder};
use tracing::warn;

use super::{DecodedFrame, DecoderError, DecoderResult, H264Decoder};

/// H.264 decoder backed by Cisco's OpenH264 library
///
/// This decoder converts AVC-format NAL units to Annex B format
Expand Down
163 changes: 160 additions & 3 deletions crates/ironrdp-egfx/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ use crate::CHANNEL_NAME;
use crate::pdu::{
Avc420BitmapStream, Avc420Region, Avc444BitmapStream, CacheImportOfferPdu, CacheImportReplyPdu,
CapabilitiesAdvertisePdu, CapabilitiesConfirmPdu, CapabilitiesV8Flags, CapabilitiesV10Flags, CapabilitiesV81Flags,
CapabilitiesV103Flags, CapabilitiesV104Flags, CapabilitiesV107Flags, CapabilitySet, Codec1Type, CreateSurfacePdu,
DeleteSurfacePdu, Encoding, EndFramePdu, FrameAcknowledgePdu, GfxPdu, MapSurfaceToOutputPdu, PixelFormat,
QoeFrameAcknowledgePdu, ResetGraphicsPdu, StartFramePdu, Timestamp, WireToSurface1Pdu, encode_avc420_bitmap_stream,
CapabilitiesV103Flags, CapabilitiesV104Flags, CapabilitiesV107Flags, CapabilitySet, Codec1Type, Codec2Type,
CreateSurfacePdu, DeleteSurfacePdu, Encoding, EndFramePdu, FrameAcknowledgePdu, GfxPdu, MapSurfaceToOutputPdu,
PixelFormat, QoeFrameAcknowledgePdu, ResetGraphicsPdu, StartFramePdu, Timestamp, WireToSurface1Pdu,
WireToSurface2Pdu, encode_avc420_bitmap_stream,
};

// ============================================================================
Expand Down Expand Up @@ -863,6 +864,31 @@ pub struct GraphicsPipelineServer {
compression_mode: CompressionMode,
}

/// Payload for a single tile within a mixed-codec frame.
///
/// Each variant corresponds to a different EGFX codec. Used with
/// [`GraphicsPipelineServer::send_mixed_frame()`] to pack multiple codec
/// types into a single `StartFrame`/`EndFrame` pair.
pub enum MixedTilePayload {
/// Lossless ClearCodec tile (text, UI elements, icons).
/// `bitmap_data` is a pre-encoded ClearCodec bitmap stream.
ClearCodec {
destination: InclusiveRectangle,
bitmap_data: Vec<u8>,
},
/// RemoteFX Progressive tile (photos, gradients).
/// `progressive_data` is a valid progressive block stream.
RemoteFxProgressive {
codec_context_id: u32,
progressive_data: Vec<u8>,
},
/// H.264 AVC420 tile (video, high-motion content).
Avc420 {
regions: Vec<Avc420Region>,
h264_data: Vec<u8>,
},
}

impl GraphicsPipelineServer {
/// Create a new GraphicsPipelineServer
pub fn new(handler: Box<dyn GraphicsPipelineHandler>) -> Self {
Expand Down Expand Up @@ -1408,6 +1434,137 @@ impl GraphicsPipelineServer {
Some(frame_id)
}

/// Queue a RemoteFX Progressive frame for transmission.
///
/// Progressive frames use `WireToSurface2Pdu` with a pre-encoded progressive
/// block stream as the bitmap payload. The `codec_context_id` associates
/// this data with persistent tile state on the client.
///
/// The `progressive_data` must be a valid progressive block stream
/// (SYNC + CONTEXT + FRAME_BEGIN + REGION + FRAME_END) as produced by
/// `ironrdp_pdu::codecs::rfx::progressive::encode_progressive_stream()`.
///
/// Returns `Some(frame_id)` if queued, `None` if not ready or backpressured.
pub fn send_remotefx_progressive_frame(
&mut self,
surface_id: u16,
codec_context_id: u32,
progressive_data: Vec<u8>,
timestamp_ms: u32,
) -> Option<u32> {
if !self.is_ready() {
return None;
}
if self.should_backpressure() {
return None;
}

let surface = self.surfaces.get(surface_id)?;

let timestamp = Self::make_timestamp(timestamp_ms);
let frame_id = self.frames.begin_frame(timestamp);

self.output_queue
.push_back(GfxPdu::StartFrame(StartFramePdu { timestamp, frame_id }));

self.output_queue.push_back(GfxPdu::WireToSurface2(WireToSurface2Pdu {
surface_id,
codec_id: Codec2Type::RemoteFxProgressive,
codec_context_id,
pixel_format: surface.pixel_format,
bitmap_data: progressive_data,
}));

self.output_queue.push_back(GfxPdu::EndFrame(EndFramePdu { frame_id }));

Some(frame_id)
}

// ========================================================================
// Mixed-Codec Frame Support
// ========================================================================

/// Queue a mixed-codec frame containing tiles encoded with different codecs.
///
/// This is the core of multi-codec EGFX: a single frame update can contain
/// ClearCodec tiles (lossless text), Progressive tiles (photos), and H.264
/// tiles (video), all sent between one `StartFrame`/`EndFrame` pair.
///
/// This matches how Azure VDI achieves its visual quality — each tile uses
/// the codec best suited to its content type.
///
/// Returns `Some(frame_id)` if queued, `None` if not ready or backpressured.
pub fn send_mixed_frame(
&mut self,
surface_id: u16,
tiles: Vec<MixedTilePayload>,
timestamp_ms: u32,
) -> Option<u32> {
if !self.is_ready() {
return None;
}
if self.should_backpressure() {
return None;
}
if tiles.is_empty() {
return None;
}

let surface = self.surfaces.get(surface_id)?;
let pixel_format = surface.pixel_format;

let timestamp = Self::make_timestamp(timestamp_ms);
let frame_id = self.frames.begin_frame(timestamp);

self.output_queue
.push_back(GfxPdu::StartFrame(StartFramePdu { timestamp, frame_id }));

for tile in tiles {
match tile {
MixedTilePayload::ClearCodec {
destination,
bitmap_data,
} => {
self.output_queue.push_back(GfxPdu::WireToSurface1(WireToSurface1Pdu {
surface_id,
codec_id: Codec1Type::ClearCodec,
pixel_format,
destination_rectangle: destination,
bitmap_data,
}));
}
MixedTilePayload::RemoteFxProgressive {
codec_context_id,
progressive_data,
} => {
self.output_queue.push_back(GfxPdu::WireToSurface2(WireToSurface2Pdu {
surface_id,
codec_id: Codec2Type::RemoteFxProgressive,
codec_context_id,
pixel_format,
bitmap_data: progressive_data,
}));
}
MixedTilePayload::Avc420 { regions, h264_data } => {
let encoded_stream = encode_avc420_bitmap_stream(&regions, &h264_data);
let target_rect = Self::compute_dest_rect(&regions, surface.width, surface.height);

self.output_queue.push_back(GfxPdu::WireToSurface1(WireToSurface1Pdu {
surface_id,
codec_id: Codec1Type::Avc420,
pixel_format,
destination_rectangle: target_rect,
bitmap_data: encoded_stream,
}));
}
}
}

self.output_queue.push_back(GfxPdu::EndFrame(EndFramePdu { frame_id }));

Some(frame_id)
}

// ========================================================================
// Output Management
// ========================================================================
Expand Down
1 change: 1 addition & 0 deletions crates/ironrdp-graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod dwt;
pub mod dwt_extrapolate;
pub mod image_processing;
pub mod pointer;
pub mod progressive;
pub mod quantization;
pub mod rdp6;
pub mod rectangle_processing;
Expand Down
Loading
Loading