From fd6c34ae2f301ce7ed7c870a9f41a43054c2f572 Mon Sep 17 00:00:00 2001 From: Frank Du Date: Wed, 10 Jan 2024 13:08:36 +0800 Subject: [PATCH 1/3] rx/timing_parser: add -7 support test with: ./build/app/RxVideoTimingParserSample --p_port 0000:af:01.0 --r_port 0000:af:01.1 Signed-off-by: Frank Du --- app/sample/rx_st20_pipeline_sample.c | 9 +- app/sample/rx_st20p_timing_parser_sample.c | 43 ++++++-- app/sample/tx_st22_pipeline_sample.c | 9 +- doc/design.md | 2 +- include/st20_api.h | 2 +- include/st_pipeline_api.h | 2 +- lib/src/st2110/pipeline/st20_pipeline_rx.c | 17 ++-- lib/src/st2110/pipeline/st20_pipeline_rx.h | 2 +- lib/src/st2110/st_header.h | 6 +- lib/src/st2110/st_rx_timing_parser.c | 112 +++++++++++---------- lib/src/st2110/st_rx_timing_parser.h | 7 +- lib/src/st2110/st_rx_video_session.c | 45 +++++---- 12 files changed, 156 insertions(+), 100 deletions(-) diff --git a/app/sample/rx_st20_pipeline_sample.c b/app/sample/rx_st20_pipeline_sample.c index 3588304c3..a4c712f64 100644 --- a/app/sample/rx_st20_pipeline_sample.c +++ b/app/sample/rx_st20_pipeline_sample.c @@ -146,12 +146,19 @@ int main(int argc, char** argv) { memset(&ops_rx, 0, sizeof(ops_rx)); ops_rx.name = "st20p_test"; ops_rx.priv = app[i]; // app handle register to lib - ops_rx.port.num_port = 1; + ops_rx.port.num_port = ctx.param.num_ports; memcpy(ops_rx.port.sip_addr[MTL_SESSION_PORT_P], ctx.rx_sip_addr[MTL_PORT_P], MTL_IP_ADDR_LEN); snprintf(ops_rx.port.port[MTL_SESSION_PORT_P], MTL_PORT_MAX_LEN, "%s", ctx.param.port[MTL_PORT_P]); ops_rx.port.udp_port[MTL_SESSION_PORT_P] = ctx.udp_port + i * 2; + if (ops_rx.port.num_port > 1) { + memcpy(ops_rx.port.sip_addr[MTL_SESSION_PORT_R], ctx.rx_sip_addr[MTL_PORT_R], + MTL_IP_ADDR_LEN); + snprintf(ops_rx.port.port[MTL_SESSION_PORT_R], MTL_PORT_MAX_LEN, "%s", + ctx.param.port[MTL_PORT_R]); + ops_rx.port.udp_port[MTL_SESSION_PORT_R] = ctx.udp_port + i * 2; + } ops_rx.port.payload_type = ctx.payload_type; ops_rx.width = ctx.width; ops_rx.height = ctx.height; diff --git a/app/sample/rx_st20p_timing_parser_sample.c b/app/sample/rx_st20p_timing_parser_sample.c index 128e572fe..ea8b4e3d1 100644 --- a/app/sample/rx_st20p_timing_parser_sample.c +++ b/app/sample/rx_st20p_timing_parser_sample.c @@ -39,7 +39,8 @@ struct rx_timing_parser_sample_ctx { bool pass_get; /* pass critical is only available */ /* stat report */ - struct rx_tp_stat stat; + struct rx_tp_stat stat[MTL_SESSION_PORT_MAX]; + uint8_t num_port; }; static void rx_st20p_tp_stat_init(struct rx_tp_stat* stat) { @@ -62,9 +63,9 @@ static void rx_st20p_tp_stat_init(struct rx_tp_stat* stat) { } static void rx_st20p_tp_stat_print(struct rx_timing_parser_sample_ctx* s, - struct rx_tp_stat* stat) { + enum mtl_session_port port, struct rx_tp_stat* stat) { int idx = s->idx; - info("%s(%d), COMPLIANT NARROW %d WIDE %d FAILED %d!\n", __func__, idx, + info("%s(%d,%d), COMPLIANT NARROW %d WIDE %d FAILED %d!\n", __func__, idx, port, stat->compliant_result[ST_RX_TP_COMPLIANT_NARROW], stat->compliant_result[ST_RX_TP_COMPLIANT_WIDE], stat->compliant_result[ST_RX_TP_COMPLIANT_FAILED]); @@ -81,14 +82,14 @@ static void rx_st20p_tp_stat_print(struct rx_timing_parser_sample_ctx* s, } static int rx_st20p_tp_consume(struct rx_timing_parser_sample_ctx* s, - struct st20_rx_tp_meta* tp) { + enum mtl_session_port port, struct st20_rx_tp_meta* tp) { if (tp->compliant != ST_RX_TP_COMPLIANT_NARROW) { dbg("%s(%d), compliant failed %d cause: %s, frame idx %d\n", __func__, s->idx, tp->compliant, tp->failed_cause, s->fb_recv); } /* update stat */ - struct rx_tp_stat* stat = &s->stat; + struct rx_tp_stat* stat = &s->stat[port]; stat->vrx_min = ST_MIN(tp->vrx_min, stat->vrx_min); stat->vrx_max = ST_MAX(tp->vrx_max, stat->vrx_max); stat->cinst_min = ST_MIN(tp->cinst_min, stat->cinst_min); @@ -132,7 +133,9 @@ static void* rx_st20p_tp_thread(void* arg) { s->pass.vrx_max_narrow, s->pass.vrx_max_wide); } } - rx_st20p_tp_consume(s, frame->tp); + rx_st20p_tp_consume(s, MTL_SESSION_PORT_P, frame->tp[MTL_SESSION_PORT_P]); + if (s->num_port > 1) + rx_st20p_tp_consume(s, MTL_SESSION_PORT_R, frame->tp[MTL_SESSION_PORT_R]); s->fb_recv++; st20p_rx_put_frame(handle, frame); @@ -151,6 +154,9 @@ int main(int argc, char** argv) { ret = rx_sample_parse_args(&ctx, argc, argv); if (ret < 0) return ret; + /* enable hw offload timestamp */ + ctx.param.flags |= MTL_FLAG_ENABLE_HW_TIMESTAMP; + ctx.st = mtl_init(&ctx.param); if (!ctx.st) { err("%s: mtl_init fail\n", __func__); @@ -172,18 +178,28 @@ int main(int argc, char** argv) { app[i]->idx = i; app[i]->stop = false; app[i]->fb_cnt = ctx.framebuff_cnt; - rx_st20p_tp_stat_init(&app[i]->stat); + app[i]->num_port = ctx.param.num_ports; + + rx_st20p_tp_stat_init(&app[i]->stat[MTL_SESSION_PORT_P]); + rx_st20p_tp_stat_init(&app[i]->stat[MTL_SESSION_PORT_R]); struct st20p_rx_ops ops_rx; memset(&ops_rx, 0, sizeof(ops_rx)); ops_rx.name = "st20p_test"; ops_rx.priv = app[i]; // app handle register to lib - ops_rx.port.num_port = 1; + ops_rx.port.num_port = app[i]->num_port; memcpy(ops_rx.port.sip_addr[MTL_SESSION_PORT_P], ctx.rx_sip_addr[MTL_PORT_P], MTL_IP_ADDR_LEN); snprintf(ops_rx.port.port[MTL_SESSION_PORT_P], MTL_PORT_MAX_LEN, "%s", ctx.param.port[MTL_PORT_P]); ops_rx.port.udp_port[MTL_SESSION_PORT_P] = ctx.udp_port + i * 2; + if (ops_rx.port.num_port > 1) { + memcpy(ops_rx.port.sip_addr[MTL_SESSION_PORT_R], ctx.rx_sip_addr[MTL_PORT_R], + MTL_IP_ADDR_LEN); + snprintf(ops_rx.port.port[MTL_SESSION_PORT_R], MTL_PORT_MAX_LEN, "%s", + ctx.param.port[MTL_PORT_R]); + ops_rx.port.udp_port[MTL_SESSION_PORT_R] = ctx.udp_port + i * 2; + } ops_rx.port.payload_type = ctx.payload_type; ops_rx.width = ctx.width; ops_rx.height = ctx.height; @@ -221,8 +237,14 @@ int main(int argc, char** argv) { cnt++; if ((cnt % 10) == 0) { for (int i = 0; i < session_num; i++) { - rx_st20p_tp_stat_print(app[i], &app[i]->stat); - rx_st20p_tp_stat_init(&app[i]->stat); + rx_st20p_tp_stat_print(app[i], MTL_SESSION_PORT_P, + &app[i]->stat[MTL_SESSION_PORT_P]); + rx_st20p_tp_stat_init(&app[i]->stat[MTL_SESSION_PORT_P]); + if (app[i]->num_port > 1) { + rx_st20p_tp_stat_print(app[i], MTL_SESSION_PORT_R, + &app[i]->stat[MTL_SESSION_PORT_R]); + rx_st20p_tp_stat_init(&app[i]->stat[MTL_SESSION_PORT_R]); + } } } } @@ -230,6 +252,7 @@ int main(int argc, char** argv) { // stop app thread for (int i = 0; i < session_num; i++) { app[i]->stop = true; + if (app[i]->handle) st20p_rx_wake_block(app[i]->handle); pthread_join(app[i]->frame_thread, NULL); info("%s(%d), received frames %d\n", __func__, i, app[i]->fb_recv); } diff --git a/app/sample/tx_st22_pipeline_sample.c b/app/sample/tx_st22_pipeline_sample.c index d42049205..60bac5311 100644 --- a/app/sample/tx_st22_pipeline_sample.c +++ b/app/sample/tx_st22_pipeline_sample.c @@ -203,12 +203,19 @@ int main(int argc, char** argv) { memset(&ops_tx, 0, sizeof(ops_tx)); ops_tx.name = "st22p_sample"; ops_tx.priv = app[i]; // app handle register to lib - ops_tx.port.num_port = 1; + ops_tx.port.num_port = ctx.param.num_ports; memcpy(ops_tx.port.dip_addr[MTL_SESSION_PORT_P], ctx.tx_dip_addr[MTL_PORT_P], MTL_IP_ADDR_LEN); snprintf(ops_tx.port.port[MTL_SESSION_PORT_P], MTL_PORT_MAX_LEN, "%s", ctx.param.port[MTL_PORT_P]); ops_tx.port.udp_port[MTL_SESSION_PORT_P] = ctx.udp_port + i * 2; + if (ops_tx.port.num_port > 1) { + memcpy(ops_tx.port.dip_addr[MTL_SESSION_PORT_R], ctx.tx_dip_addr[MTL_PORT_R], + MTL_IP_ADDR_LEN); + snprintf(ops_tx.port.port[MTL_SESSION_PORT_R], MTL_PORT_MAX_LEN, "%s", + ctx.param.port[MTL_PORT_R]); + ops_tx.port.udp_port[MTL_SESSION_PORT_R] = ctx.udp_port + i * 2; + } ops_tx.port.payload_type = ctx.payload_type; ops_tx.width = ctx.width; ops_tx.height = ctx.height; diff --git a/doc/design.md b/doc/design.md index 032b1f032..9f78bec35 100644 --- a/doc/design.md +++ b/doc/design.md @@ -478,7 +478,7 @@ In addition to the built-in RxTxApp, IMTL also provides numerous sample codes th ### 6.16 RX Timing Parser -To verify the compliance of incoming ST2110-20 RX streams with the ST2110 standard, IMTL provides several utilities for analysis. To support this functionality, if the Network Interface Card (NIC) supports the hardware time synchronization feature, IMTL will read the RX timestamp directly from the NIC's hardware to obtain accurate timing. +To verify the compliance of incoming ST2110-20 RX streams with the ST2110 standard, IMTL provides several utilities for analysis. To support this functionality, if the Network Interface Card (NIC) supports the hardware time synchronization feature, IMTL will read the RX timestamp directly from the NIC's hardware to obtain accurate timing. And please set `MTL_FLAG_ENABLE_HW_TIMESTAMP` flag to enable HW offload timestamp for all RX packets. The fallback method is to read the time when IMTL processes the packet. However, it's important to note that this fallback method cannot guarantee timing accuracy, potentially rendering the parsed results unreliable. The simplest method is to enable the built-in status report. An application can activate the feature by setting the flag `ST20_RX_FLAG_TIMING_PARSER_STAT` or `ST20P_RX_FLAG_TIMING_PARSER_STAT`. Subsequently, IMTL will engage the Timing Parser module and include the results in the status log. diff --git a/include/st20_api.h b/include/st20_api.h index d5f963d1c..626dc06e1 100644 --- a/include/st20_api.h +++ b/include/st20_api.h @@ -522,7 +522,7 @@ struct st20_rx_frame_meta { * 'pkts_total,' which serves as an indicator of signal quality. */ uint32_t pkts_recv[MTL_SESSION_PORT_MAX]; /** st20 rx timing parser meta, only active if ST20_RX_FLAG_TIMING_PARSER_META */ - struct st20_rx_tp_meta* tp; + struct st20_rx_tp_meta* tp[MTL_SESSION_PORT_MAX]; }; /** diff --git a/include/st_pipeline_api.h b/include/st_pipeline_api.h index cd7a5029e..779c00a0b 100644 --- a/include/st_pipeline_api.h +++ b/include/st_pipeline_api.h @@ -296,7 +296,7 @@ struct st_frame { void* opaque; /** timing parser meta for st20p_rx_get_frame, only active if * ST20P_RX_FLAG_TIMING_PARSER_META */ - struct st20_rx_tp_meta* tp; + struct st20_rx_tp_meta* tp[MTL_SESSION_PORT_MAX]; }; /** Device type of st plugin */ diff --git a/lib/src/st2110/pipeline/st20_pipeline_rx.c b/lib/src/st2110/pipeline/st20_pipeline_rx.c index b942c9dc2..94279dbbb 100644 --- a/lib/src/st2110/pipeline/st20_pipeline_rx.c +++ b/lib/src/st2110/pipeline/st20_pipeline_rx.c @@ -204,17 +204,20 @@ static int rx_st20p_frame_ready(void* priv, void* frame, framebuff->src.status = framebuff->dst.status = meta->status; framebuff->src.pkts_total = framebuff->dst.pkts_total = meta->pkts_total; - for (enum mtl_session_port s_port = MTL_SESSION_PORT_P; s_port < MTL_SESSION_PORT_MAX; - s_port++) { + for (enum mtl_session_port s_port = 0; s_port < MTL_SESSION_PORT_MAX; s_port++) { framebuff->src.pkts_recv[s_port] = framebuff->dst.pkts_recv[s_port] = meta->pkts_recv[s_port]; } - if (meta->tp) { - mtl_memcpy(&framebuff->tp, meta->tp, sizeof(framebuff->tp)); - framebuff->src.tp = framebuff->dst.tp = &framebuff->tp; - } else { - framebuff->src.tp = framebuff->dst.tp = NULL; + /* copy timing parser meta */ + for (enum mtl_session_port s_port = 0; s_port < MTL_SESSION_PORT_MAX; s_port++) { + framebuff->src.tp[s_port] = framebuff->dst.tp[s_port] = NULL; + } + + for (enum mtl_session_port s_port = 0; s_port < ctx->ops.port.num_port; s_port++) { + if (!meta->tp[s_port]) continue; + mtl_memcpy(&framebuff->tp[s_port], meta->tp[s_port], sizeof(framebuff->tp[s_port])); + framebuff->src.tp[s_port] = framebuff->dst.tp[s_port] = &framebuff->tp[s_port]; } /* check user meta */ diff --git a/lib/src/st2110/pipeline/st20_pipeline_rx.h b/lib/src/st2110/pipeline/st20_pipeline_rx.h index a252a2b4b..f38953cd4 100644 --- a/lib/src/st2110/pipeline/st20_pipeline_rx.h +++ b/lib/src/st2110/pipeline/st20_pipeline_rx.h @@ -26,7 +26,7 @@ struct st20p_rx_frame { void* user_meta; /* the user meta data */ size_t user_meta_buffer_size; size_t user_meta_data_size; - struct st20_rx_tp_meta tp; + struct st20_rx_tp_meta tp[MTL_SESSION_PORT_MAX]; }; struct st20p_rx_ctx { diff --git a/lib/src/st2110/st_header.h b/lib/src/st2110/st_header.h index 8c841fe9b..a0537856d 100644 --- a/lib/src/st2110/st_header.h +++ b/lib/src/st2110/st_header.h @@ -544,11 +544,11 @@ struct st_rx_video_tp { struct st20_rx_tp_pass pass; /* timing info for each slot */ - struct st_rv_tp_slot slots[ST_VIDEO_RX_REC_NUM_OFO]; - uint32_t pre_rtp_tmstamp; + struct st_rv_tp_slot slots[ST_VIDEO_RX_REC_NUM_OFO][MTL_SESSION_PORT_MAX]; + uint32_t pre_rtp_tmstamp[MTL_SESSION_PORT_MAX]; /* for the status */ - struct st_rv_tp_stat stat; + struct st_rv_tp_stat stat[MTL_SESSION_PORT_MAX]; }; struct st_rx_video_session_impl { diff --git a/lib/src/st2110/st_rx_timing_parser.c b/lib/src/st2110/st_rx_timing_parser.c index e3d6fb768..f99c140a6 100644 --- a/lib/src/st2110/st_rx_timing_parser.c +++ b/lib/src/st2110/st_rx_timing_parser.c @@ -10,8 +10,9 @@ static inline float rv_tp_calculate_avg(uint32_t cnt, int64_t sum) { return cnt ? ((float)sum / cnt) : -1.0f; } -void rv_tp_on_packet(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* slot, - uint32_t rtp_tmstamp, uint64_t pkt_time, int pkt_idx) { +void rv_tp_on_packet(struct st_rx_video_session_impl* s, enum mtl_session_port s_port, + struct st_rv_tp_slot* slot, uint32_t rtp_tmstamp, uint64_t pkt_time, + int pkt_idx) { struct st_rx_video_tp* tp = s->tp; uint64_t epoch_tmstamp; double tvd, packet_delta_ns, trs = tp->trs; @@ -31,10 +32,10 @@ void rv_tp_on_packet(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* s double diff_rtp_ts_ns = diff_rtp_ts * s->frame_time / s->frame_time_sampling; slot->meta.latency = slot->meta.fpt - diff_rtp_ts_ns; slot->meta.rtp_offset = diff_rtp_ts; - if (tp->pre_rtp_tmstamp) { - slot->meta.rtp_ts_delta = rtp_tmstamp - tp->pre_rtp_tmstamp; + if (tp->pre_rtp_tmstamp[s_port]) { + slot->meta.rtp_ts_delta = rtp_tmstamp - tp->pre_rtp_tmstamp[s_port]; } - tp->pre_rtp_tmstamp = rtp_tmstamp; + tp->pre_rtp_tmstamp[s_port] = rtp_tmstamp; } epoch_tmstamp = (uint64_t)(slot->cur_epochs * s->frame_time); @@ -139,7 +140,7 @@ static enum st_rx_tp_compliant rv_tp_compliant(struct st_rx_video_tp* tp, } void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, - struct st_rv_tp_slot* slot) { + enum mtl_session_port s_port, struct st_rv_tp_slot* slot) { struct st_rx_video_tp* tp = s->tp; float cinst_avg = rv_tp_calculate_avg(slot->meta.pkts_cnt, slot->cinst_sum); float vrx_avg = rv_tp_calculate_avg(slot->meta.pkts_cnt, slot->vrx_sum); @@ -162,7 +163,7 @@ void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, if (!s->enable_timing_parser_stat) return; /* update stat */ - struct st_rv_tp_stat* stat = &tp->stat; + struct st_rv_tp_stat* stat = &tp->stat[s_port]; struct st_rv_tp_slot* stat_slot = &stat->slot; stat->stat_compliant_result[compliant]++; @@ -197,19 +198,23 @@ void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, stat->stat_frame_cnt++; } -static void rv_tp_stat_init(struct st_rx_video_tp* tp) { - struct st_rv_tp_stat* stat = &tp->stat; - - memset(stat, 0, sizeof(*stat)); - rv_tp_slot_init(&stat->slot); - stat->stat_fpt_min = INT_MAX; - stat->stat_fpt_max = INT_MIN; - stat->stat_latency_min = INT_MAX; - stat->stat_latency_max = INT_MIN; - stat->stat_rtp_offset_min = INT_MAX; - stat->stat_rtp_offset_max = INT_MIN; - stat->stat_rtp_ts_delta_min = INT_MAX; - stat->stat_rtp_ts_delta_max = INT_MIN; +static void rv_tp_stat_init(struct st_rx_video_session_impl* s, + struct st_rx_video_tp* tp) { + MTL_MAY_UNUSED(s); + for (int s_port = 0; s_port < MTL_SESSION_PORT_MAX; s_port++) { + struct st_rv_tp_stat* stat = &tp->stat[s_port]; + + memset(stat, 0, sizeof(*stat)); + rv_tp_slot_init(&stat->slot); + stat->stat_fpt_min = INT_MAX; + stat->stat_fpt_max = INT_MIN; + stat->stat_latency_min = INT_MAX; + stat->stat_latency_max = INT_MIN; + stat->stat_rtp_offset_min = INT_MAX; + stat->stat_rtp_offset_max = INT_MIN; + stat->stat_rtp_ts_delta_min = INT_MAX; + stat->stat_rtp_ts_delta_max = INT_MIN; + } } void rv_tp_stat(struct st_rx_video_session_impl* s) { @@ -217,37 +222,40 @@ void rv_tp_stat(struct st_rx_video_session_impl* s) { struct st_rx_video_tp* tp = s->tp; if (!tp) return; - struct st_rv_tp_stat* stat = &tp->stat; - struct st_rv_tp_slot* stat_slot = &stat->slot; + for (int s_port = 0; s_port < s->ops.num_port; s_port++) { + struct st_rv_tp_stat* stat = &tp->stat[s_port]; + struct st_rv_tp_slot* stat_slot = &stat->slot; + + info("%s(%d,%d), COMPLIANT NARROW %d WIDE %d FAILED %d!\n", __func__, idx, s_port, + stat->stat_compliant_result[ST_RX_TP_COMPLIANT_NARROW], + stat->stat_compliant_result[ST_RX_TP_COMPLIANT_WIDE], + stat->stat_compliant_result[ST_RX_TP_COMPLIANT_FAILED]); + float cinst_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->cinst_sum); + float vrx_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->vrx_sum); + float ipt_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->ipt_sum); + info("%s(%d), Cinst AVG %.2f MIN %d MAX %d!\n", __func__, idx, cinst_avg, + stat_slot->meta.cinst_min, stat_slot->meta.cinst_max); + info("%s(%d), VRX AVG %.2f MIN %d MAX %d!\n", __func__, idx, vrx_avg, + stat_slot->meta.vrx_min, stat_slot->meta.vrx_max); + info("%s(%d), Inter-packet time(ns) AVG %.2f MIN %d MAX %d!\n", __func__, idx, + ipt_avg, stat_slot->meta.ipt_min, stat_slot->meta.ipt_max); + float fpt_avg = rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_fpt_sum); + info("%s(%d), FPT AVG %.2f MIN %d MAX %d DIFF %d!\n", __func__, idx, fpt_avg, + stat->stat_fpt_min, stat->stat_fpt_max, stat->stat_fpt_max - stat->stat_fpt_min); + float latency_avg = rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_latency_sum); + info("%s(%d), LATENCY AVG %.2f MIN %d MAX %d!\n", __func__, idx, latency_avg, + stat->stat_latency_min, stat->stat_latency_max); + float rtp_offset_avg = + rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_rtp_offset_sum); + info("%s(%d), RTP OFFSET AVG %.2f MIN %d MAX %d!\n", __func__, idx, rtp_offset_avg, + stat->stat_rtp_offset_min, stat->stat_rtp_offset_max); + float rtp_ts_delta_avg = + rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_rtp_ts_delta_sum); + info("%s(%d), RTP TS DELTA AVG %.2f MIN %d MAX %d!\n", __func__, idx, + rtp_ts_delta_avg, stat->stat_rtp_ts_delta_min, stat->stat_rtp_ts_delta_max); + } - info("%s(%d), COMPLIANT NARROW %d WIDE %d FAILED %d!\n", __func__, idx, - stat->stat_compliant_result[ST_RX_TP_COMPLIANT_NARROW], - stat->stat_compliant_result[ST_RX_TP_COMPLIANT_WIDE], - stat->stat_compliant_result[ST_RX_TP_COMPLIANT_FAILED]); - float cinst_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->cinst_sum); - float vrx_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->vrx_sum); - float ipt_avg = rv_tp_calculate_avg(stat_slot->meta.pkts_cnt, stat_slot->ipt_sum); - info("%s(%d), Cinst AVG %.2f MIN %d MAX %d!\n", __func__, idx, cinst_avg, - stat_slot->meta.cinst_min, stat_slot->meta.cinst_max); - info("%s(%d), VRX AVG %.2f MIN %d MAX %d!\n", __func__, idx, vrx_avg, - stat_slot->meta.vrx_min, stat_slot->meta.vrx_max); - info("%s(%d), Inter-packet time(ns) AVG %.2f MIN %d MAX %d!\n", __func__, idx, ipt_avg, - stat_slot->meta.ipt_min, stat_slot->meta.ipt_max); - float fpt_avg = rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_fpt_sum); - info("%s(%d), FPT AVG %.2f MIN %d MAX %d DIFF %d!\n", __func__, idx, fpt_avg, - stat->stat_fpt_min, stat->stat_fpt_max, stat->stat_fpt_max - stat->stat_fpt_min); - float latency_avg = rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_latency_sum); - info("%s(%d), LATENCY AVG %.2f MIN %d MAX %d!\n", __func__, idx, latency_avg, - stat->stat_latency_min, stat->stat_latency_max); - float rtp_offset_avg = - rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_rtp_offset_sum); - info("%s(%d), RTP OFFSET AVG %.2f MIN %d MAX %d!\n", __func__, idx, rtp_offset_avg, - stat->stat_rtp_offset_min, stat->stat_rtp_offset_max); - float rtp_ts_delta_avg = - rv_tp_calculate_avg(stat->stat_frame_cnt, stat->stat_rtp_ts_delta_sum); - info("%s(%d), RTP TS DELTA AVG %.2f MIN %d MAX %d!\n", __func__, idx, rtp_ts_delta_avg, - stat->stat_rtp_ts_delta_min, stat->stat_rtp_ts_delta_max); - rv_tp_stat_init(tp); + rv_tp_stat_init(s, tp); } void rv_tp_slot_init(struct st_rv_tp_slot* slot) { @@ -301,8 +309,6 @@ int rv_tp_init(struct mtl_main_impl* impl, struct st_rx_video_session_impl* s) { } s->tp = tp; - rv_tp_stat_init(tp); - double reactive = 1080.0 / 1125.0; if (ops->interlaced && ops->height <= 576) { reactive = (ops->height == 480) ? 487.0 / 525.0 : 576.0 / 625.0; @@ -337,7 +343,7 @@ int rv_tp_init(struct mtl_main_impl* impl, struct st_rx_video_session_impl* s) { tp->pass.rtp_ts_delta_max = sampling + 1; tp->pass.rtp_ts_delta_min = sampling; - rv_tp_stat_init(tp); + rv_tp_stat_init(s, tp); info("%s[%02d], trs %f tr offset %d sampling %f\n", __func__, idx, tp->trs, tp->pass.tr_offset, s->frame_time_sampling); diff --git a/lib/src/st2110/st_rx_timing_parser.h b/lib/src/st2110/st_rx_timing_parser.h index c8badf389..2d662f03f 100644 --- a/lib/src/st2110/st_rx_timing_parser.h +++ b/lib/src/st2110/st_rx_timing_parser.h @@ -12,11 +12,12 @@ int rv_tp_uinit(struct st_rx_video_session_impl* s); void rv_tp_slot_init(struct st_rv_tp_slot* slot); -void rv_tp_on_packet(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* slot, - uint32_t rtp_tmstamp, uint64_t pkt_time, int pkt_idx); +void rv_tp_on_packet(struct st_rx_video_session_impl* s, enum mtl_session_port s_port, + struct st_rv_tp_slot* slot, uint32_t rtp_tmstamp, uint64_t pkt_time, + int pkt_idx); void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, - struct st_rv_tp_slot* slot); + enum mtl_session_port s_port, struct st_rv_tp_slot* slot); void rv_tp_stat(struct st_rx_video_session_impl* s); diff --git a/lib/src/st2110/st_rx_video_session.c b/lib/src/st2110/st_rx_video_session.c index fc1768072..f9e58da13 100644 --- a/lib/src/st2110/st_rx_video_session.c +++ b/lib/src/st2110/st_rx_video_session.c @@ -713,11 +713,12 @@ static void rv_frame_notify(struct st_rx_video_session_impl* s, struct st20_rx_frame_meta* meta = &slot->meta; if (s->enable_timing_parser) { - struct st_rv_tp_slot* tp_slot = &s->tp->slots[slot->idx]; - rv_tp_slot_parse_result(s, tp_slot); - - if (s->enable_timing_parser_meta) { - meta->tp = &tp_slot->meta; + for (int s_port = 0; s_port < ops->num_port; s_port++) { + struct st_rv_tp_slot* tp_slot = &s->tp->slots[slot->idx][s_port]; + rv_tp_slot_parse_result(s, s_port, tp_slot); + if (s->enable_timing_parser_meta) { + meta->tp[s_port] = &tp_slot->meta; + } } } @@ -1001,7 +1002,12 @@ static struct st_rx_video_slot_impl* rv_slot_by_tmstamp( slot->pkts_recv_per_port[MTL_SESSION_PORT_R] = 0; s->slot_idx = slot_idx; - if (s->enable_timing_parser) rv_tp_slot_init(&s->tp->slots[slot_idx]); + if (s->enable_timing_parser) { + for (int s_port = 0; s_port < s->ops.num_port; s_port++) { + struct st_rv_tp_slot* tp_slot = &s->tp->slots[slot->idx][s_port]; + rv_tp_slot_init(tp_slot); + } + } struct st_frame_trans* frame_info = rv_get_frame(s); if (!frame_info) { @@ -1481,6 +1487,18 @@ static int rv_handle_frame_pkt(struct st_rx_video_session_impl* s, struct rte_mb return -EIO; } + /* if enable_timing_parser */ + if (s->enable_timing_parser) { + struct mtl_main_impl* impl = rv_get_impl(s); + enum mtl_port port = mt_port_logic2phy(s->port_maps, s_port); + + uint64_t pkt_ns = mt_mbuf_time_stamp(impl, mbuf, port); + struct st_rv_tp_slot* tp_slot = &s->tp->slots[slot->idx][s_port]; + dbg("%s(%d,%d), tmstamp %u pkt_ns %" PRIu64 " pkt_idx %d\n", __func__, s->idx, s_port, + tmstamp, pkt_ns, pkt_idx); + rv_tp_on_packet(s, s_port, tp_slot, tmstamp, pkt_ns, pkt_idx); + } + /* check if the same pkt got already */ if (slot->seq_id_got) { if (seq_id_u32 >= slot->seq_id_base_u32) @@ -1536,18 +1554,6 @@ static int rv_handle_frame_pkt(struct st_rx_video_session_impl* s, struct rte_mb bool need_copy = true; struct mtl_dma_lender_dev* dma_dev = s->dma_dev; - /* if enable_timing_parser */ - if (s->enable_timing_parser) { - struct mtl_main_impl* impl = rv_get_impl(s); - enum mtl_port port = mt_port_logic2phy(s->port_maps, s_port); - - uint64_t pkt_ns = mt_mbuf_time_stamp(impl, mbuf, port); - struct st_rv_tp_slot* tp_slot = &s->tp->slots[slot->idx]; - dbg("%s(%d,%d), tmstamp %u pkt_ns %" PRIu64 " pkt_idx %d\n", __func__, s->idx, s_port, - tmstamp, pkt_ns, pkt_idx); - rv_tp_on_packet(s, tp_slot, tmstamp, pkt_ns, pkt_idx); - } - if (s->st20_uframe_size) { /* user frame mode, pass to app to handle the payload */ struct st20_rx_uframe_pg_meta* pg_meta = &s->pg_meta; @@ -2449,6 +2455,9 @@ static int rv_handle_detect_pkt(struct st_rx_video_session_impl* s, struct rte_m uint8_t payload_type = rtp->base.payload_type; MTL_MAY_UNUSED(ctrl_thread); + /* only detect on the main port */ + if (MTL_SESSION_PORT_P != s_port) return 0; + if (payload_type != ops->payload_type) { dbg("%s, payload_type mismatch %d %d\n", __func__, payload_type, ops->payload_type); s->stat_pkts_wrong_pt_dropped++; From 91b3ae124915b0dcfb297f2cd191a26c8b4909d4 Mon Sep 17 00:00:00 2001 From: Frank Du Date: Wed, 10 Jan 2024 14:11:21 +0800 Subject: [PATCH 2/3] fix linit Signed-off-by: Frank Du --- doc/design.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/design.md b/doc/design.md index 9f78bec35..027c0e356 100644 --- a/doc/design.md +++ b/doc/design.md @@ -478,7 +478,9 @@ In addition to the built-in RxTxApp, IMTL also provides numerous sample codes th ### 6.16 RX Timing Parser -To verify the compliance of incoming ST2110-20 RX streams with the ST2110 standard, IMTL provides several utilities for analysis. To support this functionality, if the Network Interface Card (NIC) supports the hardware time synchronization feature, IMTL will read the RX timestamp directly from the NIC's hardware to obtain accurate timing. And please set `MTL_FLAG_ENABLE_HW_TIMESTAMP` flag to enable HW offload timestamp for all RX packets. +To verify the compliance of incoming ST2110-20 RX streams with the ST2110 standard, IMTL provides several utilities for analysis. +To support this functionality, if the Network Interface Card (NIC) supports the hardware time synchronization feature, IMTL will read the RX timestamp directly from the NIC's hardware to obtain accurate timing. And please set `MTL_FLAG_ENABLE_HW_TIMESTAMP` flag to enable HW offload timestamp for all RX packets. + The fallback method is to read the time when IMTL processes the packet. However, it's important to note that this fallback method cannot guarantee timing accuracy, potentially rendering the parsed results unreliable. The simplest method is to enable the built-in status report. An application can activate the feature by setting the flag `ST20_RX_FLAG_TIMING_PARSER_STAT` or `ST20P_RX_FLAG_TIMING_PARSER_STAT`. Subsequently, IMTL will engage the Timing Parser module and include the results in the status log. From c076390b02083654b60fa7f61dba14394d08ee92 Mon Sep 17 00:00:00 2001 From: Frank Du Date: Wed, 10 Jan 2024 14:25:47 +0800 Subject: [PATCH 3/3] fix Signed-off-by: Frank Du --- tests/src/st20p_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/st20p_test.cpp b/tests/src/st20p_test.cpp index 37014e2d0..caba92501 100644 --- a/tests/src/st20p_test.cpp +++ b/tests/src/st20p_test.cpp @@ -458,7 +458,7 @@ static void test_st20p_rx_frame_thread(void* args) { if (frame->timestamp == timestamp) s->incomplete_frame_cnt++; timestamp = frame->timestamp; if (s->rx_timing_parser) { - if (!frame->tp) s->incomplete_frame_cnt++; + if (!frame->tp[MTL_SESSION_PORT_P]) s->incomplete_frame_cnt++; } /* check user timestamp if it has */