Skip to content

Commit 3e63863

Browse files
committed
Add configurable timestamp offset to MCAPWriter and document behavior
1 parent b0b90e6 commit 3e63863

File tree

3 files changed

+181
-37
lines changed

3 files changed

+181
-37
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This extension is built with godot-rust and targets Godot >=4.3 APIs.
1919
- Add schemas, channels, attachments, metadata
2020
- Write full messages or header+payload to known channels
2121
- Chunking and compression (Zstd and/or LZ4 when enabled at build time)
22+
- Timestamp offset for aligning engine-relative clocks
2223
- Reader
2324
- Direct message streaming without indexes
2425
- Indexed queries when a Summary is present (time windows, per-channel, counts)
@@ -78,7 +79,7 @@ Notes
7879

7980
## Quickstart
8081

81-
All timestamps are microseconds (usec) by default. They are using the engine time since startup. However you are free to also use any other time scheme.
82+
All timestamps are microseconds (usec) by default. They use the engine time since startup, but you can shift the stored values with `MCAPWriter.set_timestamp_offset_usec()` (or `set_timestamp_offset_to_now()`) to align it them differently (or to have the timestamps start at 0). Alternatively, you can not provide a timestamp offset and write absolute timestamps if you prefer.
8283

8384
### Write an MCAP file (GDScript)
8485

@@ -93,6 +94,9 @@ if not w.open("user://out.mcap"):
9394
push_error("open failed: %s" % w.get_last_error())
9495
return
9596
97+
# Optional: treat the current engine ticks as time zero in the file
98+
w.set_timestamp_offset_to_now()
99+
96100
# Optional schema
97101
var schema_id := w.add_schema("MyType", "jsonschema", PackedByteArray())
98102
@@ -123,6 +127,10 @@ if not w.close():
123127
push_error("close failed: %s" % w.get_last_error())
124128
```
125129

130+
> Note: The timestamp offset can be changed freely until you write the first message or attachment.
131+
> After a time-bearing record is emitted the offset locks for the lifetime of the writer, and
132+
> writes that would underflow the offset will fail with an error.
133+
126134

127135
### Read messages (GDScript)
128136

@@ -181,6 +189,7 @@ Writer: `MCAPWriter` (RefCounted)
181189
- `add_channel(schema_id: int, topic: String, message_encoding: String, metadata: Dictionary) -> int`
182190
- `write(message: MCAPMessage) -> bool`
183191
- `write_to_known_channel(header: MCAPMessageHeader, data: PackedByteArray) -> bool`
192+
- `set_timestamp_offset_to_now() -> bool`, `set_timestamp_offset_usec(offset: int) -> bool`, `get_timestamp_offset_usec() -> int` (configure before writing time-bearing records)
184193
- `write_private_record(opcode: int, data: PackedByteArray, include_in_chunks: bool) -> bool`
185194
- `attach(attachment: MCAPAttachment) -> bool`
186195
- `write_metadata(meta: MCAPMetadata) -> bool`

src/api.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl MCAPChannel {
8686
#[godot_api]
8787
impl MCAPMessage {
8888
/// Create a message using the current engine time for log & publish timestamps.
89+
/// The persisted timestamp may differ if the writer applies a timestamp offset.
8990
#[func]
9091
fn create(channel: Gd<MCAPChannel>, data: PackedByteArray) -> Gd<Self> {
9192
let now = Time::singleton().get_ticks_usec();
@@ -101,6 +102,7 @@ impl MCAPMessage {
101102
}
102103

103104
/// Create a message with an explicit microsecond timestamp (used for both log & publish).
105+
/// The persisted timestamp may differ if the writer applies a timestamp offset.
104106
#[func]
105107
fn create_with_timestamp(
106108
channel: Gd<MCAPChannel>,
@@ -122,6 +124,7 @@ impl MCAPMessage {
122124
#[godot_api]
123125
impl MCAPAttachment {
124126
/// Create an attachment using the current engine time for log & create timestamps.
127+
/// The persisted timestamps may differ if the writer applies a timestamp offset.
125128
#[func]
126129
fn create(name: GString, media_type: GString, data: PackedByteArray) -> Gd<Self> {
127130
let now = Time::singleton().get_ticks_usec();
@@ -135,6 +138,7 @@ impl MCAPAttachment {
135138
}
136139

137140
/// Create an attachment with an explicit microsecond timestamp (used for both log & create).
141+
/// The persisted timestamps may differ if the writer applies a timestamp offset.
138142
#[func]
139143
fn create_with_timestamp(
140144
name: GString,
@@ -155,6 +159,7 @@ impl MCAPAttachment {
155159
#[godot_api]
156160
impl MCAPMessageHeader {
157161
/// Create a message header using the current engine time for both timestamps.
162+
/// The persisted timestamp may differ if the writer applies a timestamp offset.
158163
#[func]
159164
fn create(channel_id: i32) -> Gd<Self> {
160165
let now = Time::singleton().get_ticks_usec();
@@ -167,6 +172,7 @@ impl MCAPMessageHeader {
167172
}
168173

169174
/// Create a message header with an explicit timestamp (applied to log & publish).
175+
/// The persisted timestamp may differ if the writer applies a timestamp offset.
170176
#[func]
171177
fn create_with_timestamp(channel_id: i32, time: u64) -> Gd<Self> {
172178
Gd::from_object(Self {

0 commit comments

Comments
 (0)