Skip to content

Commit 88c3566

Browse files
authored
Add: Remove unnecessary alignment of interlaced frames (#1266)
- Remove unnecessary alignment of first fields to even N*frame_time and second fields to odd N*frame_time. This caused extra latency in interlaced streams. Problems with user_pacing and is not required by SMPTE spec. - expose user_timestamp as json parameter for RxTxApp - Align timestamps created by RxTxApp to N*frame_time - Add small fix to the lib: For user_pacing now it aligns to the closest N*frame_time instead of the last. --------- Signed-off-by: Kasiewicz, Marek <[email protected]>
1 parent 88a0777 commit 88c3566

File tree

13 files changed

+118
-112
lines changed

13 files changed

+118
-112
lines changed

config/tx_1v_user_pacing.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
"input_format": "YUV422PLANAR10LE",
2626
"transport_format": "YUV_422_10bit",
2727
"st20p_url": "./yuv422p10le_1080p.yuv",
28+
"user_timestamp": true,
2829
"user_pacing": true
2930
}
3031
],
31-
"user_pacing_offset" : 20000000
32+
"user_time_offset" : 20000000
3233
}
3334
]
3435
}

doc/design.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ Consequently, the application will receive notifications through `notify_event`
431431
432432
By default, applications simply push frames into MTL TX sessions, where the MTL TX session automatically assigns timestamps and epochs based on timing.
433433
434-
The ST**_TX_FLAG_USER_TIMESTAMP flag allows applications to assign the RTP timestamp for each frame. MTL retrieves the timestamp from st**_tx_frame_meta and delays the frame transmission until the PTP reaches this timestamp. As a result, applications must calculate the timestamp with precision.
434+
The ST**_TX_FLAG_USER_TIMESTAMP flag allows applications to assign the RTP timestamp for each frame. MTL retrieves the timestamp from st**_tx_frame_meta and calculates RTP timestamp straight from this value.
435+
Transmission time in unchanged.
435436
436437
The ST**_TX_FLAG_USER_TIMESTAMP flag is provided to enable applications to use their own timestamp values for each frame. Consequently, the RTP timestamp for all packets that belong to that frame will be assigned the customized value.
437438

