From 7a2bf2eaa1d24c170e67f6ceefbcc0d9248d1f7c Mon Sep 17 00:00:00 2001 From: GCRx Date: Tue, 14 Jul 2026 11:24:07 +0530 Subject: [PATCH] Add PTP hardware timing support to MTL and FFmpeg TX paths - Wire MTL_FLAG_PTP_ENABLE/PI/UNICAST_ADDR into both the direct MTL TX path (src/mtl/mtl_tx.c) and the FFmpeg mtl_st20p muxer path (src/ffmpeg/ffmpeg_tx.c), with a sync-notify callback logging master_utc_offset/delta. - app_context.h: add ptp_enable/ptp_pi/ptp_unicast fields. - config_reader.c/.h: hardcode PTP defaults (enable=true, pi=true, unicast=false) instead of reading them from JSON. Verified on hardware (Intel I225-V/igc) with both enable_mtl_tx=true and enable_mtl_tx=false builds: PTP enables cleanly (tx pacing ptp, ptp_init pi controller) with no grandmaster present on the test network (sync cnt 0, as expected). --- include/app_context.h | 7 +++++++ include/util/config_reader.h | 6 ++++++ src/ffmpeg/ffmpeg_tx.c | 12 ++++++++++++ src/mtl/mtl_tx.c | 19 +++++++++++++++++++ src/util/config_reader.c | 13 +++++++++++++ 5 files changed, 57 insertions(+) diff --git a/include/app_context.h b/include/app_context.h index 78fcbc3..6dac693 100644 --- a/include/app_context.h +++ b/include/app_context.h @@ -61,6 +61,13 @@ struct dvledtx_context { bool force_dhcp; int test_time_s; + /* PTP hardware timing (built-in MTL PTP client, FFmpeg mtl_st20p muxer path). + * See MTL_FLAG_PTP_* in mtl_api.h. Only used when ENABLE_MTL_TX is NOT set + * (the FFmpeg avdevice TX path); passed through as AVOptions in ffmpeg_tx.c. */ + bool ptp_enable; /* enable built-in PTP client + PTP-paced TX */ + bool ptp_pi; /* use PI controller for built-in PTP (PF only) */ + bool ptp_unicast; /* use unicast address for PTP_DELAY_REQ message */ + /* Per-session network + crop config — dynamically allocated, st20p_sessions elements */ struct tx_session_net* session_net; diff --git a/include/util/config_reader.h b/include/util/config_reader.h index d0c0d08..0b2e6b4 100644 --- a/include/util/config_reader.h +++ b/include/util/config_reader.h @@ -42,6 +42,12 @@ struct dvledtx_config { /* optional log file path (empty = console only) */ char log_file[256]; + /* PTP hardware timing (built-in MTL PTP client) — hardcoded in config_reader.c, + * not configurable via JSON. */ + int ptp_enable; + int ptp_pi; + int ptp_unicast; + /* tx_sessions array — dynamically allocated */ int session_count; int session_cap; /* allocated capacity */ diff --git a/src/ffmpeg/ffmpeg_tx.c b/src/ffmpeg/ffmpeg_tx.c index bb1b8f0..e661d41 100644 --- a/src/ffmpeg/ffmpeg_tx.c +++ b/src/ffmpeg/ffmpeg_tx.c @@ -99,6 +99,18 @@ int open_ffmpeg_tx(struct st20p_tx_ctx* ctx) { ret = av_opt_set_int(ctx->out_fmt_ctx->priv_data, "payload_type", (int64_t)payload_type, 0); if (ret < 0) LOG_WARN("ST20P TX(%d): av_opt_set_int payload_type failed (ret=%d)", ctx->idx, ret); + /* PTP hardware timing (built-in MTL PTP client). Only takes effect on the + * session that actually calls mtl_init() (mtl_dev_get()'s singleton shared + * handle) — harmless to set on every session. */ + if (ctx->app->ptp_enable) { + ret = av_opt_set_int(ctx->out_fmt_ctx->priv_data, "ptp_enable", 1, 0); + if (ret < 0) LOG_WARN("ST20P TX(%d): av_opt_set_int ptp_enable failed (ret=%d)", ctx->idx, ret); + ret = av_opt_set_int(ctx->out_fmt_ctx->priv_data, "ptp_pi", ctx->app->ptp_pi ? 1 : 0, 0); + if (ret < 0) LOG_WARN("ST20P TX(%d): av_opt_set_int ptp_pi failed (ret=%d)", ctx->idx, ret); + ret = av_opt_set_int(ctx->out_fmt_ctx->priv_data, "ptp_unicast", ctx->app->ptp_unicast ? 1 : 0, 0); + if (ret < 0) LOG_WARN("ST20P TX(%d): av_opt_set_int ptp_unicast failed (ret=%d)", ctx->idx, ret); + } + /* Multi-NIC: The FFmpeg mtl_st20p plugin's mtl_dev_get() uses a singleton * shared MTL handle — the FIRST avformat_write_header() call creates the * MTL instance via mtl_init(), which initialises DPDK EAL. EAL cannot be diff --git a/src/mtl/mtl_tx.c b/src/mtl/mtl_tx.c index a39d74d..1ca09b5 100644 --- a/src/mtl/mtl_tx.c +++ b/src/mtl/mtl_tx.c @@ -30,6 +30,7 @@ #include "app_context.h" #include "core/session_manager.h" #include "util/logger.h" +#include #include #include #include @@ -198,6 +199,15 @@ void mtl_copy_crop_to_frame(struct st_frame* dst, const AVFrame* src, * * mtl_tx_uninit() — release the MTL library instance. */ +/* PTP hardware timing (built-in MTL PTP client, direct MTL TX path). + * See MTL_FLAG_PTP_* in mtl_api.h. Mirrors the AVOption-driven callback + * wired for the FFmpeg muxer path in the external ffmpeg_plugin repo. */ +static void mtl_tx_ptp_sync_notify_cb(void* priv, struct mtl_ptp_sync_notify_meta* meta) { + (void)priv; + LOG_INFO("PTP sync: master_utc_offset=%d delta=%" PRId64 "ns", + meta->master_utc_offset, meta->delta); +} + int mtl_tx_init(session_manager_t* manager, struct dvledtx_context* app) { struct mtl_init_params mtl_params; memset(&mtl_params, 0, sizeof(mtl_params)); @@ -205,6 +215,15 @@ int mtl_tx_init(session_manager_t* manager, struct dvledtx_context* app) { mtl_params.flags = MTL_FLAG_BIND_NUMA | MTL_FLAG_DEV_AUTO_START_STOP; mtl_params.num_ports = app->nic_count; + if (app->ptp_enable) { + mtl_params.flags |= MTL_FLAG_PTP_ENABLE; + mtl_params.pacing = ST21_TX_PACING_WAY_PTP; + if (app->ptp_pi) mtl_params.flags |= MTL_FLAG_PTP_PI; + if (app->ptp_unicast) mtl_params.flags |= MTL_FLAG_PTP_UNICAST_ADDR; + mtl_params.ptp_sync_notify = mtl_tx_ptp_sync_notify_cb; + LOG_INFO("MTL init: PTP enabled (pi=%d unicast=%d)", app->ptp_pi, app->ptp_unicast); + } + /* Count sessions assigned to each NIC for queue allocation */ int* sessions_per_nic = calloc((size_t)app->nic_count, sizeof(int)); if (sessions_per_nic == NULL) { diff --git a/src/util/config_reader.c b/src/util/config_reader.c index f480ea8..98a0833 100644 --- a/src/util/config_reader.c +++ b/src/util/config_reader.c @@ -328,6 +328,11 @@ int parse_tx_config(const char* config_file, struct dvledtx_config* config) { /* --- optional top-level log_file --- */ extract_json_string(json, buf_end, "log_file", config->log_file, sizeof(config->log_file)); + /* PTP hardware timing (built-in MTL PTP client) — hardcoded, not configurable via JSON. */ + config->ptp_enable = true; + config->ptp_pi = true; + config->ptp_unicast = false; + /* --- tx_sessions array --- */ const char* sessions_arr = find_array(json, buf_end, "tx_sessions"); if (sessions_arr == NULL) { @@ -798,6 +803,14 @@ int load_and_apply_config(struct dvledtx_context* app, const char* config_file) app->log_file[sizeof(app->log_file) - 1] = '\0'; } + /* PTP hardware timing (built-in MTL PTP client) */ + app->ptp_enable = config.ptp_enable; + app->ptp_pi = config.ptp_pi; + app->ptp_unicast = config.ptp_unicast; + if (app->ptp_enable) + LOG_INFO("PTP enabled: pi_controller=%d unicast_delay_req=%d", + app->ptp_pi, app->ptp_unicast); + LOG_INFO("Config loaded: %s (%d NIC(s), %d session(s))", config_file, config.nic_count, config.session_count); for (int ni = 0; ni < config.nic_count; ni++)