hdds-recording
Record and replay DDS messages with topic filtering, file rotation, and MCAP export.
Overview
hdds-recording provides two CLI tools for capturing and replaying DDS traffic:
- hdds-record -- Subscribe to DDS topics and save messages to file
- hdds-replay -- Read recorded files and republish messages with timing control
Supported formats:
| Feature | .hdds | .mcap |
|---|---|---|
| Self-contained | Yes | Yes |
| Indexed seeking | Yes | Yes |
| Type metadata | Yes | Yes |
| Foxglove compatible | No | Yes |
| ROS 2 compatible | No | Yes |
| Minimal dependencies | Yes | No |
Installation
cargo install hdds-recording
Or build from source:
cd crates/hdds-recording
cargo build --release
This produces two binaries: hdds-record and hdds-replay.
Quick Start
Record
# Record all topics on domain 0
hdds-record --domain 0 --output capture.hdds
# Record specific topics
hdds-record --domain 0 --output capture.hdds --topics "sensor/*,rt/cmd_vel"
# Record with file rotation (100 MB per file)
hdds-record --domain 0 --output capture.hdds --rotate-size 100
# Record for 60 seconds
hdds-record --domain 0 --output capture.hdds --duration 60
Replay
# Replay at real-time speed
hdds-replay --input capture.hdds
# Replay at 2x speed
hdds-replay --input capture.hdds --speed 2.0
# Replay as fast as possible
hdds-replay --input capture.hdds --speed 0
# Loop playback
hdds-replay --input capture.hdds --loop
# Show recording info without replaying
hdds-replay --input capture.hdds --info-only
# Dry run (iterate without publishing)
hdds-replay --input capture.hdds --dry-run
hdds-record CLI Reference
hdds-record [OPTIONS]
Options:
-d, --domain <ID> DDS domain ID [default: 0]
-o, --output <FILE> Output file path (.hdds or .mcap)
-t, --topics <PATTERN> Topic filter (comma-separated, supports wildcards)
--exclude-topics <PAT> Topics to exclude (comma-separated)
--types <PATTERN> Type filter (comma-separated)
--description <TEXT> Recording description metadata
--rotate-size <MB> Rotate files by size (megabytes)
--rotate-duration <SEC> Rotate files by duration (seconds)
--max-files <N> Maximum rotated files to keep [default: 0]
--duration <SEC> Recording duration (0 = indefinite) [default: 0]
--log-level <LEVEL> Log level [default: info]
-q, --quiet Minimal output
-h, --help Print help
-V, --version Print version
hdds-replay CLI Reference
hdds-replay [OPTIONS]
Options:
-i, --input <FILE> Input recording file (.hdds)
-s, --speed <MULT> Playback speed (1.0 = realtime, 0 = unlimited) [default: 1.0]
-l, --loop Loop playback indefinitely
-t, --topics <PATTERN> Topic filter (comma-separated, supports wildcards)
--start <SEC> Start offset in seconds [default: 0]
--end <SEC> End time in seconds (0 = play all) [default: 0]
-d, --domain <ID> DDS domain ID for publishing [default: 0]
--dry-run Iterate without publishing
--info-only Show recording info and exit
--log-level <LEVEL> Log level [default: info]
-q, --quiet Minimal output
-h, --help Print help
-V, --version Print version
Topic Filtering
Wildcard patterns are supported for both recording and replay:
| Pattern | Matches |
|---|---|
sensor/* | sensor/temp, sensor/humidity |
*/Temperature | room1/Temperature, sensor/Temperature |
*sensor* | room/sensor/temp, sensor_data |
rt/*/status | rt/robot1/status, rt/node/status |
* | All topics |
File Rotation
For long recordings, file rotation prevents individual files from growing too large:
# Rotate every 100 MB, keep last 10 files
hdds-record --output capture.hdds --rotate-size 100 --max-files 10
# Rotate every hour
hdds-record --output capture.hdds --rotate-duration 3600
Rotated files are named sequentially: capture_0001.hdds, capture_0002.hdds, etc.
Rotation policies:
| Trigger | Description |
|---|---|
| Size | Rotate when file reaches N megabytes |
| Duration | Rotate after N seconds |
| Messages | Rotate after N messages (library API only) |
Native .hdds Format
The .hdds format is a compact binary format optimized for DDS recordings:
File Structure
+----------------------------------------------------------+
| File Header (64 bytes) |
| Magic (8) | Version (4) | Flags (4) | MetaOffset (8) |
| MetaSize (4) | IndexOffset (8) | IndexCount (4) | ... |
+----------------------------------------------------------+
| Segment 0 |
| SegmentHeader (32) | Message[] | CRC32 (4) |
+----------------------------------------------------------+
| ... |
+----------------------------------------------------------+
| Index Table |
| IndexEntry[] (topic_hash, segment_id, offset, count) |
+----------------------------------------------------------+
| Metadata (JSON) |
+----------------------------------------------------------+
- Magic bytes:
HDDSREC\0 - CRC32 integrity check per segment
- FNV-1a topic hashing for indexed seeking
- JSON metadata with topic list, domain ID, hostname, and timestamps
MCAP Export
With the mcap feature flag, recordings can be exported to MCAP format for use with Foxglove Studio and ROS 2 tools:
# Record directly to MCAP
hdds-record --domain 0 --output capture.mcap
Enable the feature in Cargo.toml:
[dependencies]
hdds-recording = { path = "../hdds/crates/hdds-recording", features = ["mcap"] }
Library API
The recording and replay functionality is also available as a Rust library:
Recording
use hdds_recording::{Recorder, RecorderConfig, TopicFilter};
let config = RecorderConfig::new("capture.hdds")
.domain_id(0)
.topic_filter(TopicFilter::include(vec!["sensor/*".into()]))
.description("Test recording");
let mut recorder = Recorder::new(config);
recorder.start()?;
// Record samples
recorder.record_sample(
"sensor/temp",
"SensorData",
"0102030405060708090a0b0c00000302",
1, // sequence number
&payload, // CDR-encoded bytes
0, // QoS hash
)?;
let stats = recorder.stop()?;
println!("Recorded {} messages in {:.1}s", stats.message_count, stats.duration_secs);
Replay
use hdds_recording::{Player, PlayerConfig, PlaybackSpeed};
use std::time::Duration;
let config = PlayerConfig::new("capture.hdds")
.speed(PlaybackSpeed::Speed(2.0))
.loop_playback(false)
.start_offset(Duration::from_secs(10));
let mut player = Player::new(config);
player.open()?;
while let Some(msg) = player.next()? {
println!("{}: {} bytes", msg.topic_name, msg.payload.len());
}
let stats = player.stats();
println!("Played {} messages", stats.messages_played);
Related
- Telemetry - Runtime metrics collection
- hdds-topic-echo - Live topic monitoring
- hdds-persistence - Durable storage service