Complete Phase 1: add clock utility, unit test, build targets, and docs

This commit is contained in:
2026-08-28 21:57:43 +02:00
parent 742611b841
commit 975e3b2974
8 changed files with 67 additions and 16 deletions
+4 -6
View File
@@ -4,12 +4,10 @@ Last updated: initial scaffold.
## Project state
- Empty project scaffolded with Meson build, C++20, module headers, and agent
workflow files.
- No implementation yet; headers are placeholders.
- Phase plan is in `docs/PHASES.md`. Agents are now explicitly instructed in
`AGENTS.md` to read `docs/PHASES.md` before planning and not to start work
belonging to a later phase unless requested.
- Phase 1 complete: Meson build, C++20 module headers, agent workflow, clock
utility, and a passing unit test.
- Active phase is now Phase 2 (software H.264 encode/decode).
- No implementation yet for capture/codec/network/render.
## Decisions
+9 -2
View File
@@ -14,7 +14,8 @@ A native Linux peer-to-peer screencast application.
Requirements:
- C++20 compiler (GCC 12+, Clang 16+)
- Meson + Ninja
- Meson >= 0.63
- Ninja
- (later phases) FFmpeg dev packages, PipeWire dev, SDL2/3 dev
Build and run tests:
@@ -25,6 +26,12 @@ meson compile -C build
meson test -C build --print-errorlogs
```
Run with verbose test output:
```sh
meson test -C build -v
```
## Architecture
See `docs/ARCHITECTURE.md` for module boundaries and design rules.
@@ -33,7 +40,7 @@ See `docs/ARCHITECTURE.md` for module boundaries and design rules.
Development is split into phases in `docs/PHASES.md`.
Current phase: **Phase 1project skeleton**.
Current phase: **Phase 2software H.264 encode / decode**.
## License
+3 -3
View File
@@ -10,8 +10,8 @@ previous one is validated.
- [x] Meson build files (`meson.build`, `meson_options.txt`).
- [x] Public module headers with `namespace sc`.
- [ ] Unit test that exercises a trivial utility function.
- [ ] `README.md` with build instructions.
- [x] Unit test that exercises a trivial utility function.
- [x] `README.md` with build instructions.
**Validation**: `meson setup build && meson compile -C build && meson test -C build`.
@@ -89,4 +89,4 @@ where available.
## Current phase
Phase 1 — skeleton and tooling.
Phase 2 — software H.264 encode/decode.
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
namespace sc {
// RTP clock rate for video is 90 kHz. Convert a monotonic nanosecond timestamp
// into a 32-bit RTP timestamp. Overflows wrap around intentionally.
constexpr uint32_t rtp_timestamp_from_ns ( uint64_t nanoseconds ) noexcept {
// (nanoseconds * 90_000) / 1_000_000_000
// Simplify: 90_000 / 1_000_000_000 = 9 / 100_000
return static_cast<uint32_t> ( ( nanoseconds * 9ULL ) / 100'000ULL );
}
static_assert ( rtp_timestamp_from_ns ( 0 ) == 0 );
static_assert ( rtp_timestamp_from_ns ( 1'000'000'000ULL ) == 90'000 );
static_assert ( rtp_timestamp_from_ns ( 500'000'000ULL ) == 45'000 );
} // namespace sc
+1 -2
View File
@@ -10,8 +10,7 @@ project('screen_cast', 'cpp',
# Public and private include directories
inc = include_directories('include')
# Build the core static library first; dependencies will be added later as
# phases require them.
# Core public headers / include dependency defined in src/meson.build.
subdir('src')
# Tests
+6 -2
View File
@@ -1,2 +1,6 @@
# Empty stub: implementation files will be added phase by phase.
# For now the project configures successfully with no targets.
# Core public API is currently header-only. Source files will be added as
# modules are implemented in later phases.
# Declare an include-only dependency so that tests and executables can depend on
# the public headers consistently.
sc_core_inc = include_directories('../include')
+5 -1
View File
@@ -1 +1,5 @@
# Empty stub: tests will be added as modules land.
test_clock = executable('test_clock',
'utils/test_clock.cpp',
include_directories : sc_core_inc)
test('clock utils', test_clock)
+20
View File
@@ -0,0 +1,20 @@
#include "screencast/utils/clock.h"
#include <cassert>
#include <cstdint>
int main () {
assert ( sc::rtp_timestamp_from_ns ( 0 ) == 0u );
assert ( sc::rtp_timestamp_from_ns ( 1'000'000'000ULL ) == 90'000u );
assert ( sc::rtp_timestamp_from_ns ( 500'000'000ULL ) == 45'000u );
assert ( sc::rtp_timestamp_from_ns ( 16'666'667ULL ) == 1'500u );
// A multiple of the wrap period should wrap back to zero.
// Period in ns = ceil(2^32 * 100_000 / 9) because the helper uses
// integer floor division (ns * 9 / 100_000).
constexpr uint64_t period_ns = ( ( 1ULL << 32 ) * 100'000ULL + 9ULL - 1ULL ) / 9ULL;
static_assert ( sc::rtp_timestamp_from_ns ( period_ns ) == 0u );
static_assert ( sc::rtp_timestamp_from_ns ( period_ns + 1'000'000'000ULL ) == 90'000u );
return 0;
}