diff --git a/ecosystem/gstreamer_plugin/gst_mtl_common.c b/ecosystem/gstreamer_plugin/gst_mtl_common.c new file mode 100644 index 000000000..5a910d439 --- /dev/null +++ b/ecosystem/gstreamer_plugin/gst_mtl_common.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 Intel Corporation + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gst_mtl_common.h" + +gboolean gst_mtl_common_parse_input_finfo(const GstVideoFormatInfo* finfo, + enum st_frame_fmt* fmt) { + if (finfo->format == GST_VIDEO_FORMAT_v210) { + *fmt = ST_FRAME_FMT_V210; + } else if (finfo->format == GST_VIDEO_FORMAT_I422_10LE) { + *fmt = ST_FRAME_FMT_YUV422PLANAR10LE; + } else { + return FALSE; + } + + return TRUE; +} + +gboolean gst_mtl_common_parse_fps_code(gint fps_code, enum st_fps* fps) { + if (!fps) { + GST_ERROR("Invalid fps pointer"); + return FALSE; + } + + switch (fps_code) { + case GST_MTL_SUPPORTED_FPS_120: + *fps = ST_FPS_P120; + break; + case GST_MTL_SUPPORTED_FPS_119_88: + *fps = ST_FPS_P119_88; + break; + case GST_MTL_SUPPORTED_FPS_100: + *fps = ST_FPS_P100; + break; + case GST_MTL_SUPPORTED_FPS_60: + *fps = ST_FPS_P60; + break; + case GST_MTL_SUPPORTED_FPS_59_94: + *fps = ST_FPS_P59_94; + break; + case GST_MTL_SUPPORTED_FPS_50: + *fps = ST_FPS_P50; + break; + case GST_MTL_SUPPORTED_FPS_30: + *fps = ST_FPS_P30; + break; + case GST_MTL_SUPPORTED_FPS_29_97: + *fps = ST_FPS_P29_97; + break; + case GST_MTL_SUPPORTED_FPS_25: + *fps = ST_FPS_P25; + break; + case GST_MTL_SUPPORTED_FPS_24: + *fps = ST_FPS_P24; + break; + case GST_MTL_SUPPORTED_FPS_23_98: + *fps = ST_FPS_P23_98; + break; + default: + return FALSE; + } + + return TRUE; +} + +gboolean gst_mtl_common_parse_fps(GstVideoInfo* info, enum st_fps* fps) { + gint fps_div; + if (info->fps_n <= 0 || info->fps_d <= 0) { + return FALSE; + } + + fps_div = info->fps_n / info->fps_d; + + switch (fps_div) { + case 24: + *fps = ST_FPS_P24; + break; + case 25: + *fps = ST_FPS_P25; + break; + case 30: + *fps = ST_FPS_P30; + break; + case 50: + *fps = ST_FPS_P50; + break; + case 60: + *fps = ST_FPS_P60; + break; + case 120: + *fps = ST_FPS_P120; + break; + default: + return FALSE; + } + + return TRUE; +} + +/* includes all formats supported by the library for future support */ +gboolean gst_mtl_common_parse_pixel_format(const char* format, enum st_frame_fmt* fmt) { + if (!fmt || !format) { + GST_ERROR("%s, invalid input\n", __func__); + return FALSE; + } + + if (strcmp(format, "YUV422PLANAR10LE") == 0) { + *fmt = ST_FRAME_FMT_YUV422PLANAR10LE; + } else if (strcmp(format, "v210") == 0) { + *fmt = ST_FRAME_FMT_V210; + } else if (strcmp(format, "Y210") == 0) { + *fmt = ST_FRAME_FMT_Y210; + } else if (strcmp(format, "YUV422PLANAR8") == 0) { + *fmt = ST_FRAME_FMT_YUV422PLANAR8; + } else if (strcmp(format, "UYVY") == 0) { + *fmt = ST_FRAME_FMT_UYVY; + } else if (strcmp(format, "YUV422RFC4175PG2BE10") == 0) { + *fmt = ST_FRAME_FMT_YUV422RFC4175PG2BE10; + } else if (strcmp(format, "YUV422PLANAR12LE") == 0) { + *fmt = ST_FRAME_FMT_YUV422PLANAR12LE; + } else if (strcmp(format, "YUV422RFC4175PG2BE12") == 0) { + *fmt = ST_FRAME_FMT_YUV422RFC4175PG2BE12; + } else if (strcmp(format, "YUV444PLANAR10LE") == 0) { + *fmt = ST_FRAME_FMT_YUV444PLANAR10LE; + } else if (strcmp(format, "YUV444RFC4175PG4BE10") == 0) { + *fmt = ST_FRAME_FMT_YUV444RFC4175PG4BE10; + } else if (strcmp(format, "YUV444PLANAR12LE") == 0) { + *fmt = ST_FRAME_FMT_YUV444PLANAR12LE; + } else if (strcmp(format, "YUV444RFC4175PG2BE12") == 0) { + *fmt = ST_FRAME_FMT_YUV444RFC4175PG2BE12; + } else if (strcmp(format, "YUV420CUSTOM8") == 0) { + *fmt = ST_FRAME_FMT_YUV420CUSTOM8; + } else if (strcmp(format, "YUV422CUSTOM8") == 0) { + *fmt = ST_FRAME_FMT_YUV422CUSTOM8; + } else if (strcmp(format, "YUV420PLANAR8") == 0) { + *fmt = ST_FRAME_FMT_YUV420PLANAR8; + } else if (strcmp(format, "ARGB") == 0) { + *fmt = ST_FRAME_FMT_ARGB; + } else if (strcmp(format, "BGRA") == 0) { + *fmt = ST_FRAME_FMT_BGRA; + } else if (strcmp(format, "RGB8") == 0) { + *fmt = ST_FRAME_FMT_RGB8; + } else if (strcmp(format, "GBRPLANAR10LE") == 0) { + *fmt = ST_FRAME_FMT_GBRPLANAR10LE; + } else if (strcmp(format, "RGBRFC4175PG4BE10") == 0) { + *fmt = ST_FRAME_FMT_RGBRFC4175PG4BE10; + } else if (strcmp(format, "GBRPLANAR12LE") == 0) { + *fmt = ST_FRAME_FMT_GBRPLANAR12LE; + } else if (strcmp(format, "RGBRFC4175PG2BE12") == 0) { + *fmt = ST_FRAME_FMT_RGBRFC4175PG2BE12; + } else { + GST_ERROR("invalid output format %s\n", format); + return FALSE; + } + + return TRUE; +} diff --git a/ecosystem/gstreamer_plugin/gst_mtl_common.h b/ecosystem/gstreamer_plugin/gst_mtl_common.h new file mode 100644 index 000000000..c90a1fc92 --- /dev/null +++ b/ecosystem/gstreamer_plugin/gst_mtl_common.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Intel Corporation + */ + +#ifndef __GST_MTL_COMMON_H__ +#define __GST_MTL_COMMON_H__ + +#include +#include +#include +#include +#include + +enum gst_mtl_supported_fps { + GST_MTL_SUPPORTED_FPS_23_98 = 2398, + GST_MTL_SUPPORTED_FPS_24 = 24, + GST_MTL_SUPPORTED_FPS_25 = 25, + GST_MTL_SUPPORTED_FPS_29_97 = 2997, + GST_MTL_SUPPORTED_FPS_30 = 30, + GST_MTL_SUPPORTED_FPS_50 = 50, + GST_MTL_SUPPORTED_FPS_59_94 = 5994, + GST_MTL_SUPPORTED_FPS_60 = 60, + GST_MTL_SUPPORTED_FPS_100 = 100, + GST_MTL_SUPPORTED_FPS_119_88 = 11988, + GST_MTL_SUPPORTED_FPS_120 = 120 +}; + +typedef struct StDevArgs { + gchar port[MTL_PORT_MAX_LEN]; + gchar local_ip_string[MTL_PORT_MAX_LEN]; + gint tx_queues_cnt[MTL_PORT_MAX]; + gint rx_queues_cnt[MTL_PORT_MAX]; + gchar dma_dev[MTL_PORT_MAX_LEN]; +} StDevArgs; + +typedef struct SessionPortArgs { + gchar session_ip_string[MTL_PORT_MAX_LEN]; + gchar port[MTL_PORT_MAX_LEN]; + gint udp_port; + gint payload_type; +} SessionPortArgs; + +gboolean gst_mtl_common_parse_input_finfo(const GstVideoFormatInfo* finfo, + enum st_frame_fmt* fmt); +gboolean gst_mtl_common_parse_fps(GstVideoInfo* info, enum st_fps* fps); +gboolean gst_mtl_common_parse_fps_code(gint fps_code, enum st_fps* fps); +gboolean gst_mtl_common_parse_pixel_format(const char* format, enum st_frame_fmt* fmt); + +#endif /* __GST_MTL_COMMON_H__ */ \ No newline at end of file diff --git a/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.c b/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.c new file mode 100644 index 000000000..70a5b4b18 --- /dev/null +++ b/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.c @@ -0,0 +1,670 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2024 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-mtl_rx_src + * + * The mtl_rx_src element is a GStreamer src plugin designed to interface with + * the Media Transport Library (MTL). + * MTL is a software-based solution optimized for high-throughput, low-latency + * transmission and reception of media data. + * + * It features an efficient user-space LibOS UDP stack specifically crafted for + * media transport and includes a built-in SMPTE ST 2110-compliant + * implementation for Professional Media over Managed IP Networks. + * + * This element allows GStreamer pipelines to recive media data using the MTL + * framework, ensuring efficient and reliable media transport over IP networks. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "gst_mtl_st20p_rx.h" + +GST_DEBUG_CATEGORY_STATIC(gst_mtl_st20p_rx_debug); +#define GST_CAT_DEFAULT gst_mtl_st20p_rx_debug +#ifndef GST_LICENSE +#define GST_LICENSE "LGPL" +#endif +#ifndef GST_API_VERSION +#define GST_API_VERSION "1.0" +#endif +#ifndef GST_PACKAGE_NAME +#define GST_PACKAGE_NAME "Media Transport Library st2110 st20 rx plugin" +#endif +#ifndef GST_PACKAGE_ORIGIN +#define GST_PACKAGE_ORIGIN "https://github.com/OpenVisualCloud/Media-Transport-Library" +#endif +#ifndef PACKAGE +#define PACKAGE "gst-mtl-st20p-rx" +#endif +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION "1.0" +#endif + +enum { + PROP_0, + PROP_SILENT, + PROP_RX_DEV_ARGS_PORT, + PROP_RX_DEV_ARGS_SIP, + PROP_RX_DEV_ARGS_DMA_DEV, + PROP_RX_PORT_PORT, + PROP_RX_PORT_IP, + PROP_RX_PORT_UDP_PORT, + PROP_RX_PORT_PAYLOAD_TYPE, + PROP_RX_PORT_RX_QUEUES, + PROP_RX_FRAMERATE, + PROP_RX_FRAMEBUFF_NUM, + PROP_RX_WIDTH, + PROP_RX_HEIGHT, + PROP_RX_INTERLACED, + PROP_RX_PIXEL_FORMAT, + PROP_MAX +}; + +/* pad template */ +static GstStaticPadTemplate gst_mtl_st20p_rx_src_pad_template = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-raw, " + "format = (string) {v210, I422_10LE}," + "width = (int) [64, 16384], " + "height = (int) [64, 8704], " + "framerate = (fraction) [0, MAX]")); + +#define gst_mtl_st20p_rx_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE(Gst_Mtl_St20p_Rx, gst_mtl_st20p_rx, GST_TYPE_BASE_SRC, + GST_DEBUG_CATEGORY_INIT(gst_mtl_st20p_rx_debug, "mtl_st20p_rx", 0, + "MTL St2110 st20 transmission src")); + +GST_ELEMENT_REGISTER_DEFINE(mtl_st20p_rx, "mtl_st20p_rx", GST_RANK_NONE, + GST_TYPE_MTL_ST20P_RX); + +static void gst_mtl_st20p_rx_set_property(GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec); +static void gst_mtl_st20p_rx_get_property(GObject* object, guint prop_id, GValue* value, + GParamSpec* pspec); +static void gst_mtl_st20p_rx_finalize(GObject* object); + +static gboolean gst_mtl_st20p_rx_start(GstBaseSrc* basesrc); +static gboolean gst_mtl_st20p_rx_stop(GstBaseSrc* basesrc); +static gboolean gst_mtl_st20p_rx_negotiate(GstBaseSrc* basesrc); +static GstFlowReturn gst_mtl_st20p_rx_create(GstBaseSrc* basesrc, guint64 offset, + guint length, GstBuffer** buffer); + +static void gst_mtl_st20p_rx_class_init(Gst_Mtl_St20p_RxClass* klass) { + GObjectClass* gobject_class; + GstElementClass* gstelement_class; + GstBaseSrcClass* gstbasesrc_class; + + gobject_class = G_OBJECT_CLASS(klass); + gstelement_class = GST_ELEMENT_CLASS(klass); + gstbasesrc_class = GST_BASE_SRC_CLASS(klass); + + gst_element_class_set_metadata( + gstelement_class, "MtlRxSt20Src", "Src/Video", + "MTL transmission plugin for SMPTE ST 2110-20 standard (uncompressed video)", + "Dawid Wesierski "); + + gst_element_class_add_static_pad_template(gstelement_class, + &gst_mtl_st20p_rx_src_pad_template); + + gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_get_property); + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_finalize); + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_stop); + gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_negotiate); + gstbasesrc_class->create = GST_DEBUG_FUNCPTR(gst_mtl_st20p_rx_create); + + g_object_class_install_property( + gobject_class, PROP_SILENT, + g_param_spec_boolean("silent", "Silent", "Turn on silent mode.", FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, PROP_RX_DEV_ARGS_PORT, + g_param_spec_string("dev-port", "DPDK device port", + "DPDK port for synchronous ST 2110-20 uncompressed" + "video transmission, bound to the VFIO DPDK driver. ", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_DEV_ARGS_SIP, + g_param_spec_string("dev-ip", "Local device IP", + "Local IP address that the port will be " + "identified by. This is the address from which ARP " + "responses will be sent.", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_DEV_ARGS_DMA_DEV, + g_param_spec_string("dma-dev", "DPDK DMA port", + "DPDK port for the MTL direct memory functionality.", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PORT_PORT, + g_param_spec_string("rx-port", "Transmission Device Port", + "DPDK device port initialized for the transmission.", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PORT_IP, + g_param_spec_string("rx-ip", "Sender node's IP", "Receiving MTL node IP address.", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PORT_UDP_PORT, + g_param_spec_uint("rx-udp-port", "Sender UDP port", "Receiving MTL node UDP port.", + 0, G_MAXUINT, 20000, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PORT_PAYLOAD_TYPE, + g_param_spec_uint("rx-payload-type", "ST 2110 payload type", + "SMPTE ST 2110 payload type.", 0, G_MAXUINT, 112, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PORT_RX_QUEUES, + g_param_spec_uint("rx-queues", "Number of RX queues", + "Number of RX queues to initialize in DPDK backend.", 0, + G_MAXUINT, 16, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_FRAMERATE, + g_param_spec_uint("rx-fps", "Video framerate", "Framerate of the video.", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_FRAMEBUFF_NUM, + g_param_spec_uint("rx-framebuff-num", "Number of framebuffers", + "Number of framebuffers to be used for transmission.", 0, + G_MAXUINT, 3, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_WIDTH, + g_param_spec_uint("rx-width", "Video width", "Width of the video.", 0, G_MAXUINT, + 1920, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_HEIGHT, + g_param_spec_uint("rx-height", "Video height", "Height of the video.", 0, G_MAXUINT, + 1080, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_INTERLACED, + g_param_spec_boolean("rx-interlaced", "Interlaced video", + "Whether the video is interlaced.", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_RX_PIXEL_FORMAT, + g_param_spec_string("rx-pixel-format", "Pixel format", "Pixel format of the video.", + "v210", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static gboolean gst_mtl_st20p_rx_start(GstBaseSrc* basesrc) { + struct mtl_init_params mtl_init_params = {0}; + struct st20p_rx_ops* ops_rx; + gint ret; + + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(basesrc); + ops_rx = &src->ops_rx; + + GST_DEBUG_OBJECT(src, "start"); + GST_DEBUG("Media Transport Initialization start"); + + /* mtl is already initialzied */ + if (src->mtl_lib_handle) { + GST_INFO("Mtl already initialized"); + if (mtl_start(src->mtl_lib_handle)) { + GST_ERROR("Failed to start MTL"); + return FALSE; + } + return TRUE; + } else { + strncpy(mtl_init_params.port[MTL_PORT_P], src->devArgs.port, MTL_PORT_MAX_LEN); + + ret = inet_pton(AF_INET, src->devArgs.local_ip_string, + mtl_init_params.sip_addr[MTL_PORT_P]); + if (ret != 1) { + GST_ERROR("%s, sip %s is not valid ip address\n", __func__, + src->devArgs.local_ip_string); + return FALSE; + } + + if (src->devArgs.rx_queues_cnt[MTL_PORT_P]) { + mtl_init_params.rx_queues_cnt[MTL_PORT_P] = src->devArgs.rx_queues_cnt[MTL_PORT_P]; + } else { + mtl_init_params.rx_queues_cnt[MTL_PORT_P] = 16; + mtl_init_params.tx_queues_cnt[MTL_PORT_P] = 16; + } + + mtl_init_params.num_ports++; + + mtl_init_params.flags |= MTL_FLAG_BIND_NUMA; + if (src->silent) { + mtl_init_params.log_level = MTL_LOG_LEVEL_ERROR; + } else { + mtl_init_params.log_level = MTL_LOG_LEVEL_INFO; + } + + src->retry_frame = 10; + + mtl_init_params.flags |= MTL_FLAG_BIND_NUMA; + + if (src->devArgs.dma_dev) { + strncpy(mtl_init_params.dma_dev_port[0], src->devArgs.dma_dev, MTL_PORT_MAX_LEN); + } + + if (src->mtl_lib_handle) { + GST_ERROR("MTL already initialized"); + return FALSE; + } + + src->mtl_lib_handle = mtl_init(&mtl_init_params); + if (!src->mtl_lib_handle) { + GST_ERROR("Could not initialize MTL"); + return FALSE; + } + } + + if (src->width == 0 || src->height == 0) { + GST_ERROR("Invalid resolution: %dx%d", src->width, src->height); + return FALSE; + } + + ops_rx->name = "st20src"; + ops_rx->device = ST_PLUGIN_DEVICE_AUTO; + ops_rx->width = src->width; + ops_rx->height = src->height; + ops_rx->transport_fmt = ST20_FMT_YUV_422_10BIT; + ops_rx->port.num_port = 1; + ops_rx->interlaced = src->interlaced; + ops_rx->flags |= ST20P_RX_FLAG_BLOCK_GET; + + if (!src->framerate || !gst_mtl_common_parse_fps_code(src->framerate, &ops_rx->fps)) { + GST_ERROR("Failed to parse custom ops_rx fps code %d", src->framerate); + return FALSE; + } + + if (src->framebuffer_num) { + ops_rx->framebuff_cnt = src->framebuffer_num; + } else { + ops_rx->framebuff_cnt = 3; + } + + if (!gst_mtl_common_parse_pixel_format(src->pixel_format, &ops_rx->output_fmt)) { + GST_ERROR("Failed to parse input format \"%s\"", src->pixel_format); + ops_rx = NULL; + return FALSE; + } + + if (inet_pton(AF_INET, src->portArgs.session_ip_string, + ops_rx->port.ip_addr[MTL_PORT_P]) != 1) { + GST_ERROR("Invalid destination IP address: %s", src->portArgs.session_ip_string); + return FALSE; + } + + if (strlen(src->portArgs.port) == 0) { + strncpy(ops_rx->port.port[MTL_PORT_P], src->devArgs.port, MTL_PORT_MAX_LEN); + } else { + strncpy(ops_rx->port.port[MTL_PORT_P], src->portArgs.port, MTL_PORT_MAX_LEN); + } + + if ((src->portArgs.udp_port < 0) || (src->portArgs.udp_port > 0xFFFF)) { + GST_ERROR("%s, invalid UDP port: %d\n", __func__, src->portArgs.udp_port); + } else { + ops_rx->port.udp_port[0] = src->portArgs.udp_port; + } + + if ((src->portArgs.payload_type < 0) || (src->portArgs.payload_type > 0x7F)) { + GST_ERROR("%s, invalid payload_type: %d\n", __func__, src->portArgs.payload_type); + } else { + ops_rx->port.payload_type = src->portArgs.payload_type; + } + ret = mtl_start(src->mtl_lib_handle); + if (ret < 0) { + GST_ERROR("Failed to start MTL"); + return FALSE; + } + + src->rx_handle = st20p_rx_create(src->mtl_lib_handle, &src->ops_rx); + if (!src->rx_handle) { + GST_ERROR("Failed to create st20p rx handle"); + return FALSE; + } + + src->frame_size = st20p_rx_frame_size(src->rx_handle); + if (src->frame_size <= 0) { + GST_ERROR("Failed to get frame size"); + return FALSE; + } + + return TRUE; +} + +static void gst_mtl_st20p_rx_init(Gst_Mtl_St20p_Rx* src) { + GstElement* element = GST_ELEMENT(src); + GstPad* srcpad; + + srcpad = gst_element_get_static_pad(element, "src"); + if (!srcpad) { + GST_ERROR_OBJECT(src, "Failed to get src pad from child element"); + return; + } +} + +static void gst_mtl_st20p_rx_set_property(GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec) { + Gst_Mtl_St20p_Rx* self = GST_MTL_ST20P_RX(object); + + switch (prop_id) { + case PROP_SILENT: + self->silent = g_value_get_boolean(value); + break; + case PROP_RX_DEV_ARGS_PORT: + strncpy(self->devArgs.port, g_value_get_string(value), MTL_PORT_MAX_LEN); + break; + case PROP_RX_DEV_ARGS_SIP: + strncpy(self->devArgs.local_ip_string, g_value_get_string(value), MTL_PORT_MAX_LEN); + break; + case PROP_RX_DEV_ARGS_DMA_DEV: + strncpy(self->devArgs.dma_dev, g_value_get_string(value), MTL_PORT_MAX_LEN); + break; + case PROP_RX_PORT_PORT: + strncpy(self->portArgs.port, g_value_get_string(value), MTL_PORT_MAX_LEN); + break; + case PROP_RX_PORT_IP: + strncpy(self->portArgs.session_ip_string, g_value_get_string(value), + MTL_PORT_MAX_LEN); + break; + case PROP_RX_PORT_UDP_PORT: + self->portArgs.udp_port = g_value_get_uint(value); + break; + case PROP_RX_PORT_PAYLOAD_TYPE: + self->portArgs.payload_type = g_value_get_uint(value); + break; + case PROP_RX_PORT_RX_QUEUES: + self->devArgs.rx_queues_cnt[MTL_PORT_P] = g_value_get_uint(value); + break; + case PROP_RX_FRAMERATE: + self->framerate = g_value_get_uint(value); + break; + case PROP_RX_FRAMEBUFF_NUM: + self->framebuffer_num = g_value_get_uint(value); + break; + case PROP_RX_WIDTH: + self->width = g_value_get_uint(value); + break; + case PROP_RX_HEIGHT: + self->height = g_value_get_uint(value); + break; + case PROP_RX_INTERLACED: + self->interlaced = g_value_get_boolean(value); + break; + case PROP_RX_PIXEL_FORMAT: + strncpy(self->pixel_format, g_value_get_string(value), MTL_PORT_MAX_LEN); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_mtl_st20p_rx_get_property(GObject* object, guint prop_id, GValue* value, + GParamSpec* pspec) { + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(object); + + switch (prop_id) { + case PROP_SILENT: + g_value_set_boolean(value, src->silent); + break; + case PROP_RX_DEV_ARGS_PORT: + g_value_set_string(value, src->devArgs.port); + break; + case PROP_RX_DEV_ARGS_SIP: + g_value_set_string(value, src->devArgs.local_ip_string); + break; + case PROP_RX_DEV_ARGS_DMA_DEV: + g_value_set_string(value, src->devArgs.dma_dev); + break; + case PROP_RX_PORT_PORT: + g_value_set_string(value, src->portArgs.port); + break; + case PROP_RX_PORT_IP: + g_value_set_string(value, src->portArgs.session_ip_string); + break; + case PROP_RX_PORT_UDP_PORT: + g_value_set_uint(value, src->portArgs.udp_port); + break; + case PROP_RX_PORT_PAYLOAD_TYPE: + g_value_set_uint(value, src->portArgs.payload_type); + break; + case PROP_RX_PORT_RX_QUEUES: + g_value_set_uint(value, src->devArgs.rx_queues_cnt[MTL_PORT_P]); + break; + case PROP_RX_FRAMERATE: + g_value_set_uint(value, src->framerate); + break; + case PROP_RX_FRAMEBUFF_NUM: + g_value_set_uint(value, src->framebuffer_num); + break; + case PROP_RX_WIDTH: + g_value_set_uint(value, src->width); + break; + case PROP_RX_HEIGHT: + g_value_set_uint(value, src->height); + break; + case PROP_RX_INTERLACED: + g_value_set_boolean(value, src->interlaced); + break; + case PROP_RX_PIXEL_FORMAT: + g_value_set_string(value, src->pixel_format); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/* + * Create MTL session rx handle and initialize the session with the parameters + * from caps negotiated by the pipeline. + */ + +static gboolean gst_mtl_st20p_rx_negotiate(GstBaseSrc* basesrc) { + GstVideoInfo* info; + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(basesrc); + struct st20p_rx_ops* ops_rx = &src->ops_rx; + gint ret; + GstCaps* caps; + + info = gst_video_info_new(); + if (!info) { + GST_ERROR("Failed to allocate video info"); + return FALSE; + } + + /* + * Convert boolean interlaced value to integer, + * as only basic interlaced mode (1) is supported. + */ + info->interlace_mode = src->interlaced; + + switch (ops_rx->output_fmt) { + case ST_FRAME_FMT_V210: + info->finfo = gst_video_format_get_info(GST_VIDEO_FORMAT_v210); + break; + case ST20_FMT_YUV_422_10BIT: + info->finfo = gst_video_format_get_info(GST_VIDEO_FORMAT_I422_10LE); + break; + default: + GST_ERROR("Unsupported pixel format"); + gst_video_info_free(info); + return FALSE; + } + + caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, + gst_video_format_to_string(info->finfo->format), "width", + G_TYPE_INT, info->width, "height", G_TYPE_INT, info->height, + "framerate", GST_TYPE_FRACTION, info->fps_n, 1, + "interlace-mode", G_TYPE_BOOLEAN, src->interlaced, NULL); + + if (!caps) caps = gst_pad_get_pad_template_caps(GST_BASE_SRC_PAD(basesrc)); + + if (gst_caps_is_empty(caps)) { + GST_ERROR("Failed to set caps: caps are empty"); + gst_video_info_free(info); + gst_caps_unref(caps); + return FALSE; + } + + ret = gst_pad_set_caps(GST_BASE_SRC_PAD(basesrc), caps); + gst_caps_unref(caps); + if (!ret) { + GST_ERROR("Failed to set caps error %d", ret); + gst_video_info_free(info); + return FALSE; + } + + gst_video_info_free(info); + return TRUE; +} + +static GstFlowReturn gst_mtl_st20p_rx_create(GstBaseSrc* basesrc, guint64 offset, + guint length, GstBuffer** buffer) { + GstBuffer* buf; + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(basesrc); + struct st_frame* frame; + GstMapInfo dest_info; + gint ret; + gsize fill_size; + + buf = gst_buffer_new_allocate(NULL, src->frame_size, NULL); + if (!buf) { + GST_ERROR("Failed to allocate buffer"); + return GST_FLOW_ERROR; + } + + *buffer = buf; + + GST_OBJECT_LOCK(src); + + for (int i = 0; i < src->retry_frame; i++) { + frame = st20p_rx_get_frame(src->rx_handle); + if (frame) { + break; + } + } + + if (!frame) { + GST_INFO("Failed to get frame EOS"); + GST_OBJECT_UNLOCK(src); + return GST_FLOW_EOS; + } + + gst_buffer_map(buf, &dest_info, GST_MAP_WRITE); + + fill_size = gst_buffer_fill(buf, 0, frame->addr[0], src->frame_size); + GST_BUFFER_PTS(buf) = frame->timestamp; + + gst_buffer_unmap(buf, &dest_info); + + if (fill_size != src->frame_size) { + GST_ERROR("Failed to fill buffer"); + ret = GST_FLOW_ERROR; + } else { + ret = GST_FLOW_OK; + } + + st20p_rx_put_frame(src->rx_handle, frame); + GST_OBJECT_UNLOCK(src); + return ret; +} + +static void gst_mtl_st20p_rx_finalize(GObject* object) { + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(object); + + if (src->rx_handle) { + if (st20p_rx_free(src->rx_handle)) { + GST_ERROR("Failed to free rx handle"); + return; + } + } + + if (src->mtl_lib_handle) { + if (mtl_stop(src->mtl_lib_handle) || mtl_uninit(src->mtl_lib_handle)) { + GST_ERROR("Failed to uninitialize MTL library"); + return; + } + } +} + +static gboolean gst_mtl_st20p_rx_stop(GstBaseSrc* basesrc) { + Gst_Mtl_St20p_Rx* src = GST_MTL_ST20P_RX(basesrc); + + if (src->mtl_lib_handle) { + mtl_stop(src->mtl_lib_handle); + } + + return TRUE; +} + +static gboolean plugin_init(GstPlugin* mtl_st20p_rx) { + return gst_element_register(mtl_st20p_rx, "mtl_st20p_rx", GST_RANK_SECONDARY, + GST_TYPE_MTL_ST20P_RX); +} + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, mtl_st20p_rx, + "software-based solution designed for high-throughput transmission", + plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.h b/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.h new file mode 100644 index 000000000..94137e5d2 --- /dev/null +++ b/ecosystem/gstreamer_plugin/gst_mtl_st20p_rx.h @@ -0,0 +1,97 @@ +/* + * GStreamer + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2020 Niels De Graef + * Copyright (C) 2024 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_MTL_ST20P_RX_H__ +#define __GST_MTL_ST20P_RX_H__ + +#include + +#include "gst_mtl_common.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MTL_ST20P_RX (gst_mtl_st20p_rx_get_type()) +G_DECLARE_FINAL_TYPE(Gst_Mtl_St20p_Rx, gst_mtl_st20p_rx, GST, MTL_ST20P_RX, GstBaseSrc) + +struct _Gst_Mtl_St20p_Rx { + GstBaseSrc element; + GstBuffer* buffer; + + /*< private >*/ + struct st20p_rx_ops ops_rx; + gboolean silent; + mtl_handle mtl_lib_handle; + st20p_rx_handle rx_handle; + + /* arguments for incomplete frame buffers */ + guint retry_frame; + guint frame_size; + + /* arguments for imtl initialization device */ + StDevArgs devArgs; + /* arguments for imtl rx session */ + SessionPortArgs portArgs; + + /* arguments for session */ + guint width; + guint height; + gboolean interlaced; + gchar pixel_format[MTL_PORT_MAX_LEN]; + guint framebuffer_num; + guint framerate; + + /* TODO add support for gpu direct */ +#ifdef MTL_GPU_DIRECT_ENABLED + gboolean gpu_direct_enabled; + gint gpu_driver_index; + gint gpu_device_index; + guint8* gpu_context; +#endif /* MTL_GPU_DIRECT_ENABLED */ +}; + +G_END_DECLS + +#endif /* __GST_MTL_ST20P_RX_H__ */ diff --git a/ecosystem/gstreamer_plugin/gstmtltxsink.c b/ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.c similarity index 74% rename from ecosystem/gstreamer_plugin/gstmtltxsink.c rename to ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.c index 9486e71f1..558a1bc6f 100644 --- a/ecosystem/gstreamer_plugin/gstmtltxsink.c +++ b/ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.c @@ -66,10 +66,10 @@ #include -#include "gstmtltxsink.h" +#include "gst_mtl_st20p_tx.h" -GST_DEBUG_CATEGORY_STATIC(gst_mtltxsink_debug); -#define GST_CAT_DEFAULT gst_mtltxsink_debug +GST_DEBUG_CATEGORY_STATIC(gst_mtl_st20p_tx_debug); +#define GST_CAT_DEFAULT gst_mtl_st20p_tx_debug #ifndef GST_LICENSE #define GST_LICENSE "LGPL" #endif @@ -82,11 +82,11 @@ GST_DEBUG_CATEGORY_STATIC(gst_mtltxsink_debug); #ifndef GST_PACKAGE_ORIGIN #define GST_PACKAGE_ORIGIN "https://github.com/OpenVisualCloud/Media-Transport-Library" #endif -#ifndef PACKAGE -#define PACKAGE "gst-mtl-tx-st20" +#ifndef PACKAGEf +#define PACKAGE "gst-mtl-st20p-tx" #endif #ifndef PACKAGE_VERSION -#define PACKAGE_VERSION "1.19.0.1" +#define PACKAGE_VERSION "1.0" #endif enum { @@ -106,7 +106,7 @@ enum { }; /* pad template */ -static GstStaticPadTemplate gst_mtltxsink_sink_pad_template = +static GstStaticPadTemplate gst_mtl_st20p_tx_sink_pad_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("video/x-raw, " "format = (string) {v210, I422_10LE}," @@ -114,126 +114,28 @@ static GstStaticPadTemplate gst_mtltxsink_sink_pad_template = "height = (int) [64, 8704], " "framerate = (fraction) [0, MAX]")); -#define gst_mtltxsink_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE(GstMtlTxSink, gst_mtltxsink, GST_TYPE_VIDEO_SINK, - GST_DEBUG_CATEGORY_INIT(gst_mtltxsink_debug, "mtltxsink", 0, +#define gst_mtl_st20p_tx_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE(Gst_Mtl_St20p_Tx, gst_mtl_st20p_tx, GST_TYPE_VIDEO_SINK, + GST_DEBUG_CATEGORY_INIT(gst_mtl_st20p_tx_debug, "mtl_st20p_tx", 0, "MTL St2110 st20 transmission sink")); -GST_ELEMENT_REGISTER_DEFINE(mtltxsink, "mtltxsink", GST_RANK_NONE, GST_TYPE_MTL_TX_SINK); +GST_ELEMENT_REGISTER_DEFINE(mtl_st20p_tx, "mtl_st20p_tx", GST_RANK_NONE, + GST_TYPE_MTL_ST20P_TX); -static void gst_mtltxsink_set_property(GObject* object, guint prop_id, - const GValue* value, GParamSpec* pspec); -static void gst_mtltxsink_get_property(GObject* object, guint prop_id, GValue* value, - GParamSpec* pspec); -static void gst_mtltxsink_finalize(GObject* object); +static void gst_mtl_st20p_tx_set_property(GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec); +static void gst_mtl_st20p_tx_get_property(GObject* object, guint prop_id, GValue* value, + GParamSpec* pspec); +static void gst_mtl_st20p_tx_finalize(GObject* object); -static gboolean gst_mtltxsink_sink_event(GstPad* pad, GstObject* parent, GstEvent* event); -static GstFlowReturn gst_mtltxsink_chain(GstPad* pad, GstObject* parent, GstBuffer* buf); +static gboolean gst_mtl_st20p_tx_sink_event(GstPad* pad, GstObject* parent, + GstEvent* event); +static GstFlowReturn gst_mtl_st20p_tx_chain(GstPad* pad, GstObject* parent, + GstBuffer* buf); -static gboolean gst_mtltxsink_start(GstBaseSink* bsink); -static gboolean gst_mtltxsink_stop(GstBaseSink* bsink); +static gboolean gst_mtl_st20p_tx_start(GstBaseSink* bsink); -static gboolean gst_mtltxsink_parse_input_fmt(GstVideoInfo* info, enum st_frame_fmt* fmt); -static gboolean gst_mtltxsink_parse_fps(GstVideoInfo* info, enum st_fps* fps); -static gboolean gst_mtltxsink_parse_fps_code(gint fps_code, enum st_fps* fps); - -static gboolean gst_mtltxsink_parse_input_fmt(GstVideoInfo* info, - enum st_frame_fmt* fmt) { - GstVideoFormatInfo* finfo = info->finfo; - - if (finfo->format == GST_VIDEO_FORMAT_v210) { - *fmt = ST_FRAME_FMT_V210; - } else if (finfo->format == GST_VIDEO_FORMAT_I420_10LE) { - *fmt = ST_FRAME_FMT_YUV422PLANAR10LE; - } else { - return FALSE; - } - - return TRUE; -} - -static gboolean gst_mtltxsink_parse_fps_code(gint fps_code, enum st_fps* fps) { - if (!fps) { - GST_ERROR("Invalid fps pointer"); - return FALSE; - } - - switch (fps_code) { - case 120: - *fps = ST_FPS_P120; - break; - case 11988: - *fps = ST_FPS_P119_88; - break; - case 100: - *fps = ST_FPS_P100; - break; - case 60: - *fps = ST_FPS_P60; - break; - case 5994: - *fps = ST_FPS_P59_94; - break; - case 50: - *fps = ST_FPS_P50; - break; - case 30: - *fps = ST_FPS_P30; - break; - case 2997: - *fps = ST_FPS_P29_97; - break; - case 25: - *fps = ST_FPS_P25; - break; - case 24: - *fps = ST_FPS_P24; - break; - case 2398: - *fps = ST_FPS_P23_98; - break; - default: - return FALSE; - } - return TRUE; -} - -// TODO add support for the partial fps -static gboolean gst_mtltxsink_parse_fps(GstVideoInfo* info, enum st_fps* fps) { - gint fps_div; - if (info->fps_n <= 0 || info->fps_d <= 0) { - return FALSE; - } - - fps_div = info->fps_n / info->fps_d; - - switch (fps_div) { - case 24: - *fps = ST_FPS_P24; - break; - case 25: - *fps = ST_FPS_P25; - break; - case 30: - *fps = ST_FPS_P30; - break; - case 50: - *fps = ST_FPS_P50; - break; - case 60: - *fps = ST_FPS_P60; - break; - case 120: - *fps = ST_FPS_P120; - break; - default: - return FALSE; - } - - return TRUE; -} - -static void gst_mtltxsink_class_init(GstMtlTxSinkClass* klass) { +static void gst_mtl_st20p_tx_class_init(Gst_Mtl_St20p_TxClass* klass) { GObjectClass* gobject_class; GstElementClass* gstelement_class; GstVideoSinkClass* gstvideosinkelement_class; @@ -248,12 +150,13 @@ static void gst_mtltxsink_class_init(GstMtlTxSinkClass* klass) { "Dawid Wesierski "); gst_element_class_add_static_pad_template(gstelement_class, - &gst_mtltxsink_sink_pad_template); + &gst_mtl_st20p_tx_sink_pad_template); - gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_mtltxsink_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_mtltxsink_get_property); - gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_mtltxsink_finalize); - gstvideosinkelement_class->parent_class.start = GST_DEBUG_FUNCPTR(gst_mtltxsink_start); + gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_get_property); + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_finalize); + gstvideosinkelement_class->parent_class.start = + GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_start); g_object_class_install_property( gobject_class, PROP_SILENT, @@ -323,11 +226,11 @@ static void gst_mtltxsink_class_init(GstMtlTxSinkClass* klass) { G_MAXUINT, 3, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } -static gboolean gst_mtltxsink_start(GstBaseSink* bsink) { +static gboolean gst_mtl_st20p_tx_start(GstBaseSink* bsink) { struct mtl_init_params mtl_init_params = {0}; gint ret; - GstMtlTxSink* sink = GST_MTL_TX_SINK(bsink); + Gst_Mtl_St20p_Tx* sink = GST_MTL_ST20P_TX(bsink); GST_DEBUG_OBJECT(sink, "start"); GST_DEBUG("Media Transport Initialization start"); @@ -393,7 +296,7 @@ static gboolean gst_mtltxsink_start(GstBaseSink* bsink) { return true; } -static void gst_mtltxsink_init(GstMtlTxSink* sink) { +static void gst_mtl_st20p_tx_init(Gst_Mtl_St20p_Tx* sink) { GstElement* element = GST_ELEMENT(sink); GstPad* sinkpad; @@ -403,14 +306,14 @@ static void gst_mtltxsink_init(GstMtlTxSink* sink) { return; } - gst_pad_set_event_function(sinkpad, GST_DEBUG_FUNCPTR(gst_mtltxsink_sink_event)); + gst_pad_set_event_function(sinkpad, GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_sink_event)); - gst_pad_set_chain_function(sinkpad, GST_DEBUG_FUNCPTR(gst_mtltxsink_chain)); + gst_pad_set_chain_function(sinkpad, GST_DEBUG_FUNCPTR(gst_mtl_st20p_tx_chain)); } -static void gst_mtltxsink_set_property(GObject* object, guint prop_id, - const GValue* value, GParamSpec* pspec) { - GstMtlTxSink* self = GST_MTL_TX_SINK(object); +static void gst_mtl_st20p_tx_set_property(GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec) { + Gst_Mtl_St20p_Tx* self = GST_MTL_ST20P_TX(object); switch (prop_id) { case PROP_SILENT: @@ -429,7 +332,8 @@ static void gst_mtltxsink_set_property(GObject* object, guint prop_id, strncpy(self->portArgs.port, g_value_get_string(value), MTL_PORT_MAX_LEN); break; case PROP_TX_PORT_IP: - strncpy(self->portArgs.tx_ip_string, g_value_get_string(value), MTL_PORT_MAX_LEN); + strncpy(self->portArgs.session_ip_string, g_value_get_string(value), + MTL_PORT_MAX_LEN); break; case PROP_TX_PORT_UDP_PORT: self->portArgs.udp_port = g_value_get_uint(value); @@ -452,9 +356,9 @@ static void gst_mtltxsink_set_property(GObject* object, guint prop_id, } } -static void gst_mtltxsink_get_property(GObject* object, guint prop_id, GValue* value, - GParamSpec* pspec) { - GstMtlTxSink* sink = GST_MTL_TX_SINK(object); +static void gst_mtl_st20p_tx_get_property(GObject* object, guint prop_id, GValue* value, + GParamSpec* pspec) { + Gst_Mtl_St20p_Tx* sink = GST_MTL_ST20P_TX(object); switch (prop_id) { case PROP_SILENT: @@ -473,7 +377,7 @@ static void gst_mtltxsink_get_property(GObject* object, guint prop_id, GValue* v g_value_set_string(value, sink->portArgs.port); break; case PROP_TX_PORT_IP: - g_value_set_string(value, sink->portArgs.tx_ip_string); + g_value_set_string(value, sink->portArgs.session_ip_string); break; case PROP_TX_PORT_UDP_PORT: g_value_set_uint(value, sink->portArgs.udp_port); @@ -500,7 +404,7 @@ static void gst_mtltxsink_get_property(GObject* object, guint prop_id, GValue* v * Create MTL session tx handle and initialize the session with the parameters * from caps negotiated by the pipeline. */ -static gboolean gst_mtltxsink_session_create(GstMtlTxSink* sink, GstCaps* caps) { +static gboolean gst_mtl_st20p_tx_session_create(Gst_Mtl_St20p_Tx* sink, GstCaps* caps) { GstVideoInfo* info; struct st20p_tx_ops ops_tx = {0}; gint ret; @@ -532,24 +436,24 @@ static gboolean gst_mtltxsink_session_create(GstMtlTxSink* sink, GstCaps* caps) return FALSE; } - if (!gst_mtltxsink_parse_input_fmt(info, &ops_tx.input_fmt)) { + if (!gst_mtl_common_parse_input_finfo(info->finfo, &ops_tx.input_fmt)) { GST_ERROR("Failed to parse input format"); return FALSE; } if (sink->framerate) { - if (!gst_mtltxsink_parse_fps_code(sink->framerate, &ops_tx.fps)) { + if (!gst_mtl_common_parse_fps_code(sink->framerate, &ops_tx.fps)) { GST_ERROR("Failed to parse custom ops_tx fps code %d", sink->framerate); return FALSE; } - } else if (!gst_mtltxsink_parse_fps(info, &ops_tx.fps)) { + } else if (!gst_mtl_common_parse_fps(info, &ops_tx.fps)) { GST_ERROR("Failed to parse fps"); return FALSE; } - if (inet_pton(AF_INET, sink->portArgs.tx_ip_string, ops_tx.port.dip_addr[MTL_PORT_P]) != - 1) { - GST_ERROR("Invalid destination IP address: %s", sink->portArgs.tx_ip_string); + if (inet_pton(AF_INET, sink->portArgs.session_ip_string, + ops_tx.port.dip_addr[MTL_PORT_P]) != 1) { + GST_ERROR("Invalid destination IP address: %s", sink->portArgs.session_ip_string); return FALSE; } @@ -590,13 +494,13 @@ static gboolean gst_mtltxsink_session_create(GstMtlTxSink* sink, GstCaps* caps) return TRUE; } -static gboolean gst_mtltxsink_sink_event(GstPad* pad, GstObject* parent, - GstEvent* event) { - GstMtlTxSink* sink; +static gboolean gst_mtl_st20p_tx_sink_event(GstPad* pad, GstObject* parent, + GstEvent* event) { + Gst_Mtl_St20p_Tx* sink; GstCaps* caps; gint ret; - sink = GST_MTL_TX_SINK(parent); + sink = GST_MTL_ST20P_TX(parent); GST_LOG_OBJECT(sink, "Received %s event: %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME(event), event); @@ -613,7 +517,7 @@ static gboolean gst_mtltxsink_sink_event(GstPad* pad, GstObject* parent, break; case GST_EVENT_CAPS: gst_event_parse_caps(event, &caps); - ret = gst_mtltxsink_session_create(sink, caps); + ret = gst_mtl_st20p_tx_session_create(sink, caps); if (!ret) { GST_ERROR("Failed to create TX session"); return FALSE; @@ -621,7 +525,6 @@ static gboolean gst_mtltxsink_sink_event(GstPad* pad, GstObject* parent, ret = gst_pad_event_default(pad, parent, event); break; case GST_EVENT_EOS: - gst_mtltxsink_stop(GST_BASE_SINK(sink)); ret = gst_pad_event_default(pad, parent, event); gst_element_post_message(GST_ELEMENT(sink), gst_message_new_eos(GST_OBJECT(sink))); break; @@ -638,8 +541,9 @@ static gboolean gst_mtltxsink_sink_event(GstPad* pad, GstObject* parent, * frame buffers, supports incomplete frames. But buffers needs to add up to the * actual frame size. */ -static GstFlowReturn gst_mtltxsink_chain(GstPad* pad, GstObject* parent, GstBuffer* buf) { - GstMtlTxSink* sink = GST_MTL_TX_SINK(parent); +static GstFlowReturn gst_mtl_st20p_tx_chain(GstPad* pad, GstObject* parent, + GstBuffer* buf) { + Gst_Mtl_St20p_Tx* sink = GST_MTL_ST20P_TX(parent); gint buffer_size = gst_buffer_get_size(buf); gint buffer_n = gst_buffer_n_memory(buf); struct st_frame* frame = NULL; @@ -679,8 +583,8 @@ static GstFlowReturn gst_mtltxsink_chain(GstPad* pad, GstObject* parent, GstBuff return GST_FLOW_OK; } -static void gst_mtltxsink_finalize(GObject* object) { - GstMtlTxSink* sink = GST_MTL_TX_SINK(object); +static void gst_mtl_st20p_tx_finalize(GObject* object) { + Gst_Mtl_St20p_Tx* sink = GST_MTL_ST20P_TX(object); if (sink->tx_handle) { if (st20p_tx_free(sink->tx_handle)) { @@ -697,22 +601,12 @@ static void gst_mtltxsink_finalize(GObject* object) { } } -static gboolean gst_mtltxsink_stop(GstBaseSink* bsink) { - GstMtlTxSink* sink = GST_MTL_TX_SINK(bsink); - - if (sink->mtl_lib_handle) { - mtl_stop(sink->mtl_lib_handle); - } - - return true; -} - -static gboolean plugin_init(GstPlugin* mtltxsink) { - return gst_element_register(mtltxsink, "mtltxsink", GST_RANK_SECONDARY, - GST_TYPE_MTL_TX_SINK); +static gboolean plugin_init(GstPlugin* mtl_st20p_tx) { + return gst_element_register(mtl_st20p_tx, "mtl_st20p_tx", GST_RANK_SECONDARY, + GST_TYPE_MTL_ST20P_TX); } -GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, mtltxsink, +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, mtl_st20p_tx, "software-based solution designed for high-throughput transmission", plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ecosystem/gstreamer_plugin/gstmtltxsink.h b/ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.h similarity index 75% rename from ecosystem/gstreamer_plugin/gstmtltxsink.h rename to ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.h index 08ff74ece..baeeadda2 100644 --- a/ecosystem/gstreamer_plugin/gstmtltxsink.h +++ b/ecosystem/gstreamer_plugin/gst_mtl_st20p_tx.h @@ -44,44 +44,17 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __GST_MTL_TX_SINK_H__ -#define __GST_MTL_TX_SINK_H__ +#ifndef __GST_MTL_ST20P_TX_H__ +#define __GST_MTL_ST20P_TX_H__ -#include -#include -#include -#include -#include +#include "gst_mtl_common.h" G_BEGIN_DECLS -#ifndef NS_PER_MS -#define NS_PER_MS (1000 * 1000) -#endif +#define GST_TYPE_MTL_ST20P_TX (gst_mtl_st20p_tx_get_type()) +G_DECLARE_FINAL_TYPE(Gst_Mtl_St20p_Tx, gst_mtl_st20p_tx, GST, MTL_ST20P_TX, GstVideoSink) -#ifndef NS_PER_S -#define NS_PER_S (1000 * NS_PER_MS) -#endif - -#define GST_TYPE_MTL_TX_SINK (gst_mtltxsink_get_type()) -G_DECLARE_FINAL_TYPE(GstMtlTxSink, gst_mtltxsink, GST, MTL_TX_SINK, GstVideoSink) - -typedef struct StDevArgs { - gchar port[MTL_PORT_MAX_LEN]; - gchar local_ip_string[MTL_PORT_MAX_LEN]; - gint tx_queues_cnt[MTL_PORT_MAX]; - gint rx_queues_cnt[MTL_PORT_MAX]; - gchar dma_dev[MTL_PORT_MAX_LEN]; -} StDevArgs; - -typedef struct StTxSessionPortArgs { - gchar tx_ip_string[MTL_PORT_MAX_LEN]; - gchar port[MTL_PORT_MAX_LEN]; - gint udp_port; - gint payload_type; -} StTxSessionPortArgs; - -struct _GstMtlTxSink { +struct _Gst_Mtl_St20p_Tx { GstVideoSink element; GstElement* child; gboolean silent; @@ -95,7 +68,7 @@ struct _GstMtlTxSink { /* arguments for imtl initialization device */ StDevArgs devArgs; /* arguments for imtl tx session */ - StTxSessionPortArgs portArgs; + SessionPortArgs portArgs; /* arguments for session */ guint framebuffer_num; @@ -112,4 +85,4 @@ struct _GstMtlTxSink { G_END_DECLS -#endif /* __GST_MTL_TX_SINK_H__ */ +#endif /* __GST_MTL_ST20P_TX_H__ */ diff --git a/ecosystem/gstreamer_plugin/meson.build b/ecosystem/gstreamer_plugin/meson.build index 66149b978..78f2e20ee 100644 --- a/ecosystem/gstreamer_plugin/meson.build +++ b/ecosystem/gstreamer_plugin/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright 2024 Intel Corporation -project('gstreamer-mtl-st20tx', 'c', +project('gstreamer_mtl_plugins', 'c', version: run_command(find_program('cat'), files('../../VERSION'), check: true).stdout().strip(), license : 'LGPL') cc = meson.get_compiler('c') @@ -19,7 +19,7 @@ plugin_c_args = [ '-Wno-declaration-after-statement', '-Wno-missing-prototypes', '-Wno-missing-declarations', - '-Wno-stringop-truncation', + '-Wno-stringop-truncation' ] inc_dirs = include_directories( @@ -32,16 +32,37 @@ inc_dirs = include_directories( gstreamer_video_dep = dependency('gstreamer-video-1.0') mtl_dep = dependency('mtl') -# The mtl_tx_sink Plugin - gstmtl_tx_sink_sources = [ - 'gstmtltxsink.c', - ] +gst_mtl_common_sources = [ + 'gst_mtl_common.c' +] + +# mtl_st20p_tx Plugin +gst_mtl_st20p_tx_sources = [ + 'gst_mtl_st20p_tx.c' +] + +gst_mtl_st20p_tx = library('gstmtl_st20p_tx', + gst_mtl_common_sources + gst_mtl_st20p_tx_sources, + dependencies : [gst_dep, gstbase_dep, gstreamer_video_dep, mtl_dep], + install : true, + install_dir : plugins_install_dir, + include_directories: inc_dirs, + c_args: plugin_c_args +) + + +# mtl_st20p_rx Plugin +gst_mtl_st20p_rx_sources = [ + 'gst_mtl_st20p_rx.c' +] -gstmtl_tx_sinkexample = library('gstmtltxsink', - gstmtl_tx_sink_sources, +gst_mtl_st20p_rx = library('gstmtl_st20p_rx', + gst_mtl_common_sources + gst_mtl_st20p_rx_sources, dependencies : [gst_dep, gstbase_dep, gstreamer_video_dep, mtl_dep], install : true, install_dir : plugins_install_dir, include_directories: inc_dirs, c_args: plugin_c_args -) \ No newline at end of file +) + +