@@ -105,6 +105,18 @@ typedef struct {
105
105
GstCaps * caps ;
106
106
} GstMtlSt20pTxThreadData ;
107
107
108
+ typedef struct {
109
+ GstBuffer * buf ;
110
+ uint32_t child_count ;
111
+ pthread_mutex_t parent_mutex ;
112
+ } GstSt20pTxExternalDataParent ;
113
+
114
+ typedef struct {
115
+ GstSt20pTxExternalDataParent * parent ;
116
+ GstMemory * gst_buffer_memory ;
117
+ GstMapInfo map_info ;
118
+ } GstSt20pTxExternalDataChild ;
119
+
108
120
/* pad template */
109
121
static GstStaticPadTemplate gst_mtl_st20p_tx_sink_pad_template =
110
122
GST_STATIC_PAD_TEMPLATE ("sink" , GST_PAD_SINK , GST_PAD_ALWAYS ,
@@ -137,6 +149,11 @@ static gboolean gst_mtl_st20p_tx_start(GstBaseSink* bsink);
137
149
138
150
static gboolean gst_mtl_st20p_tx_session_create (Gst_Mtl_St20p_Tx * sink , GstCaps * caps );
139
151
152
+ static int gst_mtl_st20p_tx_frame_done (void * priv , struct st_frame * frame );
153
+
154
+ static GstFlowReturn gst_mtl_st20p_tx_zero_copy (Gst_Mtl_St20p_Tx * sink , GstBuffer * buf );
155
+ static GstFlowReturn gst_mtl_st20p_tx_mem_copy (Gst_Mtl_St20p_Tx * sink , GstBuffer * buf );
156
+
140
157
static void gst_mtl_st20p_tx_class_init (Gst_Mtl_St20p_TxClass * klass ) {
141
158
GObjectClass * gobject_class ;
142
159
GstElementClass * gstelement_class ;
@@ -349,6 +366,14 @@ static gboolean gst_mtl_st20p_tx_session_create(Gst_Mtl_St20p_Tx* sink, GstCaps*
349
366
return FALSE;
350
367
}
351
368
369
+ sink -> zero_copy = (ops_tx .transport_fmt != st_frame_fmt_to_transport (ops_tx .input_fmt ));
370
+ if (sink -> zero_copy ) {
371
+ ops_tx .flags |= ST20P_TX_FLAG_EXT_FRAME ;
372
+ ops_tx .notify_frame_done = gst_mtl_st20p_tx_frame_done ;
373
+ } else {
374
+ GST_WARNING ("Using memcpy path" );
375
+ }
376
+
352
377
if (info -> fps_d != 0 ) {
353
378
ops_tx .fps = st_frame_rate_to_st_fps ((double )info -> fps_n / info -> fps_d );
354
379
if (ops_tx .fps == ST_FPS_MAX ) {
@@ -457,11 +482,6 @@ static gboolean gst_mtl_st20p_tx_sink_event(GstPad* pad, GstObject* parent,
457
482
static GstFlowReturn gst_mtl_st20p_tx_chain (GstPad * pad , GstObject * parent ,
458
483
GstBuffer * buf ) {
459
484
Gst_Mtl_St20p_Tx * sink = GST_MTL_ST20P_TX (parent );
460
- gint buffer_size , buffer_n = gst_buffer_n_memory (buf );
461
- struct st_frame * frame = NULL ;
462
- gint frame_size = sink -> frame_size ;
463
- GstMemory * gst_buffer_memory ;
464
- GstMapInfo map_info ;
465
485
466
486
if (sink -> async_session_create ) {
467
487
pthread_mutex_lock (& sink -> session_mutex );
@@ -480,6 +500,154 @@ static GstFlowReturn gst_mtl_st20p_tx_chain(GstPad* pad, GstObject* parent,
480
500
return GST_FLOW_ERROR ;
481
501
}
482
502
503
+ if (sink -> zero_copy ) {
504
+ return gst_mtl_st20p_tx_zero_copy (sink , buf );
505
+ } else {
506
+ return gst_mtl_st20p_tx_mem_copy (sink , buf );
507
+ }
508
+ }
509
+
510
+ static void gst_mtl_st20p_tx_finalize (GObject * object ) {
511
+ Gst_Mtl_St20p_Tx * sink = GST_MTL_ST20P_TX (object );
512
+
513
+ if (sink -> async_session_create ) {
514
+ if (sink -> session_thread ) pthread_join (sink -> session_thread , NULL );
515
+ pthread_mutex_destroy (& sink -> session_mutex );
516
+ }
517
+
518
+ if (sink -> tx_handle ) {
519
+ if (st20p_tx_free (sink -> tx_handle )) GST_ERROR ("Failed to free tx handle" );
520
+ }
521
+
522
+ if (sink -> mtl_lib_handle ) {
523
+ if (gst_mtl_common_deinit_handle (& sink -> mtl_lib_handle ))
524
+ GST_ERROR ("Failed to uninitialize MTL library" );
525
+ }
526
+ }
527
+
528
+ static gboolean plugin_init (GstPlugin * mtl_st20p_tx ) {
529
+ return gst_element_register (mtl_st20p_tx , "mtl_st20p_tx" , GST_RANK_SECONDARY ,
530
+ GST_TYPE_MTL_ST20P_TX );
531
+ }
532
+
533
+ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR , GST_VERSION_MINOR , mtl_st20p_tx ,
534
+ "software-based solution designed for high-throughput transmission" ,
535
+ plugin_init , PACKAGE_VERSION , GST_LICENSE , GST_PACKAGE_NAME ,
536
+ GST_PACKAGE_ORIGIN )
537
+
538
+ static int gst_mtl_st20p_tx_frame_done (void * priv , struct st_frame * frame ) {
539
+ /* In case of format conversion (transmit vs input), MTL may call
540
+ * gst_mtl_st20p_tx_frame_done twice.
541
+ * To avoid double free, we set (frame->opaque = NULL) in first call so that the second
542
+ * call can exit gracefully.
543
+ */
544
+ if (frame == NULL || frame -> opaque == NULL ) {
545
+ return 0 ;
546
+ }
547
+
548
+ GstSt20pTxExternalDataChild * child = frame -> opaque ;
549
+ GstSt20pTxExternalDataParent * parent = child -> parent ;
550
+
551
+ gst_memory_unmap (child -> gst_buffer_memory , & child -> map_info );
552
+
553
+ frame -> opaque = NULL ;
554
+ free (child );
555
+
556
+ pthread_mutex_lock (& parent -> parent_mutex );
557
+ parent -> child_count -- ;
558
+ if (parent -> child_count > 0 ) {
559
+ pthread_mutex_unlock (& parent -> parent_mutex );
560
+ return 0 ;
561
+ }
562
+
563
+ pthread_mutex_unlock (& parent -> parent_mutex );
564
+ gst_buffer_unref (parent -> buf );
565
+ pthread_mutex_destroy (& parent -> parent_mutex );
566
+ free (parent );
567
+
568
+ return 0 ;
569
+ }
570
+
571
+ static GstFlowReturn gst_mtl_st20p_tx_zero_copy (Gst_Mtl_St20p_Tx * sink , GstBuffer * buf ) {
572
+ GstSt20pTxExternalDataChild * child ;
573
+ GstSt20pTxExternalDataParent * parent ;
574
+ struct st_frame * frame ;
575
+ struct st_ext_frame ext_frame ;
576
+ GstVideoMeta * video_meta = gst_buffer_get_video_meta (buf );
577
+ gint buffer_n = gst_buffer_n_memory (buf );
578
+ if (!video_meta ) {
579
+ g_print ("Failed to get video meta from buffer\n" );
580
+ return GST_FLOW_ERROR ;
581
+ }
582
+
583
+ parent = malloc (sizeof (GstSt20pTxExternalDataParent ));
584
+ if (!parent ) {
585
+ GST_ERROR ("Failed to allocate memory for parent structure" );
586
+ return GST_FLOW_ERROR ;
587
+ }
588
+ parent -> buf = buf ;
589
+ parent -> child_count = buffer_n ;
590
+ pthread_mutex_init (& parent -> parent_mutex , NULL );
591
+
592
+ for (int i = 0 ; i < buffer_n ; i ++ ) {
593
+ child = malloc (sizeof (GstSt20pTxExternalDataChild ));
594
+ if (!child ) {
595
+ GST_ERROR ("Failed to allocate memory for child structure" );
596
+ free (parent );
597
+ }
598
+ child -> parent = parent ;
599
+ child -> gst_buffer_memory = gst_buffer_peek_memory (buf , i );
600
+
601
+ if (!gst_memory_map (child -> gst_buffer_memory , & child -> map_info , GST_MAP_READ )) {
602
+ GST_ERROR ("Failed to map memory" );
603
+ free (child );
604
+ free (parent );
605
+ return GST_FLOW_ERROR ;
606
+ }
607
+
608
+ if (child -> map_info .size < sink -> frame_size ) {
609
+ GST_ERROR ("Buffer size %lu is smaller than frame size %d" , child -> map_info .size ,
610
+ sink -> frame_size );
611
+ gst_memory_unmap (child -> gst_buffer_memory , & child -> map_info );
612
+ free (child );
613
+ free (parent );
614
+ return GST_FLOW_ERROR ;
615
+ }
616
+
617
+ frame = st20p_tx_get_frame (sink -> tx_handle );
618
+ if (!frame ) {
619
+ GST_ERROR ("Failed to get frame" );
620
+ return GST_FLOW_ERROR ;
621
+ }
622
+
623
+ // By default, timestamping is handled by MTL.
624
+ if (sink -> use_pts_for_pacing ) {
625
+ frame -> timestamp = GST_BUFFER_PTS (buf ) += sink -> pts_for_pacing_offset ;
626
+ frame -> tfmt = ST10_TIMESTAMP_FMT_TAI ;
627
+ }
628
+
629
+ for (int i = 0 ; i < video_meta -> n_planes ; i ++ ) {
630
+ ext_frame .addr [i ] = child -> map_info .data + video_meta -> offset [i ];
631
+ ext_frame .linesize [i ] = video_meta -> stride [i ];
632
+ ext_frame .iova [i ] = 0 ;
633
+ }
634
+ ext_frame .size = child -> map_info .size ;
635
+ ext_frame .opaque = child ;
636
+ frame -> opaque = NULL ;
637
+
638
+ st20p_tx_put_ext_frame (sink -> tx_handle , frame , & ext_frame );
639
+ }
640
+
641
+ return GST_FLOW_OK ;
642
+ }
643
+
644
+ static GstFlowReturn gst_mtl_st20p_tx_mem_copy (Gst_Mtl_St20p_Tx * sink , GstBuffer * buf ) {
645
+ gint buffer_size , buffer_n = gst_buffer_n_memory (buf );
646
+ struct st_frame * frame = NULL ;
647
+ gint frame_size = sink -> frame_size ;
648
+ GstMemory * gst_buffer_memory ;
649
+ GstMapInfo map_info ;
650
+
483
651
for (int i = 0 ; i < buffer_n ; i ++ ) {
484
652
gst_buffer_memory = gst_buffer_peek_memory (buf , i );
485
653
@@ -515,31 +683,3 @@ static GstFlowReturn gst_mtl_st20p_tx_chain(GstPad* pad, GstObject* parent,
515
683
gst_buffer_unref (buf );
516
684
return GST_FLOW_OK ;
517
685
}
518
-
519
- static void gst_mtl_st20p_tx_finalize (GObject * object ) {
520
- Gst_Mtl_St20p_Tx * sink = GST_MTL_ST20P_TX (object );
521
-
522
- if (sink -> async_session_create ) {
523
- if (sink -> session_thread ) pthread_join (sink -> session_thread , NULL );
524
- pthread_mutex_destroy (& sink -> session_mutex );
525
- }
526
-
527
- if (sink -> tx_handle ) {
528
- if (st20p_tx_free (sink -> tx_handle )) GST_ERROR ("Failed to free tx handle" );
529
- }
530
-
531
- if (sink -> mtl_lib_handle ) {
532
- if (gst_mtl_common_deinit_handle (& sink -> mtl_lib_handle ))
533
- GST_ERROR ("Failed to uninitialize MTL library" );
534
- }
535
- }
536
-
537
- static gboolean plugin_init (GstPlugin * mtl_st20p_tx ) {
538
- return gst_element_register (mtl_st20p_tx , "mtl_st20p_tx" , GST_RANK_SECONDARY ,
539
- GST_TYPE_MTL_ST20P_TX );
540
- }
541
-
542
- GST_PLUGIN_DEFINE (GST_VERSION_MAJOR , GST_VERSION_MINOR , mtl_st20p_tx ,
543
- "software-based solution designed for high-throughput transmission" ,
544
- plugin_init , PACKAGE_VERSION , GST_LICENSE , GST_PACKAGE_NAME ,
545
- GST_PACKAGE_ORIGIN )
0 commit comments