lib/src/st2110/st_tx_ancillary_session.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ static int tx_ancillary_session_sync_pacing(struct mtl_main_impl* impl,
270270

271271
if (required_tai) {
272272
uint64_t ptp_epochs = ptp_time / frame_time;
273-
epochs = required_tai / frame_time;
273+
epochs = (required_tai + frame_time / 2) / frame_time;
274274
dbg("%s(%d), required tai %" PRIu64 " ptp_epochs %" PRIu64 " epochs %" PRIu64 "\n",
275275
__func__, s->idx, required_tai, ptp_epochs, epochs);
276276
if (epochs < ptp_epochs) {
@@ -291,11 +291,9 @@ static int tx_ancillary_session_sync_pacing(struct mtl_main_impl* impl,
291291
}
292292

293293
if (interlaced) {
294-
if (second_field) { /* align to odd epoch */
295-
if (!(epochs & 0x1)) epochs++;
294+
if (second_field) {
296295
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
297-
} else { /* align to even epoch */
298-
if (epochs & 0x1) epochs++;
296+
} else {
299297
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
300298
}
301299
}

lib/src/st2110/st_tx_audio_session.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ static int tx_audio_session_sync_pacing(struct mtl_main_impl* impl,
275275

276276
if (required_tai) {
277277
ptp_epochs = ptp_time / pkt_time;
278-
epochs = required_tai / pkt_time;
278+
epochs = (required_tai + pkt_time / 2) / pkt_time;
279279
if (epochs < ptp_epochs) {
280280
ST_SESSION_STAT_INC(s, port_user_stats.common, stat_error_user_timestamp);
281281
dbg("%s(%d), required tai %" PRIu64 " ptp_epochs %" PRIu64 " epochs %" PRIu64 "\n",

lib/src/st2110/st_tx_fastmetadata_session.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ static int tx_fastmetadata_session_sync_pacing(struct mtl_main_impl* impl,
270270

271271
if (required_tai) {
272272
uint64_t ptp_epochs = ptp_time / frame_time;
273-
epochs = required_tai / frame_time;
273+
epochs = (required_tai + frame_time / 2) / frame_time;
274274
dbg("%s(%d), required tai %" PRIu64 " ptp_epochs %" PRIu64 " epochs %" PRIu64 "\n",
275275
__func__, s->idx, required_tai, ptp_epochs, epochs);
276276
if (epochs < ptp_epochs) {
@@ -291,11 +291,9 @@ static int tx_fastmetadata_session_sync_pacing(struct mtl_main_impl* impl,
291291
}
292292

293293
if (interlaced) {
294-
if (second_field) { /* align to odd epoch */
295-
if (!(epochs & 0x1)) epochs++;
294+
if (second_field) {
296295
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
297-
} else { /* align to even epoch */
298-
if (epochs & 0x1) epochs++;
296+
} else {
299297
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
300298
}
301299
}

lib/src/st2110/st_tx_video_session.c

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -570,29 +570,15 @@ static int tv_init_pacing_epoch(struct mtl_main_impl* impl,
570570
return 0;
571571
}
572572

573-
static inline uint64_t align_for_interlaced(struct st_tx_video_session_impl* s,
574-
uint64_t frame_count, bool second_field) {
575-
if (second_field) { /* align to odd epoch */
576-
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
577-
if (!(frame_count & 0x1)) frame_count++;
578-
} else { /* align to even epoch */
579-
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
580-
if (frame_count & 0x1) frame_count++;
581-
}
582-
return frame_count;
583-
}
584-
585573
static inline uint64_t calc_frame_count_since_epoch(struct st_tx_video_session_impl* s,
586574
uint64_t cur_tai,
587-
uint64_t required_tai,
588-
bool second_field) {
575+
uint64_t required_tai) {
589576
uint64_t frame_count_tai = cur_tai / s->pacing.frame_time;
590577
uint64_t next_free_frame_slot = s->pacing.cur_epochs + 1;
591-
bool interlaced = s->ops.interlaced;
592578
uint64_t frame_count;
593579

594580
if (required_tai) {
595-
frame_count = required_tai / s->pacing.frame_time;
581+
frame_count = (required_tai + s->pacing.frame_time / 2) / s->pacing.frame_time;
596582
} else {
597583
if (frame_count_tai <= next_free_frame_slot) {
598584
/* There is time buffer until the next available frame time window */
@@ -618,11 +604,6 @@ static inline uint64_t calc_frame_count_since_epoch(struct st_tx_video_session_i
618604
frame_count = frame_count_tai;
619605
}
620606
}
621-
622-
if (interlaced) {
623-
frame_count = align_for_interlaced(s, frame_count, second_field);
624-
}
625-
626607
return frame_count;
627608
}
628609

@@ -645,7 +626,7 @@ static inline uint64_t validate_and_adjust_user_timestamp(
645626
}
646627

647628
static int tv_sync_pacing(struct mtl_main_impl* impl, struct st_tx_video_session_impl* s,
648-
uint64_t required_tai, bool second_field) {
629+
uint64_t required_tai) {
649630
struct st_tx_video_pacing* pacing = &s->pacing;
650631
uint64_t cur_tai = mt_get_ptp_time(impl, MTL_PORT_P);
651632
uint64_t cur_tsc = mt_get_tsc(impl);
@@ -656,8 +637,7 @@ static int tv_sync_pacing(struct mtl_main_impl* impl, struct st_tx_video_session
656637
required_tai = validate_and_adjust_user_timestamp(s, required_tai, cur_tai);
657638
}
658639

659-
pacing->cur_epochs =
660-
calc_frame_count_since_epoch(s, cur_tai, required_tai, second_field);
640+
pacing->cur_epochs = calc_frame_count_since_epoch(s, cur_tai, required_tai);
661641
if (s->ops.flags & ST20_TX_FLAG_EXACT_USER_PACING) {
662642
start_time_tai = required_tai;
663643
} else {
@@ -685,12 +665,12 @@ static int tv_sync_pacing(struct mtl_main_impl* impl, struct st_tx_video_session
685665

686666
static int tv_sync_pacing_st22(struct mtl_main_impl* impl,
687667
struct st_tx_video_session_impl* s, uint64_t required_tai,
688-
bool second_field, int pkts_in_frame) {
668+
int pkts_in_frame) {
689669
struct st_tx_video_pacing* pacing = &s->pacing;
690670
/* reset trs */
691671
pacing->trs = pacing->frame_time * pacing->reactive / pkts_in_frame;
692672
dbg("%s(%d), trs %f\n", __func__, s->idx, pacing->trs);
693-
return tv_sync_pacing(impl, s, required_tai, second_field);
673+
return tv_sync_pacing(impl, s, required_tai);
694674
}
695675

696676
static int tv_init_next_meta(struct st_tx_video_session_impl* s,
@@ -1266,14 +1246,17 @@ static int tv_build_rtp(struct mtl_main_impl* impl, struct st_tx_video_session_i
12661246
s->port_user_stats.common.port[MTL_SESSION_PORT_P].frames++;
12671247
if (s->ops.num_port > 1) s->port_user_stats.common.port[MTL_SESSION_PORT_R].frames++;
12681248
s->st20_rtp_time = rtp->tmstamp;
1269-
bool second_field = false;
12701249
if (s->ops.interlaced) {
12711250
struct st20_rfc4175_rtp_hdr* rfc4175 = rte_pktmbuf_mtod_offset(
12721251
pkt, struct st20_rfc4175_rtp_hdr*, sizeof(struct mt_udp_hdr));
12731252
uint16_t line1_number = ntohs(rfc4175->row_number);
1274-
second_field = (line1_number & ST20_SECOND_FIELD) ? true : false;
1253+
if (line1_number & ST20_SECOND_FIELD) {
1254+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
1255+
} else {
1256+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
1257+
}
12751258
}
1276-
tv_sync_pacing(impl, s, 0, second_field);
1259+
tv_sync_pacing(impl, s, 0);
12771260
if (s->ops.flags & ST20_TX_FLAG_USER_TIMESTAMP) {
12781261
s->pacing.rtp_time_stamp = ntohl(rtp->tmstamp);
12791262
} else {
@@ -1332,14 +1315,17 @@ static int tv_build_rtp_chain(struct mtl_main_impl* impl,
13321315
s->port_user_stats.common.port[MTL_SESSION_PORT_P].frames++;
13331316
if (s->ops.num_port > 1) s->port_user_stats.common.port[MTL_SESSION_PORT_R].frames++;
13341317
s->st20_rtp_time = rtp->tmstamp;
1335-
bool second_field = false;
13361318
if (s->ops.interlaced) {
13371319
struct st20_rfc4175_rtp_hdr* rfc4175 =
13381320
rte_pktmbuf_mtod(pkt_chain, struct st20_rfc4175_rtp_hdr*);
13391321
uint16_t line1_number = ntohs(rfc4175->row_number);
1340-
second_field = (line1_number & ST20_SECOND_FIELD) ? true : false;
1322+
if (line1_number & ST20_SECOND_FIELD) {
1323+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
1324+
} else {
1325+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
1326+
}
13411327
}
1342-
tv_sync_pacing(impl, s, 0, second_field);
1328+
tv_sync_pacing(impl, s, 0);
13431329
if (s->ops.flags & ST20_TX_FLAG_USER_TIMESTAMP) {
13441330
s->pacing.rtp_time_stamp = ntohl(rtp->tmstamp);
13451331
} else {
@@ -1793,8 +1779,16 @@ static int tv_tasklet_frame(struct mtl_main_impl* impl,
17931779

17941780
/* user timestamp control if any */
17951781
uint64_t required_tai = tv_pacing_required_tai(s, meta.tfmt, meta.timestamp);
1796-
bool second_field = frame->tv_meta.second_field;
1797-
tv_sync_pacing(impl, s, required_tai, second_field);
1782+
if (s->ops.interlaced) {
1783+
if (frame->tv_meta.second_field) {
1784+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
1785+
} else {
1786+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
1787+
}
1788+
/* s->second_field is used to init the next frame */
1789+
s->second_field = !frame->tv_meta.second_field;
1790+
}
1791+
tv_sync_pacing(impl, s, required_tai);
17981792
if (ops->flags & ST20_TX_FLAG_USER_TIMESTAMP) {
17991793
pacing->rtp_time_stamp =
18001794
st10_get_media_clk(meta.tfmt, meta.timestamp, s->fps_tm.sampling_clock_rate);
@@ -1815,9 +1809,6 @@ static int tv_tasklet_frame(struct mtl_main_impl* impl,
18151809
frame->tv_meta.rtp_timestamp = pacing->rtp_time_stamp;
18161810
frame->tv_meta.epoch = pacing->cur_epochs;
18171811
/* init to next field */
1818-
if (ops->interlaced) {
1819-
s->second_field = second_field ? false : true;
1820-
}
18211812
MT_USDT_ST20_TX_FRAME_NEXT(s->mgr->idx, s->idx, next_frame_idx, frame->addr,
18221813
pacing->rtp_time_stamp);
18231814
/* check if dump USDT enabled */
@@ -2305,9 +2296,16 @@ static int tv_tasklet_st22(struct mtl_main_impl* impl,
23052296

23062297
/* user timestamp control if any */
23072298
uint64_t required_tai = tv_pacing_required_tai(s, meta.tfmt, meta.timestamp);
2308-
bool second_field = frame->tx_st22_meta.second_field;
2309-
tv_sync_pacing_st22(impl, s, required_tai, second_field,
2310-
st22_info->st22_total_pkts);
2299+
if (s->ops.interlaced) {
2300+
if (frame->tx_st22_meta.second_field) {
2301+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_second_field);
2302+
} else {
2303+
ST_SESSION_STAT_INC(s, port_user_stats, stat_interlace_first_field);
2304+
}
2305+
/* s->second_field is used to init the next frame */
2306+
s->second_field = !frame->tx_st22_meta.second_field;
2307+
}
2308+
tv_sync_pacing_st22(impl, s, required_tai, st22_info->st22_total_pkts);
23112309
if (ops->flags & ST20_TX_FLAG_USER_TIMESTAMP) {
23122310
pacing->rtp_time_stamp =
23132311
st10_get_media_clk(meta.tfmt, meta.timestamp, s->fps_tm.sampling_clock_rate);
@@ -2327,10 +2325,6 @@ static int tv_tasklet_st22(struct mtl_main_impl* impl,
23272325
frame->tx_st22_meta.timestamp = pacing->ptp_time_cursor;
23282326
frame->tx_st22_meta.epoch = pacing->cur_epochs;
23292327
frame->tx_st22_meta.rtp_timestamp = pacing->rtp_time_stamp;
2330-
/* init to next field */
2331-
if (ops->interlaced) {
2332-
s->second_field = second_field ? false : true;
2333-
}
23342328
MT_USDT_ST22_TX_FRAME_NEXT(s->mgr->idx, s->idx, next_frame_idx, frame->addr,
23352329
pacing->rtp_time_stamp, codestream_size);
23362330
/* check if dump USDT enabled */

tests/tools/RxTxApp/src/app_base.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
#define ST_APP_DEFAULT_FB_CNT (3)
5252

53-
#define ST_APP_USER_PACING_DEFAULT_OFFSET (10 * NS_PER_MS) /* 10ms */
53+
#define ST_APP_USER_CLOCK_DEFAULT_OFFSET (10 * NS_PER_MS) /* 10ms */
5454

5555
#define ST_APP_EXPECT_NEAR(val, expect, delta) \
5656
((val > (expect - delta)) && (val < (expect + delta)))
@@ -99,10 +99,10 @@ struct st_display {
9999
pthread_mutex_t display_frame_mutex;
100100
};
101101

102-
struct st_user_pacing {
102+
struct st_user_time {
103103
uint64_t base_tai_time;
104104
pthread_mutex_t base_tai_time_mutex;
105-
uint64_t user_pacing_offset;
105+
uint64_t user_time_offset;
106106
};
107107

108108
struct st_app_frameinfo {
@@ -521,9 +521,9 @@ struct st_app_tx_st20p_session {
521521
uint64_t last_stat_time_ns;
522522
bool sha_check;
523523
/* for now used only with user pacing to keep track of the frame timestamps */
524-
uint64_t frame_time;
524+
uint64_t frame_num;
525525
uint64_t local_tai_base_time;
526-
struct st_user_pacing* user_pacing;
526+
struct st_user_time* user_time;
527527

528528
char st20p_source_url[ST_APP_URL_MAX_LEN];
529529
uint8_t* st20p_source_begin;
@@ -581,10 +581,10 @@ struct st_app_tx_st30p_session {
581581
uint8_t num_port;
582582
uint64_t last_stat_time_ns;
583583
/* for now used only with user pacing to keep track of the frame timestamps */
584-
uint64_t frame_time;
584+
uint64_t frame_num;
585585
uint64_t packet_time;
586586
uint64_t local_tai_base_time;
587-
struct st_user_pacing* user_pacing;
587+
struct st_user_time* user_time;
588588

589589
char st30p_source_url[ST_APP_URL_MAX_LEN];
590590
uint8_t* st30p_source_begin;
@@ -757,7 +757,7 @@ struct st_app_context {
757757
char ttf_file[ST_APP_URL_MAX_LEN];
758758
int utc_offset;
759759

760-
struct st_user_pacing user_pacing;
760+
struct st_user_time user_time;
761761
};
762762

763763
static inline void* st_app_malloc(size_t sz) {
@@ -801,7 +801,7 @@ int st_set_mtl_log_file(struct st_app_context* ctx, const char* file);
801801

802802
void st_sha_dump(const char* tag, const unsigned char* sha);
803803

804-
uint64_t st_app_user_pacing_time(void* ctx, struct st_user_pacing* user_pacing,
805-
uint64_t frame_time, bool restart_base_time);
804+
uint64_t st_app_user_time(void* ctx, struct st_user_time* user_time, uint64_t frame_num,
805+
double frame_time, bool restart_base_time);
806806

807807
#endif

tests/tools/RxTxApp/src/args.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ enum st_args_cmd {
152152
ST_ARG_RSS_SCH_NB,
153153
ST_ARG_ALLOW_ACROSS_NUMA_CORE,
154154
ST_ARG_NO_MULTICAST,
155-
ST_ARG_TX_USER_PACING_OFFSET,
155+
ST_ARG_TX_USER_CLOCK_OFFSET,
156156
ST_ARG_MAX,
157157
};
158158

@@ -301,7 +301,7 @@ static struct option st_app_args_options[] = {
301301
{"rss_sch_nb", required_argument, 0, ST_ARG_RSS_SCH_NB},
302302
{"allow_across_numa_core", no_argument, 0, ST_ARG_ALLOW_ACROSS_NUMA_CORE},
303303
{"no_multicast", no_argument, 0, ST_ARG_NO_MULTICAST},
304-
{"tx_user_pacing_offset", required_argument, 0, ST_ARG_TX_USER_PACING_OFFSET},
304+
{"tx_user_time_offset", required_argument, 0, ST_ARG_TX_USER_CLOCK_OFFSET},
305305
{"timestamp_epoch", no_argument, 0, ST_ARG_TIMESTAMP_EPOCH},
306306

307307
{0, 0, 0, 0}};
@@ -930,8 +930,8 @@ int st_app_parse_args(struct st_app_context* ctx, struct mtl_init_params* p, int
930930
case ST_ARG_NO_MULTICAST:
931931
p->flags |= MTL_FLAG_NO_MULTICAST;
932932
break;
933-
case ST_ARG_TX_USER_PACING_OFFSET:
934-
ctx->user_pacing.user_pacing_offset = atoi(optarg);
933+
case ST_ARG_TX_USER_CLOCK_OFFSET:
934+
ctx->user_time.user_time_offset = atoi(optarg);
935935
break;
936936
case '?':
937937
break;

tests/tools/RxTxApp/src/parse_json.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,9 @@ static int st_json_parse_tx_st20p(int idx, json_object* st20p_obj,
20412041
st20p->exact_user_pacing =
20422042
json_object_get_boolean(st_json_object_object_get(st20p_obj, "exact_user_pacing"));
20432043

2044+
st20p->user_timestamp =
2045+
json_object_get_boolean(st_json_object_object_get(st20p_obj, "user_timestamp"));
2046+
20442047
return ST_JSON_SUCCESS;
20452048
}
20462049

@@ -2720,12 +2723,12 @@ int st_app_parse_json(st_json_context_t* ctx, const char* filename) {
27202723
}
27212724
}
27222725
}
2723-
/* parse global pacing offset options*/
2724-
json_object* pacing_obj = st_json_object_object_get(tx_group, "user_pacing_offset");
2726+
/* parse global clock offset options*/
2727+
json_object* pacing_obj = st_json_object_object_get(tx_group, "user_time_offset");
27252728
if (pacing_obj) {
2726-
ctx->user_pacing_offset = json_object_get_int(pacing_obj);
2729+
ctx->user_time_offset = json_object_get_int(pacing_obj);
27272730
} else {
2728-
ctx->user_pacing_offset = ST_APP_USER_PACING_DEFAULT_OFFSET;
2731+
ctx->user_time_offset = ST_APP_USER_CLOCK_DEFAULT_OFFSET;
27292732
}
27302733
}
27312734
}

tests/tools/RxTxApp/src/parse_json.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ typedef struct st_json_st20p_session {
277277
bool enable_rtcp;
278278
bool user_pacing;
279279
bool exact_user_pacing;
280-
uint64_t user_pacing_offset;
280+
bool user_timestamp;
281+
uint64_t user_time_offset;
281282
} st_json_st20p_session_t;
282283

283284
typedef struct st_json_context {
@@ -292,7 +293,7 @@ typedef struct st_json_context {
292293
bool shared_rx_queues;
293294
bool tx_no_chain;
294295
char* log_file;
295-
uint64_t user_pacing_offset;
296+
uint64_t user_time_offset;
296297

297298
st_json_video_session_t* tx_video_sessions;
298299
int tx_video_session_cnt;

0 commit comments

Comments
 (0)