Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Devolutions/IronRDP/llms.txt
Use this file to discover all available pages before exploring further.
The ironrdp-rdpsnd crate implements the RDPSND static virtual channel for audio output redirection as specified in MS-RDPEA. This channel enables playback of remote audio on the local machine.
Overview
The RDPSND channel handles audio format negotiation, volume control, and streaming audio data from server to client. It supports multiple audio formats including PCM, ADPCM, and OPUS.
Architecture
The crate provides both client and server implementations:
- Client (
client module): Receives and plays audio from remote server
- Server (
server module): Sends audio to remote client
Client API
Rdpsnd
The client-side RDPSND processor.
pub struct Rdpsnd {
handler: Box<dyn RdpsndClientHandler>,
state: RdpsndState,
server_format: Option<ServerAudioFormatPdu>,
}
Constructor:
impl Rdpsnd {
pub const NAME: ChannelName = ChannelName::from_static(b"rdpsnd\0\0");
pub fn new(handler: Box<dyn RdpsndClientHandler>) -> Self
}
See: ironrdp-rdpsnd/src/client.rs:58
RdpsndClientHandler
Trait for handling audio output operations:
pub trait RdpsndClientHandler: Send + core::fmt::Debug {
fn get_flags(&self) -> AudioFormatFlags;
fn get_formats(&self) -> &[AudioFormat];
fn wave(&mut self, format_no: usize, ts: u32, data: Cow<'_, [u8]>);
fn set_volume(&mut self, volume: VolumePdu);
fn set_pitch(&mut self, pitch: PitchPdu);
fn close(&mut self);
}
See: ironrdp-rdpsnd/src/client.rs:13
Methods:
get_flags() - Returns supported capability flags (e.g., VOLUME, PITCH)
get_formats() - Returns list of supported audio formats
wave() - Called when audio data arrives for playback
set_volume() - Called when volume change is requested
set_pitch() - Called when pitch change is requested
close() - Called when audio channel is closed
Server API
RdpsndServer
The server-side RDPSND processor.
pub struct RdpsndServer {
handler: Box<dyn RdpsndServerHandler>,
state: RdpsndState,
client_format: Option<ClientAudioFormatPdu>,
quality_mode: Option<QualityMode>,
block_no: u8,
format_no: Option<u16>,
}
Constructor:
impl RdpsndServer {
pub const NAME: ChannelName = ChannelName::from_static(b"rdpsnd\0\0");
pub fn new(handler: Box<dyn RdpsndServerHandler>) -> Self
}
See: ironrdp-rdpsnd/src/server.rs:50
Key Methods:
wave(data: Vec<u8>, ts: u32) - Sends audio data to client
set_volume(volume_left: u16, volume_right: u16) - Sets client volume
close() - Closes the audio channel
RdpsndServerHandler
Trait for handling server-side audio operations:
pub trait RdpsndServerHandler: Send + core::fmt::Debug {
fn get_formats(&self) -> &[AudioFormat];
fn start(&mut self, client_format: &ClientAudioFormatPdu) -> Option<u16>;
fn stop(&mut self);
}
See: ironrdp-rdpsnd/src/server.rs:31
pub struct AudioFormat {
pub format: WaveFormat,
pub n_channels: u16,
pub n_samples_per_sec: u32,
pub n_avg_bytes_per_sec: u32,
pub n_block_align: u16,
pub bits_per_sample: u16,
pub data: Option<Vec<u8>>,
}
pub enum WaveFormat {
PCM = 0x0001,
ADPCM = 0x0002,
ALAW = 0x0006,
MULAW = 0x0007,
GSM610 = 0x0031,
AAC = 0x00A106,
OPUS = 0x0069,
}
Usage Examples
Client Implementation
use ironrdp::rdpsnd::client::{Rdpsnd, RdpsndClientHandler};
use ironrdp::rdpsnd::pdu::{AudioFormat, WaveFormat};
struct MyAudioHandler {
// Audio output device handle
}
impl RdpsndClientHandler for MyAudioHandler {
fn get_formats(&self) -> &[AudioFormat] {
&[
AudioFormat {
format: WaveFormat::PCM,
n_channels: 2,
n_samples_per_sec: 44100,
n_avg_bytes_per_sec: 176400,
n_block_align: 4,
bits_per_sample: 16,
data: None,
},
AudioFormat {
format: WaveFormat::OPUS,
n_channels: 2,
n_samples_per_sec: 48000,
n_avg_bytes_per_sec: 192000,
n_block_align: 4,
bits_per_sample: 16,
data: None,
},
]
}
fn wave(&mut self, format_no: usize, ts: u32, data: Cow<'_, [u8]>) {
// Play audio data through output device
self.play_audio(&data);
}
fn set_volume(&mut self, volume: VolumePdu) {
// Adjust output volume
}
fn set_pitch(&mut self, pitch: PitchPdu) {
// Adjust pitch (rarely used)
}
fn close(&mut self) {
// Close audio device
}
}
// Create and register channel
let handler = Box::new(MyAudioHandler::new());
let rdpsnd = Rdpsnd::new(handler);
svc_processor.add_static_channel(rdpsnd);
Server Implementation
use ironrdp::rdpsnd::server::{RdpsndServer, RdpsndServerHandler};
struct MyServerAudioHandler;
impl RdpsndServerHandler for MyServerAudioHandler {
fn get_formats(&self) -> &[AudioFormat] {
&[
AudioFormat {
format: WaveFormat::PCM,
n_channels: 2,
n_samples_per_sec: 44100,
n_avg_bytes_per_sec: 176400,
n_block_align: 4,
bits_per_sample: 16,
data: None,
},
]
}
fn start(&mut self, client_format: &ClientAudioFormatPdu) -> Option<u16> {
// Find compatible format
for (idx, fmt) in client_format.formats.iter().enumerate() {
if self.get_formats().contains(fmt) {
return Some(idx as u16);
}
}
None
}
fn stop(&mut self) {
// Stop audio capture
}
}
// Send audio data
let data = capture_audio();
let messages = rdpsnd_server.wave(data, timestamp)?;
send_messages(messages);
Protocol Flow
Initialization (Client)
- Server sends
ServerAudioFormatPdu with supported formats
- Client responds with
ClientAudioFormatPdu containing intersected formats
- If version >= V6, client sends
QualityModePdu
- Server sends
TrainingPdu with test audio
- Client responds with
TrainingConfirmPdu
- Channel enters
Ready state
Audio Streaming
- Server sends
Wave2Pdu (v8+) or WavePdu (v7-) with audio data
- Client handler receives data via
wave() callback
- Client sends
WaveConfirmPdu to acknowledge receipt
- Server continues streaming subsequent blocks
Version-Specific Behavior
- Version 2-5: Basic audio streaming
- Version 6+: Adds quality mode support
- Version 8+: Uses
Wave2Pdu format with audio timestamp
PDU Types
Key protocol data units:
ServerAudioFormatPdu - Server’s supported audio formats
ClientAudioFormatPdu - Client’s supported audio formats
WavePdu / Wave2Pdu - Audio data packets
WaveConfirmPdu - Acknowledgment from client
TrainingPdu / TrainingConfirmPdu - Audio latency testing
VolumePdu - Volume control
PitchPdu - Pitch control
QualityModePdu - Audio quality settings
Quality Modes
pub enum QualityMode {
High = 0x0000, // High quality, more bandwidth
Medium = 0x0001, // Medium quality
Dynamic = 0x0002, // Adaptive based on bandwidth
}
- ironrdp-rdpsnd-native: Platform-specific audio output implementations
- ironrdp-svc: Static virtual channel infrastructure
- ironrdp-pdu: Protocol data unit encoding/decoding
References