Skip to content

Commit 6248abe

Browse files
authored
Fix: rounding error in st10_tai_to_media_clk (#1342)
For 59.94 fps RTP timestamp increment should alternate between 1501, 1502 for two consecutive frames. Before, the same increment value was sometimes repeated, this fixes it. Additionally: - noctx integration tests for st40 user pacing was added - added tests for st10_tai_to_media_clk and st10_media_clk_to_ns Fixes: #1325 Signed-off-by: Kasiewicz, Marek <[email protected]>
1 parent 1f0513a commit 6248abe

20 files changed

+1292
-484
lines changed

lib/src/st2110/pipeline/st40_pipeline_rx.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,7 @@ static int rx_st40p_rtp_ready(void* priv) {
8888

8989
struct rte_mbuf* pkt = mbuf;
9090
enum mtl_port port = mt_port_by_id(ctx->impl, pkt->port);
91-
uint64_t receive_timestamp = 0;
92-
if (port < MTL_PORT_MAX)
93-
receive_timestamp = mt_mbuf_time_stamp(ctx->impl, pkt, port);
94-
else
95-
receive_timestamp = mtl_ptp_read_time(ctx->impl);
91+
uint64_t receive_timestamp = mt_mbuf_time_stamp(ctx->impl, pkt, port);
9692

9793
uint32_t hdr_bytes = sizeof(struct st40_rfc8331_rtp_hdr);
9894
if (len < hdr_bytes) {

lib/src/st2110/st_fmt.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -906,16 +906,35 @@ bool st_frame_fmt_equal_transport(enum st_frame_fmt fmt, enum st20_fmt tfmt) {
906906
return (fmt == to_fmt) ? true : false;
907907
}
908908

909+
static uint64_t st_muldiv_u64_round_closest(uint64_t value, uint64_t multiplier,
910+
uint64_t divisor) {
911+
/* keep conversions reproducible without relying on floating point */
912+
__uint128_t product = (__uint128_t)value * multiplier;
913+
__uint128_t quotient = product / divisor;
914+
__uint128_t remainder = product - quotient * divisor;
915+
__uint128_t half = divisor / 2;
916+
917+
if (remainder > half) quotient++; /* ties round down to keep jitter bounded */
918+
919+
return (uint64_t)quotient;
920+
}
921+
909922
uint32_t st10_tai_to_media_clk(uint64_t tai_ns, uint32_t sampling_rate) {
910-
long double ts = (long double)tai_ns * sampling_rate / NS_PER_S;
911-
uint64_t tmstamp64 = ts;
912-
uint32_t tmstamp32 = tmstamp64;
913-
return tmstamp32;
923+
if (!sampling_rate) {
924+
err("%s, invalid sampling rate\n", __func__);
925+
return 0;
926+
}
927+
928+
return (uint32_t)st_muldiv_u64_round_closest(tai_ns, sampling_rate, NS_PER_S);
914929
}
915930

916931
uint64_t st10_media_clk_to_ns(uint32_t media_ts, uint32_t sampling_rate) {
917-
long double ts = (long double)media_ts * NS_PER_S / sampling_rate;
918-
return ts;
932+
if (!sampling_rate) {
933+
err("%s, invalid sampling rate\n", __func__);
934+
return 0;
935+
}
936+
937+
return st_muldiv_u64_round_closest(media_ts, NS_PER_S, sampling_rate);
919938
}
920939

921940
int st_draw_logo(struct st_frame* frame, struct st_frame* logo, uint32_t x, uint32_t y) {

tests/integration_tests/noctx/core/test_fixture.cpp

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,37 @@
1515

1616
#include "handlers/st20p_handler.hpp"
1717
#include "handlers/st30p_handler.hpp"
18+
#include "handlers/st40p_handler.hpp"
1819
#include "session.hpp"
1920
#include "strategy.hpp"
2021

22+
namespace {
23+
24+
struct TestPtpClockState {
25+
std::atomic<uint64_t> start_ns{0};
26+
std::atomic<bool> running{false};
27+
};
28+
29+
TestPtpClockState g_test_ptp_clock;
30+
31+
uint64_t monotonicNowNs() {
32+
struct timespec spec;
33+
clock_gettime(CLOCK_MONOTONIC, &spec);
34+
return (uint64_t)spec.tv_sec * NS_PER_S + spec.tv_nsec;
35+
}
36+
37+
void startClock(uint64_t now) {
38+
g_test_ptp_clock.start_ns.store(now, std::memory_order_release);
39+
g_test_ptp_clock.running.store(true, std::memory_order_release);
40+
}
41+
42+
void resetClock() {
43+
g_test_ptp_clock.running.store(false, std::memory_order_release);
44+
g_test_ptp_clock.start_ns.store(0, std::memory_order_release);
45+
}
46+
47+
} /* namespace */
48+
2149
void NoCtxTest::SetUp() {
2250
ctx = new st_tests_context;
2351
if (!ctx) {
@@ -42,10 +70,12 @@ void NoCtxTest::SetUp() {
4270
ctx->para.tx_queues_cnt[MTL_PORT_R] = 16;
4371
ctx->para.rx_queues_cnt[MTL_PORT_P] = 16;
4472
ctx->para.rx_queues_cnt[MTL_PORT_R] = 16;
73+
ResetFakePtpClock();
4574
defaultTestDuration = 20;
4675
}
4776

4877
void NoCtxTest::TearDown() {
78+
st40pHandlers.clear();
4979
st30pHandlers.clear();
5080
st20pHandlers.clear();
5181
frameTestStrategies.clear();
@@ -61,34 +91,37 @@ void NoCtxTest::TearDown() {
6191
}
6292
}
6393

64-
uint64_t NoCtxTest::TestPtpSourceSinceEpoch(void* priv) {
65-
struct timespec spec;
66-
static std::atomic<uint64_t> adjustment_ns{0};
67-
68-
if (adjustment_ns.load() == 0 || priv == nullptr) {
69-
struct timespec spec_adjustment_to_epoch;
70-
clock_gettime(CLOCK_MONOTONIC, &spec_adjustment_to_epoch);
71-
uint64_t temp_adjustment = (uint64_t)spec_adjustment_to_epoch.tv_sec * NS_PER_S +
72-
spec_adjustment_to_epoch.tv_nsec;
94+
uint64_t NoCtxTest::FakePtpClockNow(void* priv) {
95+
(void)priv;
96+
if (!g_test_ptp_clock.running.load(std::memory_order_acquire)) {
97+
StartFakePtpClock();
98+
}
7399

74-
adjustment_ns.store(temp_adjustment);
100+
const uint64_t start = g_test_ptp_clock.start_ns.load(std::memory_order_acquire);
101+
const uint64_t now = monotonicNowNs();
102+
if (now <= start) {
103+
return 0;
75104
}
76105

77-
clock_gettime(CLOCK_MONOTONIC, &spec);
78-
uint64_t result =
79-
((uint64_t)spec.tv_sec * NS_PER_S + spec.tv_nsec) - adjustment_ns.load();
106+
return now - start;
107+
}
108+
109+
void NoCtxTest::StartFakePtpClock() {
110+
startClock(monotonicNowNs());
111+
}
80112

81-
return result;
113+
void NoCtxTest::ResetFakePtpClock() {
114+
resetClock();
82115
}
83116

84117
void NoCtxTest::sleepUntilFailure(int sleep_duration) {
85118
if (!sleep_duration) {
86119
sleep_duration = defaultTestDuration;
87120
}
88121

89-
for (int i = 0; i < sleep_duration; ++i) {
122+
for (int i = 0; i < sleep_duration * 10; ++i) {
90123
if (HasFailure()) break;
91-
sleep(1);
124+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
92125
}
93126
}
94127

@@ -106,6 +139,8 @@ NoCtxTest::St20pHandlerBundle NoCtxTest::createSt20pHandlerBundle(
106139
configure(handler);
107140
}
108141

142+
handler->normalizeSessionOps();
143+
109144
std::unique_ptr<FrameTestStrategy> strategyOwned;
110145
FrameTestStrategy* strategy = nullptr;
111146
if (strategyFactory) {
@@ -153,6 +188,8 @@ NoCtxTest::St30pHandlerBundle NoCtxTest::createSt30pHandlerBundle(
153188
configure(handler);
154189
}
155190

191+
handler->normalizeSessionOps();
192+
156193
std::unique_ptr<FrameTestStrategy> strategyOwned;
157194
FrameTestStrategy* strategy = nullptr;
158195
if (strategyFactory) {
@@ -185,12 +222,58 @@ NoCtxTest::St30pHandlerBundle NoCtxTest::registerSt30pResources(
185222
return bundle;
186223
}
187224

188-
void NoCtxTest::initSt20pDefaultContext() {
225+
NoCtxTest::St40pHandlerBundle NoCtxTest::createSt40pHandlerBundle(
226+
bool createTx, bool createRx,
227+
std::function<FrameTestStrategy*(St40pHandler*)> strategyFactory,
228+
std::function<void(St40pHandler*)> configure) {
229+
if (!ctx) {
230+
throw std::runtime_error("createSt40pHandlerBundle expects initialized ctx");
231+
}
232+
233+
auto handlerOwned = std::make_unique<St40pHandler>(ctx);
234+
auto* handler = handlerOwned.get();
235+
if (configure) {
236+
configure(handler);
237+
}
238+
239+
std::unique_ptr<FrameTestStrategy> strategyOwned;
240+
FrameTestStrategy* strategy = nullptr;
241+
if (strategyFactory) {
242+
strategyOwned.reset(strategyFactory(handler));
243+
strategy = strategyOwned.get();
244+
handler->setFrameTestStrategy(strategy);
245+
}
246+
247+
if (createRx) {
248+
handler->createSessionRx();
249+
}
250+
if (createTx) {
251+
handler->createSessionTx();
252+
}
253+
254+
return registerSt40pResources(std::move(handlerOwned), std::move(strategyOwned));
255+
}
256+
257+
NoCtxTest::St40pHandlerBundle NoCtxTest::registerSt40pResources(
258+
std::unique_ptr<St40pHandler> handler, std::unique_ptr<FrameTestStrategy> strategy) {
259+
St40pHandlerBundle bundle;
260+
if (handler) {
261+
bundle.handler = handler.get();
262+
st40pHandlers.emplace_back(std::move(handler));
263+
}
264+
if (strategy) {
265+
bundle.strategy = strategy.get();
266+
frameTestStrategies.emplace_back(std::move(strategy));
267+
}
268+
return bundle;
269+
}
270+
271+
void NoCtxTest::initDefaultContext() {
189272
if (!ctx) {
190-
throw std::runtime_error("initSt20pDefaultContext expects initialized ctx");
273+
throw std::runtime_error("initDefaultContext expects initialized ctx");
191274
}
192275

193-
ctx->para.ptp_get_time_fn = NoCtxTest::TestPtpSourceSinceEpoch;
276+
ctx->para.ptp_get_time_fn = NoCtxTest::FakePtpClockNow;
194277
ctx->para.log_level = MTL_LOG_LEVEL_INFO;
195278
ctx->para.flags &= ~MTL_FLAG_DEV_AUTO_START_STOP;
196279
ctx->handle = mtl_init(&ctx->para);

tests/integration_tests/noctx/core/test_fixture.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "handlers/st20p_handler.hpp"
1515
#include "handlers/st30p_handler.hpp"
16+
#include "handlers/st40p_handler.hpp"
1617
#include "tests.hpp"
1718

1819
class Session;
@@ -35,10 +36,13 @@ class NoCtxTest : public ::testing::Test {
3536

3637
using St20pHandlerBundle = HandlerBundle<St20pHandler>;
3738
using St30pHandlerBundle = HandlerBundle<St30pHandler>;
39+
using St40pHandlerBundle = HandlerBundle<St40pHandler>;
3840

3941
uint defaultTestDuration = 0;
4042

41-
static uint64_t TestPtpSourceSinceEpoch(void* priv);
43+
static uint64_t FakePtpClockNow(void* priv);
44+
static void StartFakePtpClock();
45+
static void ResetFakePtpClock();
4246

4347
void sleepUntilFailure(int sleepDuration = 0);
4448
St20pHandlerBundle createSt20pHandlerBundle(
@@ -53,11 +57,18 @@ class NoCtxTest : public ::testing::Test {
5357
std::function<void(St30pHandler*)> configure = nullptr);
5458
St30pHandlerBundle registerSt30pResources(std::unique_ptr<St30pHandler> handler,
5559
std::unique_ptr<FrameTestStrategy> strategy);
56-
void initSt20pDefaultContext();
60+
St40pHandlerBundle createSt40pHandlerBundle(
61+
bool createTx, bool createRx,
62+
std::function<FrameTestStrategy*(St40pHandler*)> strategyFactory,
63+
std::function<void(St40pHandler*)> configure = nullptr);
64+
St40pHandlerBundle registerSt40pResources(std::unique_ptr<St40pHandler> handler,
65+
std::unique_ptr<FrameTestStrategy> strategy);
66+
void initDefaultContext();
5767
bool waitForSession(Session& session,
5868
std::chrono::milliseconds timeout =
5969
std::chrono::milliseconds(SessionStartTimeoutMs));
6070

71+
std::vector<std::unique_ptr<St40pHandler>> st40pHandlers;
6172
std::vector<std::unique_ptr<St30pHandler>> st30pHandlers;
6273
std::vector<std::unique_ptr<St20pHandler>> st20pHandlers;
6374
std::vector<std::unique_ptr<FrameTestStrategy>> frameTestStrategies;

0 commit comments

Comments
 (0)