diff --git a/cpp/demos/config/nmos_plugin_node_config.json b/cpp/demos/config/nmos_plugin_node_config.json new file mode 100644 index 0000000..f50108b --- /dev/null +++ b/cpp/demos/config/nmos_plugin_node_config.json @@ -0,0 +1,39 @@ +{ + "node": { + "id": "d5504cd1-fe68-489d-99d4-20d3f075f062", + "configuration": { + "label": "BISECT OSSRF Node2", + "description": "BISECT OSSRF node2", + "host_addresses": [ + "192.168.1.120" + ], + "interfaces": [ + { + "chassis_id": "c8-94-02-f7-3e-eb", + "name": "wlp1s0", + "port_id": "00-e0-4c-68-01-8d" + } + ], + "clocks": [ + { + "name": "clk0", + "ref_type": "ptp", + "traceable": false, + "version": "IEEE1588-2008", + "gmid": "00-20-fc-ff-fe-35-9c-25", + "locked": true + } + ], + "registry_address": "192.168.1.120", + "registry_version": "v1.3", + "registration_port": 8010, + "system_address": "192.168.1.120", + "system_version": "v1.0", + "system_port": 8010, + "http_port": 5113 + } + }, + "device": {}, + "receivers": [], + "senders": [] +} \ No newline at end of file diff --git a/cpp/demos/ossrf-nmos-api/config/nmos_config.json b/cpp/demos/ossrf-nmos-api/config/nmos_config.json index 4a0028b..0933c39 100644 --- a/cpp/demos/ossrf-nmos-api/config/nmos_config.json +++ b/cpp/demos/ossrf-nmos-api/config/nmos_config.json @@ -114,4 +114,4 @@ } } ] -} \ No newline at end of file +} diff --git a/cpp/demos/ossrf-nmos-api/config/nmos_plugin_config.json b/cpp/demos/ossrf-nmos-api/config/nmos_plugin_config.json deleted file mode 100644 index 0e488e9..0000000 --- a/cpp/demos/ossrf-nmos-api/config/nmos_plugin_config.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "node": { - "id": "d5504cd1-fe68-489d-99d4-20d3f075f062", - "configuration": { - "label": "BISECT OSSRF Node2", - "description": "BISECT OSSRF node2", - "host_addresses": [ - "192.168.88.92" - ], - "interfaces": [ - { - "chassis_id": "c8-94-02-f7-3e-eb", - "name": "wlp1s0", - "port_id": "00-e0-4c-68-01-8d" - } - ], - "clocks": [ - { - "name": "clk0", - "ref_type": "ptp", - "traceable": false, - "version": "IEEE1588-2008", - "gmid": "00-20-fc-ff-fe-35-9c-25", - "locked": true - } - ], - "registry_address": "192.168.88.92", - "registry_version": "v1.3", - "registration_port": 8010, - "system_address": "192.168.88.92", - "system_version": "v1.0", - "system_port": 8010, - "http_port": 5113 - } - }, - "device": { - "id": "e92e628b-7421-4723-9fb9-c1f3b38af9d3", - "label": "OSSRF Device2", - "description": "OSSRF Device2" - }, - "receivers": [ - { - "id": "db9f46cf-2414-4e25-b6c6-2078159857f9", - "label": "BISECT OSSRF receiver video", - "description": "BISECT OSSRF receiver video", - "network": { - "primary": { - "interface_address": "192.168.88.92", - "interface_name": "wlp1s0" - } - }, - "capabilities": [ - "video/raw" - ] - }, - { - "id": "15319770-0fc3-41c9-8985-aab2983d9ed0", - "label": "BISECT OSSRF receiver audio", - "description": "BISECT OSSRF receiver audio", - "network": { - "primary": { - "interface_address": "192.168.88.92", - "interface_name": "wlp1s0" - } - }, - "capabilities": [ - "audio/L24" - ] - } - ], - "senders": [] -} \ No newline at end of file diff --git a/cpp/demos/ossrf-nmos-api/config/nmos_receiver_config.json b/cpp/demos/ossrf-nmos-api/config/nmos_receiver_config.json deleted file mode 100644 index f8375ab..0000000 --- a/cpp/demos/ossrf-nmos-api/config/nmos_receiver_config.json +++ /dev/null @@ -1,14 +0,0 @@ - { - "id": "db9f46cf-2414-4e25-b6c6-2078159857f9", - "label": "BISECT OSSRF receiver video", - "description": "BISECT OSSRF receiver video", - "network": { - "primary": { - "interface_address": "192.168.88.92", - "interface_name": "wlp1s0" - } - }, - "capabilities": [ - "video/raw" - ] - } \ No newline at end of file diff --git a/cpp/demos/ossrf-nmos-api/config/nmos_sender_config.json b/cpp/demos/ossrf-nmos-api/config/nmos_sender_config.json deleted file mode 100644 index fb0a3fc..0000000 --- a/cpp/demos/ossrf-nmos-api/config/nmos_sender_config.json +++ /dev/null @@ -1,25 +0,0 @@ - { - "id": "e543a2c1-d6a2-47f5-8d14-296bb6714ef2", - "label": "BISECT OSSRF sender video", - "description": "BISECT OSSRF sender video", - "network": { - "primary": { - "source_address": "192.168.88.92", - "interface_name": "wlp1s0", - "destination_address": "192.168.88.92", - "destination_port": 5004 - } - }, - "payload_type": 97, - "media_type": "video/raw", - "media": { - "width": 640, - "height": 480, - "frame_rate": { - "num": 50, - "den": 1 - }, - "sampling": "YCbCr-4:2:2", - "structure": "progressive" - } - } \ No newline at end of file diff --git a/cpp/libs/CMakeLists.txt b/cpp/libs/CMakeLists.txt index f5bf0ec..1e0bd5d 100644 --- a/cpp/libs/CMakeLists.txt +++ b/cpp/libs/CMakeLists.txt @@ -4,6 +4,5 @@ add_subdirectory(bisect_sdp) add_subdirectory(bisect_gst) add_subdirectory(gst_nmos_sender_plugin) add_subdirectory(ossrf_nmos_api) -add_subdirectory(ossrf_c_nmos_api) add_subdirectory(ossrf_gstreamer_api) add_subdirectory(bisect_json) diff --git a/cpp/libs/gst_nmos_sender_plugin/CMakeLists.txt b/cpp/libs/gst_nmos_sender_plugin/CMakeLists.txt index 82b3670..574aa84 100644 --- a/cpp/libs/gst_nmos_sender_plugin/CMakeLists.txt +++ b/cpp/libs/gst_nmos_sender_plugin/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.16) -project(gst_nmos_sender_plugin LANGUAGES C) +project(gst_nmos_sender_plugin LANGUAGES CXX) -# Define the path to the ossrf_c_nmos_api directory -set(OSSRF_C_NMOS_API_PATH "../ossrf_c_nmos_api/") +find_package(nlohmann_json REQUIRED) +find_package(PkgConfig REQUIRED) # Find required GStreamer packages find_package(PkgConfig REQUIRED) @@ -12,13 +12,13 @@ pkg_search_module(GSTREAMER_AUDIO REQUIRED gstreamer-audio-1.0>=1.4) pkg_search_module(GSTREAMER_VIDEO REQUIRED gstreamer-video-1.0>=1.4) # Include source files -file(GLOB_RECURSE ${PROJECT_NAME}_source_files *.c *.h) +file(GLOB_RECURSE ${PROJECT_NAME}_source_files *.cpp *.h) # Define the plugin as a shared library add_library(${PROJECT_NAME} MODULE ${${PROJECT_NAME}_source_files}) # Include directories -target_include_directories(${PROJECT_NAME} PRIVATE ${GSTREAMER_INCLUDE_DIRS} ${OSSRF_C_NMOS_API_PATH}) +target_include_directories(${PROJECT_NAME} PRIVATE ${GSTREAMER_INCLUDE_DIRS}) # Link GStreamer libraries target_compile_options(${PROJECT_NAME} PRIVATE ${GSTREAMER_CFLAGS_OTHER}) @@ -27,7 +27,14 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${GSTREAMER_APP_LIBRARIES} ${GSTREAMER_AUDIO_LIBRARIES} ${GSTREAMER_VIDEO_LIBRARIES} - ossrf::ossrf_c_nmos_api) + PUBLIC + bisect::project_warnings + bisect::expected + bisect::bisect_nmoscpp + bisect::bisect_json + nlohmann_json::nlohmann_json + ossrf::ossrf_nmos_api + ${GLIB_LIBRARIES}) # Specify the output directory and the library name set_target_properties(${PROJECT_NAME} PROPERTIES @@ -40,3 +47,5 @@ add_library(ossrf::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) # Install the plugin to the system's GStreamer plugin directory install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ~/.local/lib/gstreamer-1.0) + +add_subdirectory(utils) diff --git a/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.c b/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.c deleted file mode 100644 index a8cd80a..0000000 --- a/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.c +++ /dev/null @@ -1,327 +0,0 @@ - -/* GStreamer - * - * Copyright (C) <2024> Bisect Lda - * @author: Luís Ferreira - * - * 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., 51 Franklin Street, Suite 500, - * Boston, MA 02110-1335, USA. - */ - -#include "ossrf_c_nmos_api.h" -#include -#include - -GST_DEBUG_CATEGORY_STATIC(gst_nmossender_debug_category); -#define GST_CAT_DEFAULT gst_nmossender_debug_category -#define GST_TYPE_NMOSSENDER (gst_nmossender_get_type()) -#define GST_NMOSSENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_NMOSSENDER, GstNmossender)) -#define GST_NMOSSENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_NMOSSENDER, GstNmossenderClass)) -#define GST_IS_NMOSSENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_NMOSSENDER)) -#define GST_IS_NMOSSENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_NMOSSENDER)) - -typedef struct _GstNmossender -{ - GstBin parent; - GstElement* payloader; - GstElement* udpsink; - gchar* source_address; - gchar* interface_name; - gchar* destination_address; - gchar* destination_port; - GstCaps* caps; - gint width; - gint height; - const gchar* format; -} GstNmossender; - -typedef struct _GstNmossenderClass -{ - GstBinClass parent_class; -} GstNmossenderClass; - -G_DEFINE_TYPE_WITH_CODE(GstNmossender, gst_nmossender, GST_TYPE_BIN, - GST_DEBUG_CATEGORY_INIT(gst_nmossender_debug_category, "nmossender", 0, "NMOS Sender Plugin")) - -/* Pad template */ -static GstStaticPadTemplate sink_template = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS("video/x-raw, " - "format=(string){ I420, NV12, RGB } ")); - -/* Set properties so element variables can change depending on them */ -static void gst_nmossender_set_property(GObject* object, guint property_id, const GValue* value, GParamSpec* pspec) -{ - GstNmossender* self = GST_NMOSSENDER(object); - - switch(property_id) - { - case 1: - g_free(self->source_address); - self->source_address = g_value_dup_string(value); - g_object_set(G_OBJECT(self->udpsink), "host", self->source_address, NULL); - break; - - case 2: - g_free(self->interface_name); - self->interface_name = g_value_dup_string(value); - g_object_set(G_OBJECT(self->udpsink), "sync", TRUE, "bind-address", self->interface_name, NULL); - break; - - case 3: - g_free(self->destination_address); - self->destination_address = g_value_dup_string(value); - g_object_set(G_OBJECT(self->udpsink), "host", self->destination_address, NULL); - break; - - case 4: - g_free(self->destination_port); - self->destination_port = g_value_dup_string(value); - g_object_set(G_OBJECT(self->udpsink), "port", atoi(self->destination_port), NULL); - break; - - default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; - } -} - -static void gst_nmossender_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec) -{ - GstNmossender* self = GST_NMOSSENDER(object); - - switch(property_id) - { - case 1: g_value_set_string(value, self->source_address); break; - - case 2: g_value_set_string(value, self->interface_name); break; - - case 3: g_value_set_string(value, self->destination_address); break; - - case 4: g_value_set_string(value, self->destination_port); break; - - default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; - } -} - -/* Not needed for now: -Auxiliary function to validate if caps are compatible */ -// static gboolean gst_nmossender_validate_caps(GstCaps* caps) -// { -// gboolean valid = FALSE; - -// for(guint i = 0; i < gst_caps_get_size(caps); i++) -// { -// GstStructure* structure = gst_caps_get_structure(caps, i); - -// const gchar* media_type = gst_structure_get_name(structure); -// if(g_strcmp0(media_type, "video/x-raw") == 0) -// { -// valid = TRUE; -// break; -// } -// } - -// return valid; -// } - -/* Event handler for the sink pad */ -static gboolean gst_nmossender_sink_event(GstPad* pad, GstObject* parent, GstEvent* event) -{ - GstNmossender* self = GST_NMOSSENDER(parent); - - switch(GST_EVENT_TYPE(event)) - { - case GST_EVENT_CAPS: { - GstCaps* caps = NULL; - gst_event_parse_caps(event, &caps); - - if(caps) - { - gchar* caps_str = gst_caps_to_string(caps); - g_print("\n\nReceived CAPS from upstream: %s\n", caps_str); - - for(guint i = 0; i < gst_caps_get_size(caps); i++) - { - const GstStructure* structure = gst_caps_get_structure(caps, i); - // Convert the structure to a string - gchar* structure_str = gst_structure_to_string(structure); - GST_INFO_OBJECT(self, "Caps Structure %u: %s", i, structure_str); - g_free(structure_str); - - // If you want to retrieve individual fields (e.g., width, height, format) - gint width, height; - const gchar* format; - if(gst_structure_get_int(structure, "width", &width)) - { - self->width = width; - } - if(gst_structure_get_int(structure, "height", &height)) - { - self->height = height; - } - if((format = gst_structure_get_string(structure, "format"))) - { - self->format = format; - } - } - - g_free(caps_str); - } - else - { - GST_WARNING_OBJECT(self, "No caps found in CAPS event"); - } - break; - } - default: break; - } - - // g_print("Width: %d, Height: %d, Format: %s\n", self->width, self->height, self->format); - /* Pass the event downstream */ - return gst_pad_event_default(pad, parent, event); -} - -/* State Change so it doesn't boot NMOS without pipeline being set to playing */ -static GstStateChangeReturn gst_nmossender_change_state(GstElement* element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstNmossender* self = GST_NMOSSENDER(element); - - switch(transition) - { - case GST_STATE_CHANGE_NULL_TO_READY: { - // FIX ME: Example of using the nmos_client_create function, need to - // change it later so it isnt hardcoded and is instead a property - const char* config_location = - "/home/nmos/repos/nmos-sender-receiver-framework/cpp/demos/ossrf-nmos-api/config/nmos_plugin_config.json"; - - GST_INFO_OBJECT(self, "Initializing NMOS client..."); - - // FIX ME: change inner workings of plugin - nmos_client_t* client = nmos_client_create(config_location); - if(client) - { - nmos_client_add_device(client, config_location); - nmos_client_add_sender(client); - // nmos_client_add_receiver(client); - // nmos_client_remove_sender(client); - // nmos_client_remove_receiver(client); - GST_INFO_OBJECT(self, "NMOS client initialized successfully."); - } - else - { - GST_ERROR_OBJECT(self, "Failed to initialize NMOS client."); - } - } - break; - - default: break; - } - - // This is needed to work ¯\_(ツ)_/¯ - ret = GST_ELEMENT_CLASS(gst_nmossender_parent_class)->change_state(element, transition); - - return ret; -} - -/* Class initialization */ -static void gst_nmossender_class_init(GstNmossenderClass* klass) -{ - GObjectClass* object_class = G_OBJECT_CLASS(klass); - GstElementClass* element_class = GST_ELEMENT_CLASS(klass); - - gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_template)); - - object_class->set_property = gst_nmossender_set_property; - object_class->get_property = gst_nmossender_get_property; - - g_object_class_install_property(G_OBJECT_CLASS(klass), 1, - g_param_spec_string("source-address", "Source Address", - "The address of the source (default: 127.0.0.1)", "127.0.0.1", - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(G_OBJECT_CLASS(klass), 2, - g_param_spec_string("interface-name", "Interface Name", - "Name of the interface (default: wlp1s0)", "wlp1s0", - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(G_OBJECT_CLASS(klass), 3, - g_param_spec_string("destination-address", "Destination Address", - "The address of the destination (default: 127.0.0.1)", - "127.0.0.1", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(G_OBJECT_CLASS(klass), 4, - g_param_spec_string("destination-port", "Destination Port", - "Port of the destination (default: 9999)", "9999", - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gst_element_class_set_static_metadata(element_class, "NMOS Sender", "Sink/Network", - "Processes raw video and sends it over RTP and UDP to NMOS client", - "Luis Ferreira "); - - element_class->change_state = gst_nmossender_change_state; -} - -/* Object initialization */ -static void gst_nmossender_init(GstNmossender* self) -{ - self->payloader = gst_element_factory_make("rtpvrawpay", "payloader"); - self->udpsink = gst_element_factory_make("udpsink", "udpsink"); - - if(!self->payloader || !self->udpsink) - { - GST_ERROR_OBJECT(self, "Failed to create internal elements: payloader or udpsink"); - return; - } - - // default udpsink properties - g_object_set(G_OBJECT(self->udpsink), "host", "127.0.0.1", "port", 9999, NULL); - - // Add elements to the bin - gst_bin_add_many(GST_BIN(self), self->payloader, self->udpsink, NULL); - gst_element_link(self->payloader, self->udpsink); - - // create and configure the sink pad - GstPad* payloader_sinkpad = gst_element_get_static_pad(self->payloader, "sink"); - GstPad* sink_ghost_pad = gst_ghost_pad_new("sink", payloader_sinkpad); - - if(!sink_ghost_pad) - { - GST_ERROR_OBJECT(self, "Failed to create ghost pad for sink"); - gst_object_unref(payloader_sinkpad); - return; - } - - // added ghost pad to the element to enable - // connectivity between layers of element - gst_pad_set_event_function(sink_ghost_pad, gst_nmossender_sink_event); - - gst_element_add_pad(GST_ELEMENT(self), sink_ghost_pad); - - gst_object_unref(payloader_sinkpad); -} - -static gboolean plugin_init(GstPlugin* plugin) -{ - return gst_element_register(plugin, "nmossender", GST_RANK_NONE, GST_TYPE_NMOSSENDER); -} - -#define VERSION "0.1" -#define PACKAGE "gst-nmos-sender-plugin" -#define PACKAGE_NAME "AMWA NMOS Sender and Receiver Framework Plugins" -#define GST_PACKAGE_ORIGIN "https://www.amwa.tv/" -#define GST_PACKAGE_LICENSE "Apache-2.0" - -GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, nmossender, "Plugin to send to NMOS application video stream", - plugin_init, VERSION, GST_PACKAGE_LICENSE, PACKAGE_NAME, GST_PACKAGE_ORIGIN) \ No newline at end of file diff --git a/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.cpp b/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.cpp new file mode 100644 index 0000000..aa6fd7d --- /dev/null +++ b/cpp/libs/gst_nmos_sender_plugin/gst_nmos_sender_plugin.cpp @@ -0,0 +1,382 @@ + +/* GStreamer + * + * Copyright (C) <2024> Bisect Lda + * @author: Luís Ferreira + * + * 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., 51 Franklin Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include "bisect/json.h" +#include "utils/utils.h" +#include "ossrf/nmos/api/nmos_client.h" +#include +#include + +GST_DEBUG_CATEGORY_STATIC(gst_nmossender_debug_category); +#define GST_CAT_DEFAULT gst_nmossender_debug_category +#define GST_TYPE_NMOSSENDER (gst_nmossender_get_type()) +#define GST_NMOSSENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_NMOSSENDER, GstNmossender)) +#define GST_NMOSSENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_NMOSSENDER, GstNmossenderClass)) +#define GST_IS_NMOSSENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_NMOSSENDER)) +#define GST_IS_NMOSSENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_NMOSSENDER)) + +typedef struct _GstNmossender +{ + GstBin parent; + GstElement* payloader; + GstElement* udpsink; + ossrf::nmos_client_uptr client; + GstCaps* caps; + ConfigFields config; +} GstNmossender; + +typedef struct _GstNmossenderClass +{ + GstBinClass parent_class; +} GstNmossenderClass; + +enum class PropertyId : uint32_t +{ + NodeId = 1, + NodeConfigFileLocation = 2, + DeviceId = 3, + DeviceLabel = 4, + DeviceDescription = 5, + SenderId = 6, + SenderLabel = 7, + SenderDescription = 8, + SourceAddress = 9, + InterfaceName = 10, + DestinationAddress = 11, + DestinationPort = 12 +}; + +G_DEFINE_TYPE_WITH_CODE(GstNmossender, gst_nmossender, GST_TYPE_BIN, + GST_DEBUG_CATEGORY_INIT(gst_nmossender_debug_category, "nmossender", 0, "NMOS Sender Plugin")) + +/* Pad template */ +static GstStaticPadTemplate sink_template = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_REQUEST, + GST_STATIC_CAPS("video/x-raw, " + "format=(string){ I420, NV12, RGB } ")); + +/* Set properties so element variables can change depending on them */ +static void gst_nmossender_set_property(GObject* object, guint property_id, const GValue* value, GParamSpec* pspec) +{ + GstNmossender* self = GST_NMOSSENDER(object); + + switch(static_cast(property_id)) + { + case PropertyId::NodeId: + g_free(self->config.node.id); + self->config.node.id = g_value_dup_string(value); + break; + + case PropertyId::NodeConfigFileLocation: + g_free(self->config.node.configuration_location); + self->config.node.configuration_location = g_value_dup_string(value); + break; + + case PropertyId::DeviceId: + g_free(self->config.device.id); + self->config.device.id = g_value_dup_string(value); + break; + + case PropertyId::DeviceLabel: + g_free(self->config.device.label); + self->config.device.label = g_value_dup_string(value); + break; + + case PropertyId::DeviceDescription: + g_free(self->config.device.description); + self->config.device.description = g_value_dup_string(value); + break; + + case PropertyId::SenderId: + g_free(self->config.sender_id); + self->config.sender_id = g_value_dup_string(value); + break; + + case PropertyId::SenderLabel: + g_free(self->config.sender_label); + self->config.sender_label = g_value_dup_string(value); + break; + + case PropertyId::SenderDescription: + g_free(self->config.sender_description); + self->config.sender_description = g_value_dup_string(value); + break; + + case PropertyId::SourceAddress: + g_free(self->config.network.source_address); + self->config.network.source_address = g_value_dup_string(value); + break; + + case PropertyId::InterfaceName: + g_free(self->config.network.interface_name); + self->config.network.interface_name = g_value_dup_string(value); + g_object_set(G_OBJECT(self->udpsink), "bind_address", self->config.network.interface_name, NULL); + break; + + case PropertyId::DestinationAddress: + g_free(self->config.network.destination_address); + self->config.network.destination_address = g_value_dup_string(value); + g_object_set(G_OBJECT(self->udpsink), "host", self->config.network.destination_address, NULL); + break; + + case PropertyId::DestinationPort: + g_free(self->config.network.destination_port); + self->config.network.destination_port = g_value_dup_string(value); + g_object_set(G_OBJECT(self->udpsink), "port", atoi(self->config.network.destination_port), NULL); + break; + + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; + } +} + +static void gst_nmossender_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec) +{ + GstNmossender* self = GST_NMOSSENDER(object); + + switch(static_cast(property_id)) + { + case PropertyId::NodeId: g_value_set_string(value, self->config.node.id); break; + case PropertyId::NodeConfigFileLocation: g_value_set_string(value, self->config.node.configuration_location); break; + case PropertyId::DeviceId: g_value_set_string(value, self->config.device.id); break; + case PropertyId::DeviceLabel: g_value_set_string(value, self->config.device.label); break; + case PropertyId::DeviceDescription: g_value_set_string(value, self->config.device.description); break; + case PropertyId::SenderId: g_value_set_string(value, self->config.sender_id); break; + case PropertyId::SenderLabel: g_value_set_string(value, self->config.sender_label); break; + case PropertyId::SenderDescription: g_value_set_string(value, self->config.sender_description); break; + case PropertyId::SourceAddress: g_value_set_string(value, self->config.network.source_address); break; + case PropertyId::InterfaceName: g_value_set_string(value, self->config.network.interface_name); break; + case PropertyId::DestinationAddress: g_value_set_string(value, self->config.network.destination_address); break; + case PropertyId::DestinationPort: g_value_set_string(value, self->config.network.destination_port); break; + + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; + } +} + +/* Event handler for the sink pad */ +static gboolean gst_nmossender_sink_event(GstPad* pad, GstObject* parent, GstEvent* event) +{ + GstNmossender* self = GST_NMOSSENDER(parent); + + switch(GST_EVENT_TYPE(event)) + { + case GST_EVENT_CAPS: { + GstCaps* caps = NULL; + gst_event_parse_caps(event, &caps); + + if(caps) + { + gchar* caps_str = gst_caps_to_string(caps); + g_print("\n\nReceived CAPS from upstream: %s\n", caps_str); + + for(guint i = 0; i < gst_caps_get_size(caps); i++) + { + const GstStructure* structure = gst_caps_get_structure(caps, i); + // Convert the structure to a string + gchar* structure_str = gst_structure_to_string(structure); + GST_INFO_OBJECT(self, "Caps Structure %u: %s", i, structure_str); + g_free(structure_str); + + // FIXME: Need to retrieve other fields + gint width, height; + const gchar* format; + if(gst_structure_get_int(structure, "width", &width)) + { + self->config.media.width = width; + } + if(gst_structure_get_int(structure, "height", &height)) + { + self->config.media.height = height; + } + if((format = gst_structure_get_string(structure, "format"))) + { + g_free(self->config.media.sampling); + self->config.media.sampling = g_strdup(format); + } + } + + g_free(caps_str); + } + else + { + GST_WARNING_OBJECT(self, "No caps found in CAPS event"); + } + break; + } + default: break; + } + + g_print("Width: %d, Height: %d, Format: %s\n", self->config.media.width, self->config.media.height, + self->config.media.sampling); + // Pass the event downstream + return gst_pad_event_default(pad, parent, event); +} + +/* State Change so it doesn't boot NMOS without pipeline being set to playing */ +static GstStateChangeReturn gst_nmossender_change_state(GstElement* element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstNmossender* self = GST_NMOSSENDER(element); + + auto sender_activation_callback = [](bool master_enabled, const nlohmann::json& transport_params) { + fmt::print("nmos_sender_callback: master_enabled={}, transport_params={}\n", master_enabled, + transport_params.dump()); + }; + + switch(transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: { + + const auto node_config_json = create_node_config(self->config); + const auto device_config_json = create_device_config(self->config); + const auto sender_config_json = create_sender_config(self->config); + + g_print("Node Configuration: %s\n", node_config_json.dump().c_str()); + g_print("Device Configuration: %s\n", device_config_json.dump().c_str()); + g_print("Sender Configuration: %s\n", sender_config_json.dump().c_str()); + + auto result = ossrf::nmos_client_t::create(self->config.node.id, node_config_json.dump()); + if(result.has_value()) + { + self->client = std::move(result.value()); + self->client->add_device(device_config_json.dump()); + self->client->add_sender(self->config.device.id, sender_config_json.dump(), sender_activation_callback); + GST_INFO_OBJECT(self, "NMOS client initialized successfully."); + } + else + { + GST_ERROR_OBJECT(self, "Failed to initialize NMOS client."); + } + } + break; + + default: break; + } + + // This is needed to work ¯\_(ツ)_/¯ + ret = GST_ELEMENT_CLASS(gst_nmossender_parent_class)->change_state(element, transition); + + return ret; +} + +/* Helper function to add properties */ +static void add_property(GObjectClass* object_class, guint id, const gchar* name, const gchar* nick, const gchar* blurb, + const gchar* default_value) +{ + g_object_class_install_property(object_class, id, + g_param_spec_string(name, nick, blurb, default_value, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); +} + +/* Class initialization */ +static void gst_nmossender_class_init(GstNmossenderClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + GstElementClass* element_class = GST_ELEMENT_CLASS(klass); + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_template)); + + object_class->set_property = gst_nmossender_set_property; + object_class->get_property = gst_nmossender_get_property; + + // Add properties using helper function + add_property(object_class, 1, "node-id", "Node ID", "The ID of the node", "0aad3458-1081-4fba-af02-a8ebd9feeae3"); + add_property( + object_class, 2, "node-config-file-location", "Node Config File Location", + "The location of the configuration file for the node", + "/home/nmos/repos/nmos-sender-receiver-framework/cpp/demos/ossrf-nmos-api/config/nmos_plugin_config.json"); + add_property(object_class, 3, "device-id", "Device ID", "The ID of the device", + "b9b85f97-58db-41fe-934f-c2afbf7bd46f"); + add_property(object_class, 4, "device-label", "Device Label", "The label to identify the device", "OSSRF Device"); + add_property(object_class, 5, "device-description", "Device Description", "Description of the device", + "OSSRF Device Description"); + add_property(object_class, 6, "sender-id", "Sender ID", "The ID of the sender", + "e543a2c1-d6a2-47f5-8d14-296bb6714ef2"); + add_property(object_class, 7, "sender-label", "Sender Label", "Label to identify the sender", + "BISECT sender video"); + add_property(object_class, 8, "sender-description", "Sender Description", + "Description string to better describe the sender", "BISECT sender video"); + add_property(object_class, 9, "source-address", "Source Address", "The address of the source", "127.0.0.1"); + add_property(object_class, 10, "interface-name", "Interface Name", "Name of the interface", "wlp1s0"); + add_property(object_class, 11, "destination-address", "Destination Address", "The address of the destination", + "127.0.0.1"); + add_property(object_class, 12, "destination-port", "Destination Port", "Port of the destination", "9999"); + + gst_element_class_set_static_metadata(element_class, "NMOS Sender", "Sink/Network", + "Processes raw video and sends it over RTP and UDP to NMOS client", + "Luis Ferreira "); + + element_class->change_state = gst_nmossender_change_state; +} + +/* Object initialization */ +static void gst_nmossender_init(GstNmossender* self) +{ + self->payloader = gst_element_factory_make("rtpvrawpay", "payloader"); + self->udpsink = gst_element_factory_make("udpsink", "udpsink"); + + if(!self->payloader || !self->udpsink) + { + GST_ERROR_OBJECT(self, "Failed to create internal elements: payloader or udpsink"); + return; + } + + // default udpsink properties + g_object_set(G_OBJECT(self->udpsink), "host", "127.0.0.1", "port", 9999, NULL); + self->config = create_default_config_fields(); + + // Add elements to the bin + gst_bin_add_many(GST_BIN(self), self->payloader, self->udpsink, NULL); + gst_element_link(self->payloader, self->udpsink); + + // create and configure the sink pad + GstPad* payloader_sinkpad = gst_element_get_static_pad(self->payloader, "sink"); + GstPad* sink_ghost_pad = gst_ghost_pad_new("sink", payloader_sinkpad); + + if(!sink_ghost_pad) + { + GST_ERROR_OBJECT(self, "Failed to create ghost pad for sink"); + gst_object_unref(payloader_sinkpad); + return; + } + + // added ghost pad to the element to enable + // connectivity between layers of element + gst_pad_set_event_function(sink_ghost_pad, gst_nmossender_sink_event); + + gst_element_add_pad(GST_ELEMENT(self), sink_ghost_pad); + + gst_object_unref(payloader_sinkpad); +} + +static gboolean plugin_init(GstPlugin* plugin) +{ + return gst_element_register(plugin, "nmossender", GST_RANK_NONE, GST_TYPE_NMOSSENDER); +} + +#define VERSION "0.1" +#define PACKAGE "gst-nmos-sender-plugin" +#define PACKAGE_NAME "AMWA NMOS Sender and Receiver Framework Plugins" +#define GST_PACKAGE_ORIGIN "https://www.amwa.tv/" +#define GST_PACKAGE_LICENSE "Apache-2.0" + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, nmossender, "Plugin to send to NMOS application video stream", + plugin_init, VERSION, GST_PACKAGE_LICENSE, PACKAGE_NAME, GST_PACKAGE_ORIGIN) \ No newline at end of file diff --git a/cpp/libs/ossrf_c_nmos_api/CMakeLists.txt b/cpp/libs/gst_nmos_sender_plugin/utils/CMakeLists.txt similarity index 79% rename from cpp/libs/ossrf_c_nmos_api/CMakeLists.txt rename to cpp/libs/gst_nmos_sender_plugin/utils/CMakeLists.txt index 78c1b71..60124f7 100644 --- a/cpp/libs/ossrf_c_nmos_api/CMakeLists.txt +++ b/cpp/libs/gst_nmos_sender_plugin/utils/CMakeLists.txt @@ -1,7 +1,17 @@ cmake_minimum_required(VERSION 3.16) -project(ossrf_c_nmos_api LANGUAGES CXX C) +project(utils LANGUAGES CXX C) find_package(nlohmann_json REQUIRED) +find_package(PkgConfig REQUIRED) + +# Locate GLib package +pkg_check_modules(GLIB REQUIRED glib-2.0) + +# Include GLib directories and link libraries +include_directories(${GLIB_INCLUDE_DIRS}) +link_directories(${GLIB_LIBRARY_DIRS}) + +add_definitions(${GLIB_CFLAGS_OTHER}) file(GLOB_RECURSE ${PROJECT_NAME}_source_files *.cpp *.h) @@ -17,6 +27,7 @@ target_link_libraries( bisect::bisect_json nlohmann_json::nlohmann_json ossrf::ossrf_nmos_api + ${GLIB_LIBRARIES} ) set_target_properties( diff --git a/cpp/libs/gst_nmos_sender_plugin/utils/utils.cpp b/cpp/libs/gst_nmos_sender_plugin/utils/utils.cpp new file mode 100644 index 0000000..b29e0ae --- /dev/null +++ b/cpp/libs/gst_nmos_sender_plugin/utils/utils.cpp @@ -0,0 +1,192 @@ +#include "utils.h" +#include "ossrf/nmos/api/nmos_client.h" +#include "bisect/nmoscpp/configuration.h" +#include "bisect/expected/macros.h" +#include "bisect/expected.h" +#include "bisect/json.h" +#include + +using namespace bisect; +using json = nlohmann::json; + +ConfigFields create_default_config_fields() +{ + ConfigFields config; + + // Initialize NodeFields + config.node.id = g_strdup("d5504cd1-fe68-489d-99d4-20d3f075f062"); + config.node.configuration_location = g_strdup( + "/home/nmos/repos/nmos-sender-receiver-framework/cpp/demos/ossrf-nmos-api/config/nmos_plugin_node_config.json"); + + // Initialize DeviceFields + config.device.id = g_strdup("e92e628b-7421-4723-9fb9-c1f3b38af9d3"); + config.device.label = g_strdup("OSSRF Device2"); + config.device.description = g_strdup("OSSRF Device2"); + + // Initialize MediaFields + config.media.width = 640; + config.media.height = 480; + config.media.frame_rate_num = 50; + config.media.frame_rate_density = 1; + config.media.sampling = g_strdup("YCbCr-4:2:2"); + config.media.structure = g_strdup("progressive"); + + // Initialize NetworkFields + config.network.source_address = g_strdup("192.168.1.120"); + config.network.interface_name = g_strdup("wlp1s0"); + config.network.destination_address = g_strdup("192.168.1.120"); + config.network.destination_port = g_strdup("5004"); + + // Initialize ConfigFields + config.sender_id = g_strdup("e543a2c1-d6a2-47f5-8d14-296bb6714ef2"); + config.sender_label = g_strdup("BISECT OSSRF sender video"); + config.sender_description = g_strdup("BISECT OSSRF sender video"); + + return config; +} + +json create_node_config(const ConfigFields& config) +{ + const std::string node_config_str = get_node_config(config.node.configuration_location); + + // Parse the configuration into a JSON object + json node_config = json::parse(node_config_str); + + // Override the "id" field with the one from the ConfigFields structure + node_config["id"] = config.node.id; + + return node_config; +} + +json create_device_config(const ConfigFields& config) +{ + json device = { + {"id", config.device.id}, {"label", config.device.label}, {"description", config.device.description}}; + return device; +} + +json create_sender_config(const ConfigFields& config) +{ + json sender = {{"id", config.sender_id}, + {"label", config.sender_label}, + {"description", config.sender_description}, + {"network", + {{"primary", + {{"source_address", config.network.source_address}, + {"interface_name", config.network.interface_name}, + {"destination_address", config.network.destination_address}, + {"destination_port", config.network.destination_port}}}}}, + {"payload_type", 97}, + {"media_type", "video/raw"}, + {"media", + {{"width", config.media.width}, + {"height", config.media.height}, + {"frame_rate", {{"num", config.media.frame_rate_num}, {"den", config.media.frame_rate_density}}}, + {"sampling", config.media.sampling}, + {"structure", config.media.structure}}}}; + return sender; +} + +expected load_configuration_from_file(std::string_view config_file) +{ + std::ifstream ifs(config_file.data()); + BST_ENFORCE(ifs.is_open(), "Failed opening file {}", config_file); + std::ostringstream buffer; + buffer << ifs.rdbuf(); + return parse_json(buffer.str()); +} + +const std::string get_node_id(const char* node_configuration_location) +{ + + const auto configuration_result = load_configuration_from_file(node_configuration_location); + + const json& configuration = configuration_result.value(); + + auto node_result = find(configuration, "node"); + + const json& node = node_result.value(); + + auto node_id_result = find(node, "id"); + auto node_config_result = find(node, "configuration"); + + if(node_id_result.has_value() && node_config_result.has_value()) + { + const std::string node_id = node_id_result.value(); + const std::string node_configuration = node_config_result.value().dump(); + + return node_id; + } + + return ""; +} +const std::string get_node_config(const char* node_configuration_location) +{ + + const auto configuration_result = load_configuration_from_file(node_configuration_location); + + const json& configuration = configuration_result.value(); + + auto node_result = find(configuration, "node"); + + const json& node = node_result.value(); + + auto node_id_result = find(node, "id"); + auto node_config_result = find(node, "configuration"); + + if(node_id_result.has_value() && node_config_result.has_value()) + { + const std::string node_id = node_id_result.value(); + const std::string node_configuration = node_config_result.value().dump(); + + return node_configuration; + } + + return ""; +} + +const std::string get_device_id(const char* device_configuration_location) +{ + + const auto configuration_result = load_configuration_from_file(device_configuration_location); + + const json& configuration = configuration_result.value(); + + auto device_result = find(configuration, "device"); + + const json& device = device_result.value(); + + auto device_id_result = find(device, "id"); + + const std::string device_id = device_id_result.value(); + const std::string device_config = device.dump(); + + return device_id; +} + +const std::string get_device_config(const char* device_configuration_location) +{ + + const auto configuration_result = load_configuration_from_file(device_configuration_location); + + const json& configuration = configuration_result.value(); + + auto device_result = find(configuration, "device"); + + const json& device = device_result.value(); + + auto device_id_result = find(device, "id"); + + const std::string device_id = device_id_result.value(); + const std::string device_config = device.dump(); + + return device_config; +} + +const std::string get_sender_config(const char* sender_configuration_location) +{ + // FIX ME: Gonna be hardcoded for now + const auto sender_config = load_configuration_from_file(sender_configuration_location); + + return sender_config.value().dump(); +} diff --git a/cpp/libs/gst_nmos_sender_plugin/utils/utils.h b/cpp/libs/gst_nmos_sender_plugin/utils/utils.h new file mode 100644 index 0000000..9311252 --- /dev/null +++ b/cpp/libs/gst_nmos_sender_plugin/utils/utils.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include + +typedef struct NodeFields +{ + gchar* id; + gchar* configuration_location; +} NodeFields; + +typedef struct DeviceFields +{ + gchar* id; + gchar* label; + gchar* description; +} DeviceFields; + +typedef struct MediaFields +{ + gint width; + gint height; + gint frame_rate_num; + gint frame_rate_density; + gchar* sampling; + gchar* structure; +} MediaFields; + +typedef struct NetworkFields +{ + gchar* source_address; + gchar* interface_name; + gchar* destination_address; + gchar* destination_port; +} NetworkFields; + +typedef struct ConfigFields +{ + NodeFields node; + DeviceFields device; + MediaFields media; + gchar* sender_id; + gchar* sender_label; + gchar* sender_description; + NetworkFields network; +} ConfigFields; + +ConfigFields create_default_config_fields(); + +nlohmann::json create_node_config(const ConfigFields& config); +nlohmann::json create_device_config(const ConfigFields& config); +nlohmann::json create_sender_config(const ConfigFields& config); + +const std::string get_node_id(const char* node_configuration_location); + +const std::string get_node_config(const char* node_configuration_location); + +const std::string get_device_id(const char* device_configuration_location); + +const std::string get_device_config(const char* device_configuration_location); + +const std::string get_sender_config(const char* sender_configuration_location); + diff --git a/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.cpp b/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.cpp deleted file mode 100644 index 54ff65b..0000000 --- a/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "ossrf_c_nmos_api.h" -#include "ossrf/nmos/api/nmos_client.h" -#include -#include -#include "bisect/nmoscpp/configuration.h" -#include "bisect/expected/macros.h" -#include "bisect/expected.h" -#include "bisect/json.h" - -using namespace bisect; -using json = nlohmann::json; - -struct nmos_client -{ - std::unique_ptr client; - std::string device_id; - std::unique_ptr sender_callback; - std::unique_ptr receiver_callback; - std::string sender_info_config; - std::string receiver_info_config; -}; - -expected load_configuration_from_file(std::string_view config_file) -{ - std::ifstream ifs(config_file.data()); - BST_ENFORCE(ifs.is_open(), "Failed opening file {}", config_file); - std::ostringstream buffer; - buffer << ifs.rdbuf(); - return parse_json(buffer.str()); -} - -nmos_client_t* nmos_client_create(const char* node_configuration_location) -{ - if(!node_configuration_location) - { - return nullptr; - } - - const auto configuration_result = load_configuration_from_file(node_configuration_location); - if(!configuration_result.has_value()) - { - return nullptr; - } - - const json& configuration = configuration_result.value(); - - auto node_result = find(configuration, "node"); - if(!node_result.has_value()) - { - return nullptr; - } - - const json& node = node_result.value(); - - auto node_id_result = find(node, "id"); - auto node_config_result = find(node, "configuration"); - - if(node_id_result.has_value() && node_config_result.has_value()) - { - const std::string node_id = node_id_result.value(); - const std::string node_configuration = node_config_result.value().dump(); - - auto result = ossrf::nmos_client_t::create(node_id, node_configuration); - if(!result.has_value()) - { - return nullptr; - } - - // wrap result in C compatible structure - nmos_client_t* wrapper = new nmos_client_t; - wrapper->client = std::move(*result); - return wrapper; - } - - return nullptr; -} - -int nmos_client_add_device(nmos_client_t* wrapper, const char* node_configuration_location) -{ - if(!wrapper || !node_configuration_location) - { - return -1; - } - - const auto configuration_result = load_configuration_from_file(node_configuration_location); - if(!configuration_result.has_value()) - { - return -1; - } - - const json& configuration = configuration_result.value(); - - auto device_result = find(configuration, "device"); - if(!device_result.has_value()) - { - return -1; - } - - const json& device = device_result.value(); - - auto device_id_result = find(device, "id"); - if(!device_id_result.has_value()) - { - return -1; - } - - const std::string device_id = device_id_result.value(); - const std::string device_config = device.dump(); - - auto result = wrapper->client->add_device(device_config); - if(!result.has_value()) - { - return -1; - } - - wrapper->device_id = device_id; - - return 0; -} - -int nmos_client_add_sender(nmos_client_t* wrapper) -{ - auto sender_activation_callback = [](bool master_enabled, const nlohmann::json& transport_params) { - fmt::print("nmos_sender_callback: {} {}\n", master_enabled, transport_params.dump()); - }; - // FIX ME: Gonna be hardcoded for now - const auto sender_config = load_configuration_from_file( - "/home/nmos/repos/nmos-sender-receiver-framework/cpp/demos/ossrf-nmos-api/config/nmos_sender_config.json"); - - wrapper->client->add_sender(wrapper->device_id, sender_config.value().dump(), sender_activation_callback); - wrapper->sender_info_config = sender_config.value().dump(); - - return 0; -} - -int nmos_client_add_receiver(nmos_client_t* wrapper) -{ - auto receiver_activation_callback = [](const std::optional& sdp, bool master_enable) { - fmt::print("nmos_receiver_callback: {} no sdp\n", master_enable); - }; - // FIX ME: Gonna be hardcoded for now - const auto receiver_config = load_configuration_from_file( - "/home/nmos/repos/nmos-sender-receiver-framework/cpp/demos/ossrf-nmos-api/config/nmos_receiver_config.json"); - wrapper->client->add_receiver(wrapper->device_id, receiver_config.value().dump(), receiver_activation_callback); - wrapper->receiver_info_config = receiver_config.value().dump(); - - return 0; -} - -int nmos_client_remove_sender(nmos_client_t* wrapper) -{ - // FIX ME: Error catching - wrapper->client->remove_sender(wrapper->device_id, wrapper->sender_info_config); - return 0; -} - -int nmos_client_remove_receiver(nmos_client_t* wrapper) -{ - // FIX ME: Error catching - wrapper->client->remove_receiver(wrapper->device_id, wrapper->receiver_info_config); - return 0; -} \ No newline at end of file diff --git a/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.h b/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.h deleted file mode 100644 index 1dbed19..0000000 --- a/cpp/libs/ossrf_c_nmos_api/ossrf_c_nmos_api.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct nmos_client nmos_client_t; - -nmos_client_t* nmos_client_create(const char* node_configuration_location); - -int nmos_client_add_device(nmos_client_t* client, const char* node_configuration_location); - -int nmos_client_add_sender(nmos_client_t* client); - -int nmos_client_add_receiver(nmos_client_t* client); - -int nmos_client_remove_sender(nmos_client_t* client); - -int nmos_client_remove_receiver(nmos_client_t* client); - -#ifdef __cplusplus -} -#endif