diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c4aea7f8..e542d4e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ * dpdk: upgrade dpdk version to 23.11. * st22: add interlaced support. * log: add custom log printer, see mtl_set_log_printer. +* rx/timing_parser: add timing_parser stat report for RX video, `--rx_timing_parser` in RxTxApp to enable. * pipeline: add block get mode support, see `ST20P_TX_FLAG_BLOCK_GET`/`ST20P_RX_FLAG_BLOCK_GET`/`ST22P_TX_FLAG_BLOCK_GET`/`ST22P_RX_FLAG_BLOCK_GET`. +* rx/timing_parser: add support to export the timing_parser to app, see `app/sample/rx_st20p_timing_parser_sample.c` for usage ## Changelog for 23.12 diff --git a/app/meson.build b/app/meson.build index fdcd6f38d..e34095062 100644 --- a/app/meson.build +++ b/app/meson.build @@ -293,6 +293,13 @@ executable('TxVideoSplitSample', video_tx_split_sample_sources, dependencies: [asan_dep, mtl, ws2_32_dep, mman_dep] ) +executable('RxVideoTimingParserSample', rx_st20p_timing_parser_sample_sources, + c_args : app_c_args, + link_args: app_ld_args, + # asan should be always the first dep + dependencies: [asan_dep, mtl, libpthread, ws2_32_dep] +) + # Legacy video samples app executable('TxVideoSample', video_tx_sample_sources, c_args : app_c_args, diff --git a/app/sample/meson.build b/app/sample/meson.build index f0504174b..56ec08d53 100644 --- a/app/sample/meson.build +++ b/app/sample/meson.build @@ -18,6 +18,7 @@ rx_st20p_tx_st20p_downsample_merge_fwd_sources = files('fwd/rx_st20p_tx_st20p_do # misc video_tx_split_sample_sources = files('tx_video_split_sample.c', 'sample_util.c') +rx_st20p_timing_parser_sample_sources = files('rx_st20p_timing_parser_sample.c', 'sample_util.c') # ext frame pipeline_tx_st20_ext_frame_sample_sources = files('ext_frame/tx_st20_pipeline_ext_frame_sample.c', 'sample_util.c') diff --git a/app/sample/rx_st20p_timing_parser_sample.c b/app/sample/rx_st20p_timing_parser_sample.c new file mode 100644 index 000000000..128e572fe --- /dev/null +++ b/app/sample/rx_st20p_timing_parser_sample.c @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Intel Corporation + */ + +#include + +#include "sample_util.h" + +struct rx_tp_stat { + /* stat report */ + int32_t cinst_max; + int32_t cinst_min; + int32_t vrx_max; + int32_t vrx_min; + int32_t ipt_max; + int32_t ipt_min; + int32_t fpt_max; + int32_t fpt_min; + int32_t latency_max; + int32_t latency_min; + int32_t rtp_offset_max; + int32_t rtp_offset_min; + int32_t rtp_ts_delta_max; + int32_t rtp_ts_delta_min; + /* result */ + uint32_t compliant_result[ST_RX_TP_COMPLIANT_MAX]; +}; + +struct rx_timing_parser_sample_ctx { + int idx; + int fb_cnt; + st20p_rx_handle handle; + + bool stop; + pthread_t frame_thread; + int fb_recv; + + struct st20_rx_tp_pass pass; + bool pass_get; /* pass critical is only available */ + + /* stat report */ + struct rx_tp_stat stat; +}; + +static void rx_st20p_tp_stat_init(struct rx_tp_stat* stat) { + memset(stat, 0, sizeof(*stat)); + + stat->cinst_max = INT_MIN; + stat->cinst_min = INT_MAX; + stat->vrx_max = INT_MIN; + stat->vrx_min = INT_MAX; + stat->ipt_max = INT_MIN; + stat->ipt_min = INT_MAX; + stat->fpt_min = INT_MIN; + stat->fpt_min = INT_MAX; + stat->latency_max = INT_MIN; + stat->latency_min = INT_MAX; + stat->rtp_offset_max = INT_MIN; + stat->rtp_offset_min = INT_MAX; + stat->rtp_ts_delta_max = INT_MIN; + stat->rtp_ts_delta_min = INT_MAX; +} + +static void rx_st20p_tp_stat_print(struct rx_timing_parser_sample_ctx* s, + struct rx_tp_stat* stat) { + int idx = s->idx; + info("%s(%d), COMPLIANT NARROW %d WIDE %d FAILED %d!\n", __func__, idx, + stat->compliant_result[ST_RX_TP_COMPLIANT_NARROW], + stat->compliant_result[ST_RX_TP_COMPLIANT_WIDE], + stat->compliant_result[ST_RX_TP_COMPLIANT_FAILED]); + info("%s(%d), CINST MIN %d MAX %d!\n", __func__, idx, stat->cinst_min, stat->cinst_max); + info("%s(%d), VRX MIN %d MAX %d!\n", __func__, idx, stat->vrx_min, stat->vrx_max); + info("%s(%d), IPT MIN %d MAX %d!\n", __func__, idx, stat->ipt_min, stat->ipt_max); + info("%s(%d), FPT MIN %d MAX %d!\n", __func__, idx, stat->fpt_min, stat->fpt_max); + info("%s(%d), LATENCY MIN %d MAX %d!\n", __func__, idx, stat->latency_min, + stat->latency_max); + info("%s(%d), RTP OFFSET MIN %d MAX %d!\n", __func__, idx, stat->rtp_offset_min, + stat->rtp_offset_max); + info("%s(%d), RTP TS DELTA MIN %d MAX %d!\n", __func__, idx, stat->rtp_ts_delta_min, + stat->rtp_ts_delta_max); +} + +static int rx_st20p_tp_consume(struct rx_timing_parser_sample_ctx* s, + 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; + 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); + stat->cinst_max = ST_MAX(tp->cinst_max, stat->cinst_max); + stat->ipt_min = ST_MIN(tp->ipt_min, stat->ipt_min); + stat->ipt_max = ST_MAX(tp->ipt_max, stat->ipt_max); + stat->fpt_min = ST_MIN(tp->fpt, stat->fpt_min); + stat->fpt_max = ST_MAX(tp->fpt, stat->fpt_max); + stat->latency_min = ST_MIN(tp->latency, stat->latency_min); + stat->latency_max = ST_MAX(tp->latency, stat->latency_max); + stat->rtp_offset_min = ST_MIN(tp->rtp_offset, stat->rtp_offset_min); + stat->rtp_offset_max = ST_MAX(tp->rtp_offset, stat->rtp_offset_max); + stat->rtp_ts_delta_min = ST_MIN(tp->rtp_ts_delta, stat->rtp_ts_delta_min); + stat->rtp_ts_delta_max = ST_MAX(tp->rtp_ts_delta, stat->rtp_ts_delta_max); + + stat->compliant_result[tp->compliant]++; + + return 0; +} + +static void* rx_st20p_tp_thread(void* arg) { + struct rx_timing_parser_sample_ctx* s = arg; + st20p_rx_handle handle = s->handle; + struct st_frame* frame; + int idx = s->idx; + int ret; + + info("%s(%d), start\n", __func__, idx); + while (!s->stop) { + frame = st20p_rx_get_frame(handle); + if (!frame) { /* no frame */ + warn("%s(%d), get frame time out\n", __func__, idx); + continue; + } + if (!s->pass_get) { + ret = st20p_rx_timing_parser_critical(handle, &s->pass); + if (ret >= 0) { + s->pass_get = true; + info("%s(%d), pass critical, cinst narrow %d wide %d, vrx narrow %d wide %d\n", + __func__, idx, s->pass.cinst_max_narrow, s->pass.cinst_max_wide, + s->pass.vrx_max_narrow, s->pass.vrx_max_wide); + } + } + rx_st20p_tp_consume(s, frame->tp); + + s->fb_recv++; + st20p_rx_put_frame(handle, frame); + } + info("%s(%d), stop\n", __func__, idx); + + return NULL; +} + +int main(int argc, char** argv) { + struct st_sample_context ctx; + int ret; + + /* init sample(st) dev */ + memset(&ctx, 0, sizeof(ctx)); + ret = rx_sample_parse_args(&ctx, argc, argv); + if (ret < 0) return ret; + + ctx.st = mtl_init(&ctx.param); + if (!ctx.st) { + err("%s: mtl_init fail\n", __func__); + return -EIO; + } + + uint32_t session_num = ctx.sessions; + struct rx_timing_parser_sample_ctx* app[session_num]; + + // create and register rx session + for (int i = 0; i < session_num; i++) { + app[i] = malloc(sizeof(struct rx_timing_parser_sample_ctx)); + if (!app[i]) { + err("%s(%d), app context malloc fail\n", __func__, i); + ret = -ENOMEM; + goto error; + } + memset(app[i], 0, sizeof(struct rx_timing_parser_sample_ctx)); + app[i]->idx = i; + app[i]->stop = false; + app[i]->fb_cnt = ctx.framebuff_cnt; + rx_st20p_tp_stat_init(&app[i]->stat); + + 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; + 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; + ops_rx.port.payload_type = ctx.payload_type; + ops_rx.width = ctx.width; + ops_rx.height = ctx.height; + ops_rx.fps = ctx.fps; + ops_rx.interlaced = ctx.interlaced; + ops_rx.transport_fmt = ctx.fmt; + ops_rx.output_fmt = ctx.output_fmt; + ops_rx.device = ST_PLUGIN_DEVICE_AUTO; + ops_rx.framebuff_cnt = app[i]->fb_cnt; + ops_rx.flags = ST20P_RX_FLAG_BLOCK_GET; + ops_rx.flags |= ST20P_RX_FLAG_TIMING_PARSER_META; + + st20p_rx_handle rx_handle = st20p_rx_create(ctx.st, &ops_rx); + if (!rx_handle) { + err("%s(%d), st20p_rx_create fail\n", __func__, i); + ret = -EIO; + goto error; + } + app[i]->handle = rx_handle; + + ret = pthread_create(&app[i]->frame_thread, NULL, rx_st20p_tp_thread, app[i]); + if (ret < 0) { + err("%s(%d), thread create fail %d\n", __func__, ret, i); + ret = -EIO; + goto error; + } + } + + // start rx + ret = mtl_start(ctx.st); + + int cnt = 0; + while (!ctx.exit) { + sleep(1); + 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); + } + } + } + + // stop app thread + for (int i = 0; i < session_num; i++) { + app[i]->stop = true; + pthread_join(app[i]->frame_thread, NULL); + info("%s(%d), received frames %d\n", __func__, i, app[i]->fb_recv); + } + + // stop rx + ret = mtl_stop(ctx.st); + + // check result + for (int i = 0; i < session_num; i++) { + if (app[i]->fb_recv <= 0) { + err("%s(%d), error, no received frames %d\n", __func__, i, app[i]->fb_recv); + ret = -EIO; + } + } + +error: + for (int i = 0; i < session_num; i++) { + if (app[i]) { + if (app[i]->handle) st20p_rx_free(app[i]->handle); + free(app[i]); + } + } + + /* release sample(st) dev */ + if (ctx.st) { + mtl_uninit(ctx.st); + ctx.st = NULL; + } + return ret; +} diff --git a/app/sample/sample_util.h b/app/sample/sample_util.h index 36d3b4d1d..2968e2b77 100644 --- a/app/sample/sample_util.h +++ b/app/sample/sample_util.h @@ -55,6 +55,7 @@ #endif #define ST_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ST_MIN(a, b) ((a) < (b) ? (a) : (b)) enum sample_udp_mode { /* client/server mode */ diff --git a/app/src/app_base.h b/app/src/app_base.h index 9909119b7..e38cfe120 100644 --- a/app/src/app_base.h +++ b/app/src/app_base.h @@ -60,6 +60,7 @@ #define UTC_OFFSET (37) /* 2022/07 */ #define ST_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ST_MIN(a, b) ((a) < (b) ? (a) : (b)) struct st_display { char name[36]; diff --git a/app/src/rx_st20p_app.c b/app/src/rx_st20p_app.c index 6be8b6c43..1373cef5b 100644 --- a/app/src/rx_st20p_app.c +++ b/app/src/rx_st20p_app.c @@ -240,7 +240,7 @@ static int app_rx_st20p_init(struct st_app_context* ctx, /* always try to enable DMA offload */ ops.flags = ST20P_RX_FLAG_DMA_OFFLOAD; if (st20p && st20p->enable_rtcp) ops.flags |= ST20P_RX_FLAG_ENABLE_RTCP; - if (ctx->enable_timing_parser) ops.flags |= ST20P_RX_FLAG_ENABLE_TIMING_PARSER; + if (ctx->enable_timing_parser) ops.flags |= ST20P_RX_FLAG_TIMING_PARSER_STAT; st_pthread_mutex_init(&s->st20p_wake_mutex, NULL); st_pthread_cond_init(&s->st20p_wake_cond, NULL); diff --git a/app/src/rx_video_app.c b/app/src/rx_video_app.c index 21ee82545..6e219dc9b 100644 --- a/app/src/rx_video_app.c +++ b/app/src/rx_video_app.c @@ -562,7 +562,7 @@ static int app_rx_video_init(struct st_app_context* ctx, st_json_video_session_t ops.flags |= ST20_RX_FLAG_ENABLE_RTCP; ops.rtcp.nack_interval_us = 250; } - if (ctx->enable_timing_parser) ops.flags |= ST20_RX_FLAG_ENABLE_TIMING_PARSER; + if (ctx->enable_timing_parser) ops.flags |= ST20_RX_FLAG_TIMING_PARSER_STAT; st_pthread_mutex_init(&s->st20_wake_mutex, NULL); st_pthread_cond_init(&s->st20_wake_cond, NULL); diff --git a/doc/design.md b/doc/design.md index 52fc26ed1..77d15a16e 100644 --- a/doc/design.md +++ b/doc/design.md @@ -476,6 +476,15 @@ IntelĀ® Media SDK: Leverage IMTL's robust capabilities within IntelĀ® Media SDK In addition to the built-in RxTxApp, IMTL also provides numerous sample codes that demonstrate how to construct simple test programs using its APIs. For more details, please refer to [sample](../app/sample/). We also provide some very useful forward application demo, detail can be found at [fwd](../app/sample/fwd/). +### 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. +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. + +For applications requiring access to the timing parser results for each frame, the flag `ST20_RX_FLAG_TIMING_PARSER_META` or `ST20P_RX_FLAG_TIMING_PARSER_META` can be enabled. This allows an application to retrieve detailed parsing results via the `struct st20_rx_tp_meta` found within the RX meta structure for each frame. Reference sample code is available at [rx_st20p_timing_parser_sample.c](../app/sample/rx_st20p_timing_parser_sample.c). + ## 7. Misc ### 7.1 Logging diff --git a/include/st20_api.h b/include/st20_api.h index 339b5cf93..d5f963d1c 100644 --- a/include/st20_api.h +++ b/include/st20_api.h @@ -192,9 +192,14 @@ extern "C" { #define ST20_RX_FLAG_DISABLE_MIGRATE (MTL_BIT32(20)) /** * Flag bit in flags of struct st20_rx_ops. - * Enable the timing analyze + * Enable the timing analyze info in the stat dump */ -#define ST20_RX_FLAG_ENABLE_TIMING_PARSER (MTL_BIT32(21)) +#define ST20_RX_FLAG_TIMING_PARSER_STAT (MTL_BIT32(21)) +/** + * Flag bit in flags of struct st20_rx_ops. + * Enable the timing analyze info in the st20_rx_frame_meta + */ +#define ST20_RX_FLAG_TIMING_PARSER_META (MTL_BIT32(22)) /** * Flag bit in flags of struct st22_rx_ops, for non MTL_PMD_DPDK_USER. @@ -385,6 +390,88 @@ struct st20_tx_slice_meta { uint16_t lines_ready; }; +/** + * st20 rx timing parser meta for each frame as defined in SMPTE ST2110-21. + * Referenced from + * `https://github.com/ebu/pi-list/blob/master/docs/video_timing_analysis.md`. + * + * cinst: Instantaneous value of the Retwork Compatibility model C. + * vrx: Measured level of the Virtual Receive Buffer. + * ipt: Inter-packet time, ns + * fpt: First Packet Time measured between frame/field reference time and the first + * captured packet of a frame/field. Unit: ns + * latency: TPA0(Actual measured arrival time of a packet) - RTP Timestamp. Unit: ns + * rtp_offset: RTP OFFSET = RTP Timestamp - N x Tframe, unit: timestamp ticks + * rtp_ts_delta: Delta between RTP timestamps of 2 consecutive frames/fields, + * unit: timestamp ticks. + */ +struct st20_rx_tp_meta { + /** the max of cinst for current frame */ + int32_t cinst_max; + /** the min of cinst for current frame */ + int32_t cinst_min; + /** the average of cinst for current frame */ + float cinst_avg; + /** the max of vrx for current frame */ + int32_t vrx_max; + /** the min of vrx for current frame */ + int32_t vrx_min; + /** the average of vrx for current frame */ + float vrx_avg; + /** the max of ipt(Inter-packet time, ns) for current frame */ + int32_t ipt_max; + /** the min of ipt(Inter-packet time, ns) for current frame */ + int32_t ipt_min; + /** the average of ipt(Inter-packet time, ns) for current frame */ + float ipt_avg; + + /** fpt(ns) for current frame */ + int32_t fpt; + /** latency(ns) for current frame */ + int32_t latency; + /** rtp_offset(ticks) for current frame */ + int32_t rtp_offset; + /** rtp_ts_delta(ticks) for current frame */ + int32_t rtp_ts_delta; + /** RX timing parser compliant result */ + enum st_rx_tp_compliant compliant; + /** the failed cause if compliant is not ST_RX_TP_COMPLIANT_NARROW */ + char failed_cause[64]; + + /* packets count in current report meta */ + uint32_t pkts_cnt; +}; + +/** st20 rx timing parser pass critical */ +struct st20_rx_tp_pass { + /** The max allowed cinst for narrow */ + int32_t cinst_max_narrow; + /** The max allowed cinst for wide */ + int32_t cinst_max_wide; + /** The min allowed cinst: 0 */ + int32_t cinst_min; + /** The max allowed vrx full for narrow */ + int32_t vrx_max_narrow; + /** The max allowed vrx wide for narrow */ + int32_t vrx_max_wide; + /** The min allowed vrx: 0 */ + int32_t vrx_min; + /** tr_offset, in ns, pass if fpt < tr_offset */ + int32_t tr_offset; + /** The max allowed latency: 1000 us */ + int32_t latency_max; + /** The min allowed latency: 0 */ + int32_t latency_min; + /** The max allowed rtp_offset */ + int32_t rtp_offset_max; + /** The min allowed rtp_offset: -1 */ + int32_t rtp_offset_min; + /** The max allowed rtp_ts_delta */ + int32_t rtp_ts_delta_max; + /** The min allowed rtp_ts_delta */ + int32_t rtp_ts_delta_min; +}; + /** * Frame meta data of st2110-20(video) rx streaming */ @@ -434,6 +521,8 @@ struct st20_rx_frame_meta { * of received packets can be assessed by comparing 'pkts_recv[s_port]' with * '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; }; /** @@ -1704,35 +1793,6 @@ int st20_tx_get_port_stats(st20_tx_handle handle, enum mtl_session_port port, */ int st20_tx_reset_port_stats(st20_tx_handle handle, enum mtl_session_port port); -/** - * Retrieve the general statistics(I/O) for one rx st2110-20(video) session port. - * - * @param handle - * The handle to the rx st2110-20(video) session. - * @param port - * The port index. - * @param stats - * A pointer to stats structure. - * @return - * - >=0 succ. - * - <0: Error code. - */ -int st20_rx_get_port_stats(st20_rx_handle handle, enum mtl_session_port port, - struct st20_rx_port_status* stats); - -/** - * Reset the general statistics(I/O) for one rx st2110-20(video) session port. - * - * @param handle - * The handle to the rx st2110-20(video) session. - * @param port - * The port index. - * @return - * - >=0 succ. - * - <0: Error code. - */ -int st20_rx_reset_port_stats(st20_rx_handle handle, enum mtl_session_port port); - /** * Retrieve the pixel group info from st2110-20(video) format. * @@ -2040,6 +2100,49 @@ int st20_rx_get_queue_meta(st20_rx_handle handle, struct st_queue_meta* meta); */ bool st20_rx_dma_enabled(st20_rx_handle handle); +/** + * Get the timing parser pass critical to rx st2110-20(video) session. + * Only avaiable if ST20_RX_FLAG_TIMING_PARSER_META is enabled. + * + * @param handle + * The handle to the rx st2110-20(video) session. + * @param pass + * the pointer to save the timing parser pass critical. + * @return + * - 0: Success. + * - <0: Error code. + */ +int st20_rx_timing_parser_critical(st20_rx_handle handle, struct st20_rx_tp_pass* pass); + +/** + * Retrieve the general statistics(I/O) for one rx st2110-20(video) session port. + * + * @param handle + * The handle to the rx st2110-20(video) session. + * @param port + * The port index. + * @param stats + * A pointer to stats structure. + * @return + * - >=0 succ. + * - <0: Error code. + */ +int st20_rx_get_port_stats(st20_rx_handle handle, enum mtl_session_port port, + struct st20_rx_port_status* stats); + +/** + * Reset the general statistics(I/O) for one rx st2110-20(video) session port. + * + * @param handle + * The handle to the rx st2110-20(video) session. + * @param port + * The port index. + * @return + * - >=0 succ. + * - <0: Error code. + */ +int st20_rx_reset_port_stats(st20_rx_handle handle, enum mtl_session_port port); + /** * Create one rx st2110-22(compressed video) session. * diff --git a/include/st_api.h b/include/st_api.h index 3a85b7923..e88303e1b 100644 --- a/include/st_api.h +++ b/include/st_api.h @@ -42,6 +42,18 @@ enum st10_timestamp_fmt { ST10_TIMESTAMP_FMT_MAX, }; +/** ST RX timing parser compliant result */ +enum st_rx_tp_compliant { + /** Fail */ + ST_RX_TP_COMPLIANT_FAILED = 0, + /** Wide */ + ST_RX_TP_COMPLIANT_WIDE, + /** Narrow */ + ST_RX_TP_COMPLIANT_NARROW, + /** max value of this enum */ + ST_RX_TP_COMPLIANT_MAX, +}; + /** * FPS type of media streaming, Frame per second or Field per second */ diff --git a/include/st_pipeline_api.h b/include/st_pipeline_api.h index 4dc8a0ab4..5fe9366a7 100644 --- a/include/st_pipeline_api.h +++ b/include/st_pipeline_api.h @@ -294,6 +294,9 @@ struct st_frame { void* priv; /** priv data for user */ 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; }; /** Device type of st plugin */ @@ -535,9 +538,14 @@ enum st20p_rx_flag { */ ST20P_RX_FLAG_DISABLE_MIGRATE = (MTL_BIT32(20)), /** - * Enable the timing analyze + * Enable the timing analyze info in the stat dump */ - ST20P_RX_FLAG_ENABLE_TIMING_PARSER = (MTL_BIT32(21)), + ST20P_RX_FLAG_TIMING_PARSER_STAT = (MTL_BIT32(21)), + /** + * Enable the timing analyze info in the the return `struct st_frame` by the + * st20p_rx_get_frame + */ + ST20P_RX_FLAG_TIMING_PARSER_META = (MTL_BIT32(22)), }; /** The structure info for st plugin encode session create request. */ @@ -1748,6 +1756,20 @@ int st20p_rx_reset_port_stats(st20p_rx_handle handle, enum mtl_session_port port */ int st20p_rx_update_source(st20p_rx_handle handle, struct st_rx_source_info* src); +/** + * Get the timing parser pass critical to rx st2110-20(pipeline) session. + * Only avaiable if ST20P_RX_FLAG_TIMING_PARSER_META is enabled. + * + * @param handle + * The handle to the rx st2110-20(pipeline) session. + * @param pass + * the pointer to save the timing parser pass critical. + * @return + * - 0: Success. + * - <0: Error code. + */ +int st20p_rx_timing_parser_critical(st20p_rx_handle handle, struct st20_rx_tp_pass* pass); + /** * Convert color format from source frame to destination frame. * diff --git a/lib/src/st2110/pipeline/st20_pipeline_rx.c b/lib/src/st2110/pipeline/st20_pipeline_rx.c index 473964977..05fb11bc6 100644 --- a/lib/src/st2110/pipeline/st20_pipeline_rx.c +++ b/lib/src/st2110/pipeline/st20_pipeline_rx.c @@ -210,6 +210,13 @@ static int rx_st20p_frame_ready(void* priv, void* frame, 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; + } + /* check user meta */ framebuff->user_meta_data_size = 0; if (meta->user_meta) { @@ -413,8 +420,10 @@ static int rx_st20p_create_transport(struct mtl_main_impl* impl, struct st20p_rx if (ops->flags & ST20P_RX_FLAG_HDR_SPLIT) ops_rx.flags |= ST20_RX_FLAG_HDR_SPLIT; if (ops->flags & ST20P_RX_FLAG_DISABLE_MIGRATE) ops_rx.flags |= ST20_RX_FLAG_DISABLE_MIGRATE; - if (ops->flags & ST20P_RX_FLAG_ENABLE_TIMING_PARSER) - ops_rx.flags |= ST20_RX_FLAG_ENABLE_TIMING_PARSER; + if (ops->flags & ST20P_RX_FLAG_TIMING_PARSER_STAT) + ops_rx.flags |= ST20_RX_FLAG_TIMING_PARSER_STAT; + if (ops->flags & ST20P_RX_FLAG_TIMING_PARSER_META) + ops_rx.flags |= ST20_RX_FLAG_TIMING_PARSER_META; if (ops->flags & ST20P_RX_FLAG_PKT_CONVERT) { uint64_t pkt_cvt_output_cap = ST_FMT_CAP_YUV422PLANAR10LE | ST_FMT_CAP_Y210 | ST_FMT_CAP_UYVY; @@ -990,3 +999,16 @@ int st20p_rx_update_source(st20p_rx_handle handle, struct st_rx_source_info* src return st20_rx_update_source(ctx->transport, src); } + +int st20p_rx_timing_parser_critical(st20p_rx_handle handle, + struct st20_rx_tp_pass* pass) { + struct st20p_rx_ctx* ctx = handle; + int cidx = ctx->idx; + + if (ctx->type != MT_ST20_HANDLE_PIPELINE_RX) { + err("%s(%d), invalid type %d\n", __func__, cidx, ctx->type); + return 0; + } + + return st20_rx_timing_parser_critical(ctx->transport, pass); +} diff --git a/lib/src/st2110/pipeline/st20_pipeline_rx.h b/lib/src/st2110/pipeline/st20_pipeline_rx.h index 2e8aff035..a252a2b4b 100644 --- a/lib/src/st2110/pipeline/st20_pipeline_rx.h +++ b/lib/src/st2110/pipeline/st20_pipeline_rx.h @@ -26,6 +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 st20p_rx_ctx { diff --git a/lib/src/st2110/st_header.h b/lib/src/st2110/st_header.h index 510f52ea8..8c841fe9b 100644 --- a/lib/src/st2110/st_header.h +++ b/lib/src/st2110/st_header.h @@ -498,52 +498,23 @@ struct st_rx_session_priv { enum mtl_session_port s_port; }; -enum st_rx_tp_compliant { - ST_RX_TP_COMPLIANT_FAILED = 0, - ST_RX_TP_COMPLIANT_WIDE, - ST_RX_TP_COMPLIANT_NARROW, - ST_RX_TP_COMPLIANT_MAX, -}; - struct st_rv_tp_slot { /* epoch of current slot */ uint64_t cur_epochs; - /* result(packet) cnt in current slot */ - uint32_t pkt_cnt; + + struct st20_rx_tp_meta meta; uint32_t rtp_tmstamp; uint64_t first_pkt_time; /* ns */ uint64_t prev_pkt_time; /* ns */ - /* fpt(first packet time) against the epoch, pass criteria < TR_OFFSET */ - int32_t fpt_to_epoch; /* ns */ - int32_t latency; /* ns */ - int32_t rtp_offset; /* ticks */ - int32_t rtp_ts_delta; /* ticks */ - - /* latency */ - // int32_t latency; - /* rtp offset */ - // int32_t rtp_offset; - /* Cinst, packet level check */ - int32_t cinst_max; - int32_t cinst_min; int64_t cinst_sum; - float cinst_avg; /* vrx, packet level check */ int32_t vrx_drained_prev; int32_t vrx_prev; - int32_t vrx_max; - int32_t vrx_min; int64_t vrx_sum; - float vrx_avg; /* Inter-packet time(ns), packet level check */ - int32_t ipt_max; - int32_t ipt_min; int64_t ipt_sum; - float ipt_avg; - - enum st_rx_tp_compliant compliant; }; struct st_rv_tp_stat { @@ -569,14 +540,8 @@ struct st_rv_tp_stat { struct st_rx_video_tp { /* in ns for of 2 consecutive packets, T-Frame / N-Packets */ double trs; - /* in ns, tr offset time of each frame */ - double tr_offset; /* pass criteria */ - uint32_t c_max_narrow_pass; - uint32_t c_max_wide_pass; - uint32_t vrx_full_narrow_pass; - uint32_t vrx_full_wide_pass; - int32_t rtp_offset_max_pass; + struct st20_rx_tp_pass pass; /* timing info for each slot */ struct st_rv_tp_slot slots[ST_VIDEO_RX_REC_NUM_OFO]; @@ -692,6 +657,8 @@ struct st_rx_video_session_impl { /* if enable the parser for the st2110-21 timing */ bool enable_timing_parser; + bool enable_timing_parser_stat; + bool enable_timing_parser_meta; struct st_rx_video_tp* tp; /* status */ diff --git a/lib/src/st2110/st_pkt.h b/lib/src/st2110/st_pkt.h index 44b35412e..deffabc0b 100644 --- a/lib/src/st2110/st_pkt.h +++ b/lib/src/st2110/st_pkt.h @@ -15,20 +15,8 @@ #define ST_TP_CINST_DRAIN_FACTOR (1.1f) /* Drain factor */ -#define ST_TP_LATENCY_MAX_US (1000) /* Latency in us */ -#define ST_TP_LATENCY_MAX_NS (1000 * ST_TP_LATENCY_MAX_US) /* Latency in ns */ - -#define ST_TP_RTP_OFFSET_MIN (-1) /* MIN RTP Offset */ - #define ST_TP_RTP_WRAP_AROUND (0x100000000) -#define ST_TP_PASS_NARROW "PASSED NARROW" -#define ST_TP_PASS_WIDE "FAILED with WIDE" -/* Extend WA WIDE as no hw rx time */ -#define ST_TP_PASS_WIDE_WA "FAILED with WIDE WA error" -#define ST_TP_PASS "PASSED" -#define ST_TP_FAIL "FAILED" - /* total size: 54 */ struct st_rfc3550_hdr { struct rte_ether_hdr eth; /* size: 14 */ diff --git a/lib/src/st2110/st_rx_timing_parser.c b/lib/src/st2110/st_rx_timing_parser.c index 3c2592f4f..e3d6fb768 100644 --- a/lib/src/st2110/st_rx_timing_parser.c +++ b/lib/src/st2110/st_rx_timing_parser.c @@ -23,30 +23,30 @@ void rv_tp_on_packet(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* s slot->cur_epochs = epochs; slot->rtp_tmstamp = rtp_tmstamp; slot->first_pkt_time = pkt_time; - slot->fpt_to_epoch = pkt_time - epoch_tmstamp; + slot->meta.fpt = pkt_time - epoch_tmstamp; uint64_t tmstamp64 = epochs * s->frame_time_sampling; uint32_t tmstamp32 = tmstamp64; double diff_rtp_ts = (double)rtp_tmstamp - tmstamp32; double diff_rtp_ts_ns = diff_rtp_ts * s->frame_time / s->frame_time_sampling; - slot->latency = slot->fpt_to_epoch - diff_rtp_ts_ns; - slot->rtp_offset = diff_rtp_ts; + slot->meta.latency = slot->meta.fpt - diff_rtp_ts_ns; + slot->meta.rtp_offset = diff_rtp_ts; if (tp->pre_rtp_tmstamp) { - slot->rtp_ts_delta = rtp_tmstamp - tp->pre_rtp_tmstamp; + slot->meta.rtp_ts_delta = rtp_tmstamp - tp->pre_rtp_tmstamp; } tp->pre_rtp_tmstamp = rtp_tmstamp; } epoch_tmstamp = (uint64_t)(slot->cur_epochs * s->frame_time); - tvd = epoch_tmstamp + tp->tr_offset; + tvd = epoch_tmstamp + tp->pass.tr_offset; /* Calculate vrx */ packet_delta_ns = (double)pkt_time - tvd; int32_t drained = (packet_delta_ns + trs) / trs; int32_t vrx_cur = slot->vrx_prev + 1 - (drained - slot->vrx_drained_prev); slot->vrx_sum += vrx_cur; - slot->vrx_min = RTE_MIN(vrx_cur, slot->vrx_min); - slot->vrx_max = RTE_MAX(vrx_cur, slot->vrx_max); + slot->meta.vrx_min = RTE_MIN(vrx_cur, slot->meta.vrx_min); + slot->meta.vrx_max = RTE_MAX(vrx_cur, slot->meta.vrx_max); slot->vrx_prev = vrx_cur; slot->vrx_drained_prev = drained; @@ -54,58 +54,100 @@ void rv_tp_on_packet(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* s int exp_cin_pkts = ((pkt_time - slot->first_pkt_time) / trs) * ST_TP_CINST_DRAIN_FACTOR; int cinst = RTE_MAX(0, pkt_idx - exp_cin_pkts); slot->cinst_sum += cinst; - slot->cinst_min = RTE_MIN(cinst, slot->cinst_min); - slot->cinst_max = RTE_MAX(cinst, slot->cinst_max); + slot->meta.cinst_min = RTE_MIN(cinst, slot->meta.cinst_min); + slot->meta.cinst_max = RTE_MAX(cinst, slot->meta.cinst_max); /* calculate Inter-packet time */ if (slot->prev_pkt_time) { double ipt = (double)pkt_time - slot->prev_pkt_time; slot->ipt_sum += ipt; - slot->ipt_min = RTE_MIN(ipt, slot->ipt_min); - slot->ipt_max = RTE_MAX(ipt, slot->ipt_max); + slot->meta.ipt_min = RTE_MIN(ipt, slot->meta.ipt_min); + slot->meta.ipt_max = RTE_MAX(ipt, slot->meta.ipt_max); } slot->prev_pkt_time = pkt_time; - slot->pkt_cnt++; + slot->meta.pkts_cnt++; +} + +static void rv_tp_compliant_set_cause(struct st20_rx_tp_meta* meta, char* cause) { + snprintf(meta->failed_cause, sizeof(meta->failed_cause), "%s", cause); } -static enum st_rx_tp_compliant rv_tp_compliant(struct st_rx_video_session_impl* s, - struct st_rx_video_tp* tp, +static enum st_rx_tp_compliant rv_tp_compliant(struct st_rx_video_tp* tp, struct st_rv_tp_slot* slot) { /* fpt check */ - if (slot->fpt_to_epoch > tp->tr_offset) return ST_RX_TP_COMPLIANT_FAILED; + if (slot->meta.fpt > tp->pass.tr_offset) { + rv_tp_compliant_set_cause(&slot->meta, "fpt exceed tr_offset"); + return ST_RX_TP_COMPLIANT_FAILED; + } /* rtp ts delta check */ - int32_t sampling = s->frame_time_sampling; - if ((slot->rtp_ts_delta < sampling) || (slot->rtp_ts_delta > (sampling + 1))) + if (slot->meta.rtp_ts_delta < tp->pass.rtp_ts_delta_min) { + rv_tp_compliant_set_cause(&slot->meta, "rtp_ts_delta exceed min"); + return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.rtp_ts_delta > tp->pass.rtp_ts_delta_max) { + rv_tp_compliant_set_cause(&slot->meta, "rtp_ts_delta exceed max"); return ST_RX_TP_COMPLIANT_FAILED; + } /* rtp offset check */ - if ((slot->rtp_offset < ST_TP_RTP_OFFSET_MIN) || - (slot->rtp_offset > tp->rtp_offset_max_pass)) + if (slot->meta.rtp_offset < tp->pass.rtp_offset_min) { + rv_tp_compliant_set_cause(&slot->meta, "rtp_offset exceed min"); + return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.rtp_offset > tp->pass.rtp_offset_max) { + rv_tp_compliant_set_cause(&slot->meta, "rtp_offset exceed max"); return ST_RX_TP_COMPLIANT_FAILED; + } /* latency check */ - if ((slot->latency < 0) || (slot->latency > ST_TP_LATENCY_MAX_NS)) + if (slot->meta.latency < tp->pass.latency_min) { + rv_tp_compliant_set_cause(&slot->meta, "latency exceed min"); return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.latency > tp->pass.latency_max) { + rv_tp_compliant_set_cause(&slot->meta, "latency exceed max"); + return ST_RX_TP_COMPLIANT_FAILED; + } /* vrx check */ - if ((slot->vrx_min < 0) || (slot->vrx_max > tp->vrx_full_wide_pass)) + if (slot->meta.vrx_min < tp->pass.vrx_min) { + rv_tp_compliant_set_cause(&slot->meta, "vrx exceed min"); return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.vrx_max > tp->pass.vrx_max_wide) { + rv_tp_compliant_set_cause(&slot->meta, "vrx exceed max"); + return ST_RX_TP_COMPLIANT_FAILED; + } /* narrow or wide */ - if (slot->cinst_max > tp->c_max_wide_pass) return ST_RX_TP_COMPLIANT_FAILED; - if (slot->cinst_max > tp->c_max_narrow_pass) return ST_RX_TP_COMPLIANT_WIDE; - if (slot->vrx_max > tp->vrx_full_narrow_pass) return ST_RX_TP_COMPLIANT_WIDE; + if (slot->meta.cinst_min > tp->pass.cinst_min) { + rv_tp_compliant_set_cause(&slot->meta, "cinst exceed min"); + return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.cinst_max > tp->pass.cinst_max_wide) { + rv_tp_compliant_set_cause(&slot->meta, "cinst exceed max"); + return ST_RX_TP_COMPLIANT_FAILED; + } + if (slot->meta.cinst_max > tp->pass.cinst_max_narrow) { + rv_tp_compliant_set_cause(&slot->meta, "wide as cinst exceed narrow max"); + return ST_RX_TP_COMPLIANT_WIDE; + } + if (slot->meta.vrx_max > tp->pass.vrx_max_narrow) { + rv_tp_compliant_set_cause(&slot->meta, "wide as vrx exceed narrow max"); + return ST_RX_TP_COMPLIANT_WIDE; + } + rv_tp_compliant_set_cause(&slot->meta, "narrow"); return ST_RX_TP_COMPLIANT_NARROW; } void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, struct st_rv_tp_slot* slot) { struct st_rx_video_tp* tp = s->tp; - float cinst_avg = rv_tp_calculate_avg(slot->pkt_cnt, slot->cinst_sum); - float vrx_avg = rv_tp_calculate_avg(slot->pkt_cnt, slot->vrx_sum); - float ipt_avg = rv_tp_calculate_avg(slot->pkt_cnt, slot->ipt_sum); + 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); + float ipt_avg = rv_tp_calculate_avg(slot->meta.pkts_cnt, slot->ipt_sum); - slot->cinst_avg = cinst_avg; - slot->vrx_avg = vrx_avg; - slot->ipt_avg = ipt_avg; + slot->meta.cinst_avg = cinst_avg; + slot->meta.vrx_avg = vrx_avg; + slot->meta.ipt_avg = ipt_avg; dbg("%s(%d), Cinst AVG %.2f MIN %d MAX %d test %s!\n", __func__, s->idx, cinst_avg, slot->cinst_min, slot->cinst_max, rv_tp_cinst_result(tp, slot)); dbg("%s(%d), VRX AVG %.2f MIN %d MAX %d test %s!\n", __func__, s->idx, vrx_avg, @@ -114,8 +156,10 @@ void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, ipt_avg, slot->ipt_min, slot->ipt_max); /* parse tp compliant for current frame */ - enum st_rx_tp_compliant compliant = rv_tp_compliant(s, tp, slot); - slot->compliant = compliant; + enum st_rx_tp_compliant compliant = rv_tp_compliant(tp, slot); + slot->meta.compliant = compliant; + + if (!s->enable_timing_parser_stat) return; /* update stat */ struct st_rv_tp_stat* stat = &tp->stat; @@ -124,31 +168,31 @@ void rv_tp_slot_parse_result(struct st_rx_video_session_impl* s, stat->stat_compliant_result[compliant]++; stat_slot->vrx_sum += slot->vrx_sum; - stat_slot->vrx_min = RTE_MIN(stat_slot->vrx_min, slot->vrx_min); - stat_slot->vrx_max = RTE_MAX(stat_slot->vrx_min, slot->vrx_max); + stat_slot->meta.vrx_min = RTE_MIN(stat_slot->meta.vrx_min, slot->meta.vrx_min); + stat_slot->meta.vrx_max = RTE_MAX(stat_slot->meta.vrx_min, slot->meta.vrx_max); stat_slot->cinst_sum += slot->cinst_sum; - stat_slot->cinst_min = RTE_MIN(stat_slot->cinst_min, slot->cinst_min); - stat_slot->cinst_max = RTE_MAX(stat_slot->cinst_max, slot->cinst_max); + stat_slot->meta.cinst_min = RTE_MIN(stat_slot->meta.cinst_min, slot->meta.cinst_min); + stat_slot->meta.cinst_max = RTE_MAX(stat_slot->meta.cinst_max, slot->meta.cinst_max); stat_slot->ipt_sum += slot->ipt_sum; - stat_slot->ipt_min = RTE_MIN(stat_slot->ipt_min, slot->ipt_min); - stat_slot->ipt_max = RTE_MAX(stat_slot->ipt_min, slot->ipt_max); - stat_slot->pkt_cnt += slot->pkt_cnt; - - stat->stat_fpt_min = RTE_MIN(stat->stat_fpt_min, slot->fpt_to_epoch); - stat->stat_fpt_max = RTE_MAX(stat->stat_fpt_max, slot->fpt_to_epoch); - stat->stat_fpt_sum += slot->fpt_to_epoch; - stat->stat_latency_min = RTE_MIN(stat->stat_latency_min, slot->latency); - stat->stat_latency_max = RTE_MAX(stat->stat_latency_max, slot->latency); - stat->stat_latency_sum += slot->latency; - stat->stat_rtp_offset_min = RTE_MIN(stat->stat_rtp_offset_min, slot->rtp_offset); - stat->stat_rtp_offset_max = RTE_MAX(stat->stat_rtp_offset_max, slot->rtp_offset); - stat->stat_rtp_offset_sum += slot->rtp_offset; - if (slot->rtp_ts_delta) { + stat_slot->meta.ipt_min = RTE_MIN(stat_slot->meta.ipt_min, slot->meta.ipt_min); + stat_slot->meta.ipt_max = RTE_MAX(stat_slot->meta.ipt_min, slot->meta.ipt_max); + stat_slot->meta.pkts_cnt += slot->meta.pkts_cnt; + + stat->stat_fpt_min = RTE_MIN(stat->stat_fpt_min, slot->meta.fpt); + stat->stat_fpt_max = RTE_MAX(stat->stat_fpt_max, slot->meta.fpt); + stat->stat_fpt_sum += slot->meta.fpt; + stat->stat_latency_min = RTE_MIN(stat->stat_latency_min, slot->meta.latency); + stat->stat_latency_max = RTE_MAX(stat->stat_latency_max, slot->meta.latency); + stat->stat_latency_sum += slot->meta.latency; + stat->stat_rtp_offset_min = RTE_MIN(stat->stat_rtp_offset_min, slot->meta.rtp_offset); + stat->stat_rtp_offset_max = RTE_MAX(stat->stat_rtp_offset_max, slot->meta.rtp_offset); + stat->stat_rtp_offset_sum += slot->meta.rtp_offset; + if (slot->meta.rtp_ts_delta) { stat->stat_rtp_ts_delta_min = - RTE_MIN(stat->stat_rtp_ts_delta_min, slot->rtp_ts_delta); + RTE_MIN(stat->stat_rtp_ts_delta_min, slot->meta.rtp_ts_delta); stat->stat_rtp_ts_delta_max = - RTE_MAX(stat->stat_rtp_ts_delta_max, slot->rtp_ts_delta); - stat->stat_rtp_ts_delta_sum += slot->rtp_ts_delta; + RTE_MAX(stat->stat_rtp_ts_delta_max, slot->meta.rtp_ts_delta); + stat->stat_rtp_ts_delta_sum += slot->meta.rtp_ts_delta; } stat->stat_frame_cnt++; } @@ -180,15 +224,15 @@ void rv_tp_stat(struct st_rx_video_session_impl* s) { 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->pkt_cnt, stat_slot->cinst_sum); - float vrx_avg = rv_tp_calculate_avg(stat_slot->pkt_cnt, stat_slot->vrx_sum); - float ipt_avg = rv_tp_calculate_avg(stat_slot->pkt_cnt, stat_slot->ipt_sum); + 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->cinst_min, stat_slot->cinst_max); + 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->vrx_min, stat_slot->vrx_max); + 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->ipt_min, stat_slot->ipt_max); + 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); @@ -209,12 +253,12 @@ void rv_tp_stat(struct st_rx_video_session_impl* s) { void rv_tp_slot_init(struct st_rv_tp_slot* slot) { memset(slot, 0, sizeof(*slot)); - slot->cinst_max = INT_MIN; - slot->cinst_min = INT_MAX; - slot->vrx_max = INT_MIN; - slot->vrx_min = INT_MAX; - slot->ipt_max = INT_MIN; - slot->ipt_min = INT_MAX; + slot->meta.cinst_max = INT_MIN; + slot->meta.cinst_min = INT_MAX; + slot->meta.vrx_max = INT_MIN; + slot->meta.vrx_min = INT_MAX; + slot->meta.ipt_max = INT_MIN; + slot->meta.ipt_min = INT_MAX; } int rv_tp_uinit(struct st_rx_video_session_impl* s) { @@ -263,38 +307,45 @@ int rv_tp_init(struct mtl_main_impl* impl, struct st_rx_video_session_impl* s) { if (ops->interlaced && ops->height <= 576) { reactive = (ops->height == 480) ? 487.0 / 525.0 : 576.0 / 625.0; } - tp->trs = frame_time * reactive / st20_total_pkts; if (!ops->interlaced) { - tp->tr_offset = + tp->pass.tr_offset = ops->height >= 1080 ? frame_time * (43.0 / 1125.0) : frame_time * (28.0 / 750.0); } else { if (ops->height == 480) { - tp->tr_offset = frame_time * (20.0 / 525.0) * 2; + tp->pass.tr_offset = frame_time * (20.0 / 525.0) * 2; } else if (ops->height == 576) { - tp->tr_offset = frame_time * (26.0 / 625.0) * 2; + tp->pass.tr_offset = frame_time * (26.0 / 625.0) * 2; } else { - tp->tr_offset = frame_time * (22.0 / 1125.0) * 2; + tp->pass.tr_offset = frame_time * (22.0 / 1125.0) * 2; } } - tp->c_max_narrow_pass = + tp->pass.cinst_max_narrow = RTE_MAX(4, (double)st20_total_pkts / (43200 * reactive * frame_time_s)); - tp->c_max_wide_pass = RTE_MAX(16, (double)st20_total_pkts / (21600 * frame_time_s)); - tp->vrx_full_narrow_pass = RTE_MAX(8, st20_total_pkts / (27000 * frame_time_s)); - tp->vrx_full_wide_pass = RTE_MAX(720, st20_total_pkts / (300 * frame_time_s)); - tp->rtp_offset_max_pass = - ceil((tp->tr_offset / NS_PER_S) * fps_tm.sampling_clock_rate) + 1; + tp->pass.cinst_max_wide = RTE_MAX(16, (double)st20_total_pkts / (21600 * frame_time_s)); + tp->pass.cinst_min = 0; + tp->pass.vrx_max_narrow = RTE_MAX(8, st20_total_pkts / (27000 * frame_time_s)); + tp->pass.vrx_max_wide = RTE_MAX(720, st20_total_pkts / (300 * frame_time_s)); + tp->pass.vrx_min = 0; + tp->pass.latency_max = 1000 * 1000; /* 1000 us */ + tp->pass.latency_min = 0; + tp->pass.rtp_offset_max = + ceil((double)tp->pass.tr_offset * fps_tm.sampling_clock_rate / NS_PER_S) + 1; + tp->pass.rtp_offset_min = -1; + int32_t sampling = s->frame_time_sampling; + tp->pass.rtp_ts_delta_max = sampling + 1; + tp->pass.rtp_ts_delta_min = sampling; rv_tp_stat_init(tp); - info("%s[%02d], trs %f tr offset %f sampling %f\n", __func__, idx, tp->trs, - tp->tr_offset, s->frame_time_sampling); + info("%s[%02d], trs %f tr offset %d sampling %f\n", __func__, idx, tp->trs, + tp->pass.tr_offset, s->frame_time_sampling); info( - "%s[%02d], cmax_narrow %d cmax_wide %d vrx_full_narrow %d vrx_full_wide %d " + "%s[%02d], cinst_max_narrow %d cinst_max_wide %d vrx_max_narrow %d vrx_max_wide %d " "rtp_offset_max %d\n", - __func__, idx, tp->c_max_narrow_pass, tp->c_max_wide_pass, tp->vrx_full_narrow_pass, - tp->vrx_full_wide_pass, tp->rtp_offset_max_pass); + __func__, idx, tp->pass.cinst_max_narrow, tp->pass.cinst_max_wide, + tp->pass.vrx_max_narrow, tp->pass.vrx_max_wide, tp->pass.rtp_offset_max); return 0; } diff --git a/lib/src/st2110/st_rx_video_session.c b/lib/src/st2110/st_rx_video_session.c index 273b24910..fc1768072 100644 --- a/lib/src/st2110/st_rx_video_session.c +++ b/lib/src/st2110/st_rx_video_session.c @@ -713,9 +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) { - /* todo: share the timing data to st20_rx_frame_meta */ 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; + } } dbg("%s(%d), start\n", __func__, s->idx); @@ -2991,9 +2994,15 @@ static int rv_attach(struct mtl_main_impl* impl, struct st_rx_video_sessions_mgr s->st22_expect_frame_size = 0; s->burst_loss_cnt = 0; - if (s->ops.flags & ST20_RX_FLAG_ENABLE_TIMING_PARSER) { - info("%s(%d), enable the timing analyze\n", __func__, idx); + if (s->ops.flags & ST20_RX_FLAG_TIMING_PARSER_STAT) { + info("%s(%d), enable the timing analyze stat\n", __func__, idx); s->enable_timing_parser = true; + s->enable_timing_parser_stat = true; + } + if (s->ops.flags & ST20_RX_FLAG_TIMING_PARSER_META) { + info("%s(%d), enable the timing analyze meta\n", __func__, idx); + s->enable_timing_parser = true; + s->enable_timing_parser_meta = true; } ret = rv_init_hw(impl, s); @@ -3340,7 +3349,7 @@ static void rv_stat(struct st_rx_video_sessions_mgr* mgr, s->stat_st22_boxes); s->stat_st22_boxes = 0; } - if (s->enable_timing_parser) rv_tp_stat(s); + if (s->enable_timing_parser_stat) rv_tp_stat(s); if (s->time_measure) { struct mt_stat_u64* stat_time = &s->stat_time; @@ -4171,6 +4180,29 @@ int st20_rx_get_queue_meta(st20_rx_handle handle, struct st_queue_meta* meta) { return 0; } +int st20_rx_timing_parser_critical(st20_rx_handle handle, struct st20_rx_tp_pass* pass) { + struct st_rx_video_session_handle_impl* s_impl = handle; + + if (s_impl->type != MT_HANDLE_RX_VIDEO) { + err("%s, invalid type %d\n", __func__, s_impl->type); + return -EIO; + } + + struct st_rx_video_session_impl* s = s_impl->impl; + int idx = s->idx; + if (!s->enable_timing_parser) { + err("%s(%d), timing parser control not enabled\n", __func__, idx); + return -EIO; + } + if (!s->tp) { + err("%s(%d), timing parser info not init\n", __func__, idx); + return -EBUSY; + } + + mtl_memcpy(pass, &s->tp->pass, sizeof(*pass)); + return 0; +} + st22_rx_handle st22_rx_create(mtl_handle mt, struct st22_rx_ops* ops) { struct mtl_main_impl* impl = mt; struct mtl_sch_impl* sch; diff --git a/tests/src/st20p_test.cpp b/tests/src/st20p_test.cpp index 52816488b..126e8d27a 100644 --- a/tests/src/st20p_test.cpp +++ b/tests/src/st20p_test.cpp @@ -457,6 +457,9 @@ static void test_st20p_rx_frame_thread(void* args) { dbg("%s(%d), timestamp %" PRIu64 "\n", __func__, s->idx, frame->timestamp); if (frame->timestamp == timestamp) s->incomplete_frame_cnt++; timestamp = frame->timestamp; + if (s->rx_timing_parser) { + if (!frame->tp) s->incomplete_frame_cnt++; + } /* check user timestamp if it has */ if (s->user_timestamp && !s->user_pacing) { @@ -617,6 +620,7 @@ struct st20p_rx_digest_test_para { enum st21_pacing pacing; uint32_t ssrc; bool block_get; + bool rx_timing_parser; }; static void test_st20p_init_rx_digest_para(struct st20p_rx_digest_test_para* para) { @@ -899,6 +903,7 @@ static void st20p_rx_digest_test(enum st_fps fps[], int width[], int height[], test_ctx_rx[i]->user_timestamp = para->user_timestamp; test_ctx_rx[i]->user_meta = para->user_meta; test_ctx_rx[i]->block_get = para->block_get; + test_ctx_rx[i]->rx_timing_parser = para->rx_timing_parser; test_ctx_rx[i]->frame_size = st_frame_size(rx_fmt[i], width[i], height[i], para->interlace); /* copy sha */ @@ -975,6 +980,7 @@ static void st20p_rx_digest_test(enum st_fps fps[], int width[], int height[], ops_rx.flags |= ST20P_RX_FLAG_BLOCK_GET; else ops_rx.notify_frame_available = test_st20p_rx_frame_available; + if (para->rx_timing_parser) ops_rx.flags |= ST20P_RX_FLAG_TIMING_PARSER_META; ops_rx.notify_event = test_ctx_notify_event; if (para->rx_ext) { if (para->rx_dedicated_ext) { @@ -1126,8 +1132,9 @@ TEST(St20p, digest_1080p_s1) { struct st20p_rx_digest_test_para para; test_st20p_init_rx_digest_para(¶); - para.level = ST_TEST_LEVEL_ALL; + para.level = ST_TEST_LEVEL_MANDATORY; para.check_fps = false; + para.rx_timing_parser = true; st20p_rx_digest_test(fps, width, height, tx_fmt, t_fmt, rx_fmt, ¶); } diff --git a/tests/src/tests.h b/tests/src/tests.h index 67cfc644f..e705cff0b 100644 --- a/tests/src/tests.h +++ b/tests/src/tests.h @@ -225,6 +225,7 @@ class tests_context { uint64_t ptp_time_first_frame = 0; bool user_meta = false; bool block_get = false; + bool rx_timing_parser = false; }; #define TEST_USER_META_MAGIC ST_PLUGIN_MAGIC('U', 'S', 'M', 'T')