From a495cff6427dd66806ea6a726cf00a24d8fe39b1 Mon Sep 17 00:00:00 2001 From: Magdalena Pytel Date: Tue, 4 Feb 2025 16:16:42 +0100 Subject: [PATCH 1/4] Add vfio error handling --- nmos/patches/nmos-cpp.patch | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/nmos/patches/nmos-cpp.patch b/nmos/patches/nmos-cpp.patch index 1f9bdcd..310799a 100644 --- a/nmos/patches/nmos-cpp.patch +++ b/nmos/patches/nmos-cpp.patch @@ -563,7 +563,7 @@ index e4b420f..0e905fa 100644 slog::log(gate, SLOG_FLF) << "Preparing for connections"; diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp -index f12cca1..5affe4d 100644 +index f12cca1..bfe8143 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1,3 +1,9 @@ @@ -1895,7 +1895,7 @@ index f12cca1..5affe4d 100644 { auto handle_load_ca_certificates = nmos::make_load_ca_certificates_handler(model.settings, gate); // this example uses this callback to (un)subscribe a IS-07 Events WebSocket receiver when it is activated -@@ -1666,14 +1009,181 @@ nmos::connection_activation_handler make_node_implementation_connection_activati +@@ -1666,14 +1009,187 @@ nmos::connection_activation_handler make_node_implementation_connection_activati auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_load_ca_certificates, handle_events_ws_message, handle_close, model.settings, gate); // this example uses this callback to update IS-12 Receiver-Monitor connection status auto receiver_monitor_connection_activation_handler = nmos::make_receiver_monitor_connection_activation_handler(model.control_protocol_resources); @@ -1908,9 +1908,11 @@ index f12cca1..5affe4d 100644 + if(id_type.second == nmos::types::sender) + { + const char* vfio_port = "VFIO_PORT_TX"; -+ const char* vfio_port_value = std::getenv(vfio_port); -+ //to do add error handling for vfio_port_value -> info about setting up vfio port -+ //add readme about mtl ++ const char* vfio_port_value_tx = std::getenv(vfio_port); ++ if (!vfio_port_value_tx) { ++ slog::log(gate, SLOG_FLF) << "VFIO_PORT_TX environment variable is not set. You should export one of the virtual function interface port values."; ++ return; ++ } + std::thread ffmpegThread1; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "this is "<< id_type << "---> sends json for sender"; + auto data = connection_resource.data; @@ -1941,7 +1943,7 @@ index f12cca1..5affe4d 100644 + else if(config_by_id.stream_type.type==stream_type::st2110) + { + s.stream_type.type = stream_type::st2110; -+ s.stream_type.st2110.network_interface = vfio_port_value; ++ s.stream_type.st2110.network_interface = vfio_port_value_tx; + s.stream_type.st2110.local_ip = sender_source_ip.as_string(); + s.stream_type.st2110.remote_ip = receiver_destination_ip.as_string(); + s.stream_type.st2110.transport = config_by_id.stream_type.st2110.transport; @@ -1975,14 +1977,18 @@ index f12cca1..5affe4d 100644 + const Config config = {{s}, ffmpeg_receiver_as_file_vector, configIntel.function, multiviewer_columns, configIntel.gpu_hw_acceleration, gpu_hw_acceleration_device, configIntel.logging_level}; + + ffmpegThread1=std::thread(grpc::sendDataToFfmpeg, impl::fields::ffmpeg_grpc_server_address(model.settings), impl::fields::ffmpeg_grpc_server_port(model.settings), config); - ++ + ffmpegThread1.join(); +} + if(id_type.second == nmos::types::receiver) + { + std::thread ffmpegThread2; + const char* vfio_port = "VFIO_PORT_RX"; -+ const char* vfio_port_value = std::getenv(vfio_port); ++ const char* vfio_port_value_rx = std::getenv(vfio_port); ++ if (!vfio_port_value_rx) { ++ slog::log(gate, SLOG_FLF) << "VFIO_PORT_RX environment variable is not set. You should export one of the virtual function interface port values."; ++ return; ++ } + auto data = connection_resource.data; + auto receiver_source_ip = data[nmos::fields::endpoint_active][nmos::fields::transport_params][0][nmos::fields::source_ip]; + auto sender_destination_port = data[nmos::fields::endpoint_active][nmos::fields::transport_params][0][nmos::fields::destination_port]; @@ -2041,7 +2047,7 @@ index f12cca1..5affe4d 100644 + else if(config_by_id.stream_type.type==stream_type::st2110) + { + s.stream_type.type = stream_type::st2110; -+ s.stream_type.st2110.network_interface = vfio_port_value; ++ s.stream_type.st2110.network_interface = vfio_port_value_rx; + s.stream_type.st2110.local_ip = receiver_source_ip.as_string(); + s.stream_type.st2110.remote_ip = destination_addr; + s.stream_type.st2110.transport = config_by_id.stream_type.st2110.transport; @@ -2054,7 +2060,7 @@ index f12cca1..5affe4d 100644 + payload.video = v; + + s.payload = payload; -+ + + //receiver=nmos, sender=ffmpeg + // to get ffmpeg senders of stream_type::File + auto configIntel = config_manager.get_config(); @@ -2081,7 +2087,7 @@ index f12cca1..5affe4d 100644 receiver_monitor_connection_activation_handler(connection_resource); }; } -@@ -1716,7 +1226,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control +@@ -1716,7 +1232,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control namespace impl { @@ -2090,7 +2096,7 @@ index f12cca1..5affe4d 100644 { if (settings.has_field(impl::fields::interlace_mode)) { -@@ -1726,8 +1236,8 @@ namespace impl +@@ -1726,8 +1242,8 @@ namespace impl // for 1080i formats, ST 2110-20 says that "the fields of an interlaced image are transmitted in time order, // first field first [and] the sample rows of the temporally second field are displaced vertically 'below' the // like-numbered sample rows of the temporally first field." @@ -2101,7 +2107,7 @@ index f12cca1..5affe4d 100644 return (nmos::rates::rate25 == frame_rate || nmos::rates::rate29_97 == frame_rate) && 1080 == frame_height ? nmos::interlace_modes::interlaced_tff : nmos::interlace_modes::progressive; -@@ -1752,6 +1262,15 @@ namespace impl +@@ -1752,6 +1268,15 @@ namespace impl })); } @@ -2117,7 +2123,7 @@ index f12cca1..5affe4d 100644 // find interface with the specified address std::vector::const_iterator find_interface(const std::vector& interfaces, const utility::string_t& address) { -@@ -1853,7 +1372,7 @@ namespace impl +@@ -1853,7 +1378,7 @@ namespace impl // This constructs all the callbacks used to integrate the example device-specific underlying implementation // into the server instance for the NMOS Node. @@ -2126,7 +2132,7 @@ index f12cca1..5affe4d 100644 { return nmos::experimental::node_implementation() .on_load_server_certificates(nmos::make_load_server_certificates_handler(model.settings, gate)) -@@ -1861,12 +1380,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode +@@ -1861,12 +1386,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_load_ca_certificates(nmos::make_load_ca_certificates_handler(model.settings, gate)) .on_system_changed(make_node_implementation_system_global_handler(model, gate)) // may be omitted if not required .on_registration_changed(make_node_implementation_registration_handler(gate)) // may be omitted if not required From 0628d24e3c9a711852e1cdfc0ca4b9f8b1ea46e9 Mon Sep 17 00:00:00 2001 From: Magdalena Pytel Date: Tue, 4 Feb 2025 23:09:43 +0100 Subject: [PATCH 2/4] fix payload and type parsing --- nmos/patches/nmos-cpp.patch | 162 +++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/nmos/patches/nmos-cpp.patch b/nmos/patches/nmos-cpp.patch index 310799a..1a303d7 100644 --- a/nmos/patches/nmos-cpp.patch +++ b/nmos/patches/nmos-cpp.patch @@ -61,7 +61,7 @@ index aa55298..fdc9ace 100644 CMakeDeps diff --git a/Development/nmos-cpp-node/intel-node-rx.json b/Development/nmos-cpp-node/intel-node-rx.json new file mode 100644 -index 0000000..c887ccb +index 0000000..eb43666 --- /dev/null +++ b/Development/nmos-cpp-node/intel-node-rx.json @@ -0,0 +1,66 @@ @@ -69,19 +69,21 @@ index 0000000..c887ccb + "logging_level": 0, + "http_port": 95, + "label": "intel-broadcast-suite", ++ "activate_senders": false, + "senders": ["v"], + "senders_count": [0], + "receivers": ["v"], + "receivers_count": [1], + "device_tags": { -+ "pipeline": ["rx-sender"] ++ "pipeline": ["rx"] + }, -+ "function": "rx-sender", ++ "function": "rx", + "gpu_hw_acceleration": "none", + "domain": "local", + "ffmpeg_grpc_server_address": "localhost", + "ffmpeg_grpc_server_port": "50052", + "receiver_payload_type":112, ++ "frame_rate": { "numerator": 60000, "denominator": 1001 }, + "sender": [{ + "stream_payload": { + "video": { @@ -108,7 +110,7 @@ index 0000000..c887ccb + "receiver": [{ + "stream_payload": { + "video": { -+ "frame_width": 960, ++ "frame_width": 1920, + "frame_height": 1080, + "frame_rate": { "numerator": 60, "denominator": 1 }, + "pixel_format": "yuv422p10le", @@ -122,11 +124,9 @@ index 0000000..c887ccb + } + }, + "stream_type": { -+ "mcm": { -+ "conn_type": "st2110", ++ "st2110": { + "transport": "st2110-20", -+ "urn": "NULL", -+ "transportPixelFormat": "yuv422p10le" ++ "payloadType" : 112 + } + } + }] @@ -134,10 +134,10 @@ index 0000000..c887ccb \ No newline at end of file diff --git a/Development/nmos-cpp-node/intel-node-tx.json b/Development/nmos-cpp-node/intel-node-tx.json new file mode 100644 -index 0000000..866532b +index 0000000..87af8dc --- /dev/null +++ b/Development/nmos-cpp-node/intel-node-tx.json -@@ -0,0 +1,66 @@ +@@ -0,0 +1,65 @@ +{ + "logging_level": 0, + "http_port": 90, @@ -147,14 +147,15 @@ index 0000000..866532b + "receivers": ["v"], + "receivers_count": [0], + "device_tags": { -+ "pipeline": ["tx-sender"] ++ "pipeline": ["tx"] + }, -+ "function": "tx-sender", ++ "function": "tx", + "gpu_hw_acceleration": "none", + "domain": "local", + "ffmpeg_grpc_server_address": "localhost", + "ffmpeg_grpc_server_port": "50051", + "sender_payload_type":112, ++ "frame_rate": { "numerator": 60000, "denominator": 1001 }, + "sender": [{ + "stream_payload": { + "video": { @@ -172,11 +173,9 @@ index 0000000..866532b + } + }, + "stream_type": { -+ "mcm": { -+ "conn_type": "st2110", ++ "st2110": { + "transport": "st2110-20", -+ "urn": "NULL", -+ "transportPixelFormat": "yuv422p10le" ++ "payloadType" : 112 + } + } + }], @@ -207,17 +206,15 @@ index 0000000..866532b \ No newline at end of file diff --git a/Development/nmos-cpp-node/intel_config_parser.cpp b/Development/nmos-cpp-node/intel_config_parser.cpp new file mode 100644 -index 0000000..45cd068 +index 0000000..9b0a1c5 --- /dev/null +++ b/Development/nmos-cpp-node/intel_config_parser.cpp -@@ -0,0 +1,147 @@ +@@ -0,0 +1,145 @@ +#include "intel_config_parser.h" +#include +#include +#include + -+ -+ +const Config& ConfigManager::get_config() const { + return config; +} @@ -563,7 +560,7 @@ index e4b420f..0e905fa 100644 slog::log(gate, SLOG_FLF) << "Preparing for connections"; diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp -index f12cca1..bfe8143 100644 +index f12cca1..5d7291c 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1,3 +1,9 @@ @@ -637,7 +634,7 @@ index f12cca1..bfe8143 100644 // activate_senders: controls whether to activate senders on start up (true, default) or not (false) const web::json::field_as_bool_or activate_senders{ U("activate_senders"), true }; -@@ -83,19 +128,26 @@ namespace impl +@@ -83,19 +128,31 @@ namespace impl const web::json::field_as_value_or senders{ U("senders"), {} }; const web::json::field_as_value_or receivers{ U("receivers"), {} }; @@ -645,10 +642,6 @@ index f12cca1..bfe8143 100644 - // and the equivalent parameter constraint on video receivers - // the value must be an object like { "numerator": 25, "denominator": 1 } - // hm, unfortunately can't use nmos::make_rational(nmos::rates::rate25) during static initialization -- const web::json::field_as_value_or frame_rate{ U("frame_rate"), web::json::value_of({ -- { nmos::fields::numerator, 25 }, -- { nmos::fields::denominator, 1 } -- }) }; + const web::json::field_as_array sender{ U("sender") }; + const web::json::field_as_array receiver{ U("receiver") }; + @@ -657,25 +650,31 @@ index f12cca1..bfe8143 100644 + // it means that there are 3 senders of type video, 1 sender of type audio and 1 sender of type data + const web::json::field_as_value_or senders_count{ U("senders_count"), {} }; + const web::json::field_as_value_or receivers_count{ U("receivers_count"), {} }; - -- // frame_width, frame_height: control the frame_width and frame_height of video flows -- const web::json::field_as_integer_or frame_width{ U("frame_width"), 1920 }; -- const web::json::field_as_integer_or frame_height{ U("frame_height"), 1080 }; ++ + // sender_payload_type, receiver_payload_type: controls the payload_type of senders and receivers + // TODO: change the reference to Config by stream sender or receiver + const web::json::field_as_integer_or sender_payload_type{ U("sender_payload_type"), 112 }; + const web::json::field_as_integer_or receiver_payload_type{ U("receiver_payload_type"), 112 }; - ++ + // IP address and port to connect to ffmpeg grpc service in order to pass properties when another NMOS node + // is connected to this NMOS node for BCS pipeline + const web::json::field_as_string_or ffmpeg_grpc_server_address{ U("ffmpeg_grpc_server_address"), "localhost"}; + const web::json::field_as_string_or ffmpeg_grpc_server_port{ U("ffmpeg_grpc_server_port"), "50051"}; + + const web::json::field_as_value_or frame_rate{ U("frame_rate"), web::json::value_of({ + { nmos::fields::numerator, 25 }, + { nmos::fields::denominator, 1 } + }) }; + +- // frame_width, frame_height: control the frame_width and frame_height of video flows +- const web::json::field_as_integer_or frame_width{ U("frame_width"), 1920 }; +- const web::json::field_as_integer_or frame_height{ U("frame_height"), 1080 }; +- + // In curret use case, below params are default values // interlace_mode: controls the interlace_mode of video flows, see nmos::interlace_mode // when omitted, a default of "progressive" or "interlaced_tff" is used based on the frame_rate, etc. const web::json::field_as_string interlace_mode{ U("interlace_mode") }; -@@ -122,7 +174,7 @@ namespace impl +@@ -122,7 +179,7 @@ namespace impl const web::json::field_as_bool_or smpte2022_7{ U("smpte2022_7"), true }; } @@ -684,7 +683,7 @@ index f12cca1..bfe8143 100644 // the different kinds of 'port' (standing for the format/media type/event type) implemented by the example node // each 'port' of the example node has a source, flow, sender and/or compatible receiver -@@ -155,6 +207,7 @@ namespace impl +@@ -155,6 +212,7 @@ namespace impl bool is_rtp_port(const port& port); bool is_ws_port(const port& port); std::vector parse_ports(const web::json::value& value); @@ -692,7 +691,7 @@ index f12cca1..bfe8143 100644 const std::vector channels_repeat{ { U("Left Channel"), nmos::channel_symbols::L }, -@@ -191,23 +244,23 @@ namespace impl +@@ -191,23 +249,23 @@ namespace impl } // forward declarations for node_implementation_thread @@ -721,7 +720,7 @@ index f12cca1..bfe8143 100644 node_implementation_run(model, gate); } catch (const node_implementation_init_exception&) -@@ -237,7 +290,7 @@ void node_implementation_thread(nmos::node_model& model, nmos::experimental::con +@@ -237,7 +295,7 @@ void node_implementation_thread(nmos::node_model& model, nmos::experimental::con } } @@ -730,7 +729,7 @@ index f12cca1..bfe8143 100644 { using web::json::value; using web::json::value_from_elements; -@@ -248,29 +301,45 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -248,29 +306,46 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); @@ -759,13 +758,13 @@ index f12cca1..bfe8143 100644 const auto receiver_ports = impl::parse_ports(impl::fields::receivers(model.settings)); const auto rtp_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto ws_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_ws_port)); -- const auto frame_rate = nmos::parse_rational(impl::fields::frame_rate(model.settings)); -- const auto frame_width = impl::fields::frame_width(model.settings); -- const auto frame_height = impl::fields::frame_height(model.settings); -- const auto interlace_mode = impl::get_interlace_mode(model.settings); + + //generic values for whole node + // const auto interlace_mode = impl::get_interlace_mode(model.settings); + const auto frame_rate = nmos::parse_rational(impl::fields::frame_rate(model.settings)); +- const auto frame_width = impl::fields::frame_width(model.settings); +- const auto frame_height = impl::fields::frame_height(model.settings); +- const auto interlace_mode = impl::get_interlace_mode(model.settings); const auto colorspace = nmos::colorspace{ impl::fields::colorspace(model.settings) }; const auto transfer_characteristic = nmos::transfer_characteristic{ impl::fields::transfer_characteristic(model.settings) }; const auto sampling = sdp::sampling{ impl::fields::color_sampling(model.settings) }; @@ -783,7 +782,7 @@ index f12cca1..bfe8143 100644 const auto sublevel = nmos::sublevels::Sublev3bpp; const auto max_bits_per_pixel = 4.0; // min coding efficiency const auto bits_per_pixel = 2.0; -@@ -279,6 +348,15 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -279,6 +354,15 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // any delay between updates to the model resources is unnecessary unless for debugging purposes const unsigned int delay_millis{ 0 }; @@ -799,7 +798,7 @@ index f12cca1..bfe8143 100644 // it is important that the model be locked before inserting, updating or deleting a resource // and that the the node behaviour thread be notified after doing so const auto insert_resource_after = [&model, &lock](unsigned int milliseconds, nmos::resources& resources, nmos::resource&& resource, slog::base_gate& gate) -@@ -293,42 +371,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -293,42 +377,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr else slog::log(gate, SLOG_FLF) << "Model update error: " << id_type; @@ -845,7 +844,7 @@ index f12cca1..bfe8143 100644 { auto node = nmos::make_node(node_id, clocks, nmos::make_node_interfaces(interfaces), model.settings); node.data[nmos::fields::tags] = impl::fields::node_tags(model.settings); -@@ -352,7 +408,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -352,7 +414,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; throw node_implementation_init_exception(); } @@ -854,7 +853,7 @@ index f12cca1..bfe8143 100644 // hmm, should probably add a custom setting to control the primary and secondary interfaces for the example node's RTP senders and receivers // rather than just picking the one(s) corresponding to the first and last of the specified host addresses const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; -@@ -369,32 +425,59 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -369,32 +431,59 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto interface_names = smpte2022_7 ? std::vector{ primary_interface.name, secondary_interface.name } : std::vector{ primary_interface.name }; @@ -892,12 +891,12 @@ index f12cca1..bfe8143 100644 + auto senderDefinition = configIntel.senders[index]; + -+ const auto frame_rate_json_format = web::json::value_of({ -+ { nmos::fields::numerator, config_manager.get_framerate(senderDefinition).first }, -+ { nmos::fields::denominator, config_manager.get_framerate(senderDefinition).first } -+ }); ++ // const auto frame_rate_json_format = web::json::value_of({ ++ // { nmos::fields::numerator, config_manager.get_framerate(senderDefinition).first }, ++ // { nmos::fields::denominator, config_manager.get_framerate(senderDefinition).first } ++ // }); + -+ const auto frame_rate = nmos::parse_rational(frame_rate_json_format); ++ // const auto frame_rate = nmos::parse_rational(frame_rate_json_format); + const auto frame_width = senderDefinition.payload.video.frame_width; + const auto frame_height = senderDefinition.payload.video.frame_height; + const auto level = nmos::get_video_jxsv_level(frame_rate, frame_width, frame_height); @@ -923,7 +922,7 @@ index f12cca1..bfe8143 100644 { const auto channels = boost::copy_range>(boost::irange(0, channel_count) | boost::adaptors::transformed([&](const int& index) { -@@ -403,11 +486,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -403,11 +492,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, channels, model.settings); } @@ -937,7 +936,7 @@ index f12cca1..bfe8143 100644 { source = nmos::make_mux_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); } -@@ -478,6 +561,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -478,6 +567,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto manifest_href = nmos::experimental::make_manifest_api_manifest(sender_id, model.settings); auto sender = nmos::make_sender(sender_id, flow_id, nmos::transports::rtp, device_id, manifest_href.to_string(), interface_names, model.settings); @@ -945,7 +944,7 @@ index f12cca1..bfe8143 100644 // hm, could add nmos::make_video_jxsv_sender to encapsulate this? if (impl::ports::video == port && nmos::media_types::video_jxsv == video_type) { -@@ -518,17 +602,34 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -518,17 +608,34 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } } @@ -960,11 +959,11 @@ index f12cca1..bfe8143 100644 - + auto configIntel = config_manager.get_config(); + auto receiverDefinition = configIntel.receivers[index]; -+ const auto frame_rate_json_format = web::json::value_of({ -+ { nmos::fields::numerator, config_manager.get_framerate(receiverDefinition).first }, -+ { nmos::fields::denominator, config_manager.get_framerate(receiverDefinition).first } -+ }); -+ const auto frame_rate = nmos::parse_rational(frame_rate_json_format); ++ // const auto frame_rate_json_format = web::json::value_of({ ++ // { nmos::fields::numerator, config_manager.get_framerate(receiverDefinition).first }, ++ // { nmos::fields::denominator, config_manager.get_framerate(receiverDefinition).first } ++ // }); ++ // const auto frame_rate = nmos::parse_rational(frame_rate_json_format); + const auto frame_width = receiverDefinition.payload.video.frame_width; + const auto frame_height = receiverDefinition.payload.video.frame_height; + const auto level = nmos::get_video_jxsv_level(frame_rate, frame_width, frame_height); @@ -986,7 +985,7 @@ index f12cca1..bfe8143 100644 // add an example constraint set; these should be completed fully! if (nmos::media_types::video_raw == video_type) { -@@ -627,773 +728,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -627,773 +734,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_receiver), gate)) throw node_implementation_init_exception(); } } @@ -1760,7 +1759,7 @@ index f12cca1..bfe8143 100644 } // Example System API node behaviour callback to perform application-specific operations when the global configuration resource changes -@@ -1438,14 +776,14 @@ nmos::registration_handler make_node_implementation_registration_handler(slog::b +@@ -1438,14 +782,14 @@ nmos::registration_handler make_node_implementation_registration_handler(slog::b } // Example Connection API callback to parse "transport_file" during a PATCH /staged request @@ -1777,7 +1776,7 @@ index f12cca1..bfe8143 100644 { if (nmos::media_types::video_jxsv == nmos::get_media_type(sdp_params)) { -@@ -1462,7 +800,7 @@ nmos::transport_file_parser make_node_implementation_transport_file_parser() +@@ -1462,7 +806,7 @@ nmos::transport_file_parser make_node_implementation_transport_file_parser() } // Example Connection API callback to perform application-specific validation of the merged /staged endpoint during a PATCH /staged request @@ -1786,7 +1785,7 @@ index f12cca1..bfe8143 100644 { // this example uses an 'empty' std::function because it does not need to do any validation // beyond what is expressed by the schemas and /constraints endpoint -@@ -1470,26 +808,28 @@ nmos::details::connection_resource_patch_validator make_node_implementation_patc +@@ -1470,26 +814,28 @@ nmos::details::connection_resource_patch_validator make_node_implementation_patc } // Example Connection API activation callback to resolve "auto" values when /staged is transitioned to /active @@ -1823,7 +1822,7 @@ index f12cca1..bfe8143 100644 { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread -@@ -1528,21 +868,23 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c +@@ -1528,21 +874,23 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c } // Example Connection API activation callback to update senders' /transportfile endpoint - captures node_resources by reference! @@ -1854,7 +1853,7 @@ index f12cca1..bfe8143 100644 { const auto found = boost::range::find(rtp_sender_ids, connection_sender.id); if (rtp_sender_ids.end() != found) -@@ -1576,13 +918,13 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf +@@ -1576,13 +924,13 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf const nmos::media_type video_type{ nmos::fields::media_type(flow->data) }; if (nmos::media_types::video_raw == video_type) { @@ -1870,7 +1869,7 @@ index f12cca1..bfe8143 100644 } else { -@@ -1621,10 +963,11 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf +@@ -1621,10 +969,11 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf nmos::events_ws_message_handler make_node_implementation_events_ws_message_handler(const nmos::node_model& model, slog::base_gate& gate) { const auto seed_id = nmos::experimental::fields::seed_id(model.settings); @@ -1884,7 +1883,7 @@ index f12cca1..bfe8143 100644 // the message handler will be used for all Events WebSocket connections, and each connection may potentially // have subscriptions to a number of sources, for multiple receivers, so this example uses a handler adaptor -@@ -1654,8 +997,8 @@ nmos::events_ws_message_handler make_node_implementation_events_ws_message_handl +@@ -1654,8 +1003,8 @@ nmos::events_ws_message_handler make_node_implementation_events_ws_message_handl }, gate); } @@ -1895,7 +1894,7 @@ index f12cca1..bfe8143 100644 { auto handle_load_ca_certificates = nmos::make_load_ca_certificates_handler(model.settings, gate); // this example uses this callback to (un)subscribe a IS-07 Events WebSocket receiver when it is activated -@@ -1666,14 +1009,187 @@ nmos::connection_activation_handler make_node_implementation_connection_activati +@@ -1666,14 +1015,198 @@ nmos::connection_activation_handler make_node_implementation_connection_activati auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_load_ca_certificates, handle_events_ws_message, handle_close, model.settings, gate); // this example uses this callback to update IS-12 Receiver-Monitor connection status auto receiver_monitor_connection_activation_handler = nmos::make_receiver_monitor_connection_activation_handler(model.control_protocol_resources); @@ -1961,7 +1960,14 @@ index f12cca1..bfe8143 100644 + //sender=nmos, receiver=ffmpeg + // to get ffmpeg receivers of stream_type::File + auto configIntel = config_manager.get_config(); ++ config_manager.print_config(); ++ + auto ffmpeg_receiver_as_file_vector = tracker::get_file_streams_receivers(configIntel); ++ for (auto& stream_receiver : ffmpeg_receiver_as_file_vector) { ++ stream_receiver.payload.type = payload_type::video; ++ std::cout<<"Ffmpeg RX file -> frame_width: "< frame_width: "<::const_iterator find_interface(const std::vector& interfaces, const utility::string_t& address) { -@@ -1853,7 +1378,7 @@ namespace impl +@@ -1853,7 +1395,7 @@ namespace impl // This constructs all the callbacks used to integrate the example device-specific underlying implementation // into the server instance for the NMOS Node. @@ -2132,7 +2142,7 @@ index f12cca1..bfe8143 100644 { return nmos::experimental::node_implementation() .on_load_server_certificates(nmos::make_load_server_certificates_handler(model.settings, gate)) -@@ -1861,12 +1386,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode +@@ -1861,12 +1403,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_load_ca_certificates(nmos::make_load_ca_certificates_handler(model.settings, gate)) .on_system_changed(make_node_implementation_system_global_handler(model, gate)) // may be omitted if not required .on_registration_changed(make_node_implementation_registration_handler(gate)) // may be omitted if not required From 80ade19152432c7cd466652b875fb805c01a2817 Mon Sep 17 00:00:00 2001 From: Magdalena Pytel Date: Wed, 5 Feb 2025 12:31:16 +0100 Subject: [PATCH 3/4] sdp utils --- nmos/patches/nmos-cpp.patch | 290 ++++++++++++++++++++++++++++-------- 1 file changed, 232 insertions(+), 58 deletions(-) diff --git a/nmos/patches/nmos-cpp.patch b/nmos/patches/nmos-cpp.patch index 1a303d7..d20efaf 100644 --- a/nmos/patches/nmos-cpp.patch +++ b/nmos/patches/nmos-cpp.patch @@ -61,10 +61,10 @@ index aa55298..fdc9ace 100644 CMakeDeps diff --git a/Development/nmos-cpp-node/intel-node-rx.json b/Development/nmos-cpp-node/intel-node-rx.json new file mode 100644 -index 0000000..eb43666 +index 0000000..806b649 --- /dev/null +++ b/Development/nmos-cpp-node/intel-node-rx.json -@@ -0,0 +1,66 @@ +@@ -0,0 +1,67 @@ +{ + "logging_level": 0, + "http_port": 95, @@ -79,6 +79,7 @@ index 0000000..eb43666 + }, + "function": "rx", + "gpu_hw_acceleration": "none", ++ "color_sampling": "YCbCr-4:2:2", + "domain": "local", + "ffmpeg_grpc_server_address": "localhost", + "ffmpeg_grpc_server_port": "50052", @@ -134,10 +135,10 @@ index 0000000..eb43666 \ No newline at end of file diff --git a/Development/nmos-cpp-node/intel-node-tx.json b/Development/nmos-cpp-node/intel-node-tx.json new file mode 100644 -index 0000000..87af8dc +index 0000000..f389a50 --- /dev/null +++ b/Development/nmos-cpp-node/intel-node-tx.json -@@ -0,0 +1,65 @@ +@@ -0,0 +1,66 @@ +{ + "logging_level": 0, + "http_port": 90, @@ -149,6 +150,7 @@ index 0000000..87af8dc + "device_tags": { + "pipeline": ["tx"] + }, ++ "color_sampling": "YCbCr-4:2:2", + "function": "tx", + "gpu_hw_acceleration": "none", + "domain": "local", @@ -159,7 +161,7 @@ index 0000000..87af8dc + "sender": [{ + "stream_payload": { + "video": { -+ "frame_width": 960, ++ "frame_width": 1920, + "frame_height": 1080, + "frame_rate": { "numerator": 60, "denominator": 1 }, + "pixel_format": "yuv422p10le", @@ -560,7 +562,7 @@ index e4b420f..0e905fa 100644 slog::log(gate, SLOG_FLF) << "Preparing for connections"; diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp -index f12cca1..5d7291c 100644 +index f12cca1..00722e7 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1,3 +1,9 @@ @@ -634,7 +636,7 @@ index f12cca1..5d7291c 100644 // activate_senders: controls whether to activate senders on start up (true, default) or not (false) const web::json::field_as_bool_or activate_senders{ U("activate_senders"), true }; -@@ -83,19 +128,31 @@ namespace impl +@@ -83,19 +128,35 @@ namespace impl const web::json::field_as_value_or senders{ U("senders"), {} }; const web::json::field_as_value_or receivers{ U("receivers"), {} }; @@ -667,14 +669,15 @@ index f12cca1..5d7291c 100644 }) }; - // frame_width, frame_height: control the frame_width and frame_height of video flows -- const web::json::field_as_integer_or frame_width{ U("frame_width"), 1920 }; -- const web::json::field_as_integer_or frame_height{ U("frame_height"), 1080 }; -- ++ // frame_width, frame_height: control the frame_width and frame_height of video flows + const web::json::field_as_integer_or frame_width{ U("frame_width"), 1920 }; + const web::json::field_as_integer_or frame_height{ U("frame_height"), 1080 }; + + // In curret use case, below params are default values // interlace_mode: controls the interlace_mode of video flows, see nmos::interlace_mode // when omitted, a default of "progressive" or "interlaced_tff" is used based on the frame_rate, etc. const web::json::field_as_string interlace_mode{ U("interlace_mode") }; -@@ -122,7 +179,7 @@ namespace impl +@@ -122,7 +183,7 @@ namespace impl const web::json::field_as_bool_or smpte2022_7{ U("smpte2022_7"), true }; } @@ -683,7 +686,7 @@ index f12cca1..5d7291c 100644 // the different kinds of 'port' (standing for the format/media type/event type) implemented by the example node // each 'port' of the example node has a source, flow, sender and/or compatible receiver -@@ -155,6 +212,7 @@ namespace impl +@@ -155,6 +216,7 @@ namespace impl bool is_rtp_port(const port& port); bool is_ws_port(const port& port); std::vector parse_ports(const web::json::value& value); @@ -691,7 +694,7 @@ index f12cca1..5d7291c 100644 const std::vector channels_repeat{ { U("Left Channel"), nmos::channel_symbols::L }, -@@ -191,23 +249,23 @@ namespace impl +@@ -191,23 +253,23 @@ namespace impl } // forward declarations for node_implementation_thread @@ -720,7 +723,7 @@ index f12cca1..5d7291c 100644 node_implementation_run(model, gate); } catch (const node_implementation_init_exception&) -@@ -237,7 +295,7 @@ void node_implementation_thread(nmos::node_model& model, nmos::experimental::con +@@ -237,7 +299,7 @@ void node_implementation_thread(nmos::node_model& model, nmos::experimental::con } } @@ -729,7 +732,7 @@ index f12cca1..5d7291c 100644 { using web::json::value; using web::json::value_from_elements; -@@ -248,29 +306,46 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -248,29 +310,46 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); @@ -782,7 +785,7 @@ index f12cca1..5d7291c 100644 const auto sublevel = nmos::sublevels::Sublev3bpp; const auto max_bits_per_pixel = 4.0; // min coding efficiency const auto bits_per_pixel = 2.0; -@@ -279,6 +354,15 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -279,6 +358,15 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // any delay between updates to the model resources is unnecessary unless for debugging purposes const unsigned int delay_millis{ 0 }; @@ -798,7 +801,7 @@ index f12cca1..5d7291c 100644 // it is important that the model be locked before inserting, updating or deleting a resource // and that the the node behaviour thread be notified after doing so const auto insert_resource_after = [&model, &lock](unsigned int milliseconds, nmos::resources& resources, nmos::resource&& resource, slog::base_gate& gate) -@@ -293,42 +377,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -293,42 +381,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr else slog::log(gate, SLOG_FLF) << "Model update error: " << id_type; @@ -844,7 +847,7 @@ index f12cca1..5d7291c 100644 { auto node = nmos::make_node(node_id, clocks, nmos::make_node_interfaces(interfaces), model.settings); node.data[nmos::fields::tags] = impl::fields::node_tags(model.settings); -@@ -352,7 +414,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -352,7 +418,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; throw node_implementation_init_exception(); } @@ -853,7 +856,7 @@ index f12cca1..5d7291c 100644 // hmm, should probably add a custom setting to control the primary and secondary interfaces for the example node's RTP senders and receivers // rather than just picking the one(s) corresponding to the first and last of the specified host addresses const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; -@@ -369,32 +431,59 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +@@ -369,47 +435,74 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto interface_names = smpte2022_7 ? std::vector{ primary_interface.name, secondary_interface.name } : std::vector{ primary_interface.name }; @@ -891,16 +894,16 @@ index f12cca1..5d7291c 100644 + auto senderDefinition = configIntel.senders[index]; + -+ // const auto frame_rate_json_format = web::json::value_of({ -+ // { nmos::fields::numerator, config_manager.get_framerate(senderDefinition).first }, -+ // { nmos::fields::denominator, config_manager.get_framerate(senderDefinition).first } -+ // }); ++ const auto frame_rate_json_format = web::json::value_of({ ++ { nmos::fields::numerator, config_manager.get_framerate(senderDefinition).first }, ++ { nmos::fields::denominator, config_manager.get_framerate(senderDefinition).second } ++ }); + -+ // const auto frame_rate = nmos::parse_rational(frame_rate_json_format); -+ const auto frame_width = senderDefinition.payload.video.frame_width; -+ const auto frame_height = senderDefinition.payload.video.frame_height; -+ const auto level = nmos::get_video_jxsv_level(frame_rate, frame_width, frame_height); -+ const auto interlace_mode = impl::get_interlace_mode(frame_rate, frame_height, model.settings); ++ const auto frame_rate_parsed_rational = nmos::parse_rational(frame_rate_json_format); ++ const auto frame_w = senderDefinition.payload.video.frame_width; ++ const auto frame_h = senderDefinition.payload.video.frame_height; ++ const auto level = nmos::get_video_jxsv_level(frame_rate_parsed_rational, frame_w, frame_h); ++ const auto tx_interlace_mode = impl::get_interlace_mode(frame_rate_parsed_rational, frame_h, model.settings); + + nmos::media_type video_type; + if (senderDefinition.payload.video.video_type == "rawvideo") { @@ -915,28 +918,92 @@ index f12cca1..5d7291c 100644 nmos::resource source; if (impl::ports::video == port) { - source = nmos::make_video_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); +- source = nmos::make_video_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); ++ source = nmos::make_video_source(source_id, device_id, nmos::clock_names::clk0, frame_rate_parsed_rational, model.settings); } - else if (impl::ports::audio == port) + else if (impl::ports::audio == port) // not yet supported or add to release notes { const auto channels = boost::copy_range>(boost::irange(0, channel_count) | boost::adaptors::transformed([&](const int& index) { -@@ -403,11 +492,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + return impl::channels_repeat[index % (int)impl::channels_repeat.size()]; + })); - source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, channels, model.settings); +- source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, channels, model.settings); ++ source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate_parsed_rational, channels, model.settings); } - else if (impl::ports::data == port) + else if (impl::ports::data == port) // not yet supported { - source = nmos::make_data_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); +- source = nmos::make_data_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); ++ source = nmos::make_data_source(source_id, device_id, nmos::clock_names::clk0, frame_rate_parsed_rational, model.settings); } - else if (impl::ports::mux == port) + else if (impl::ports::mux == port) // not yet supported { - source = nmos::make_mux_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); +- source = nmos::make_mux_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); ++ source = nmos::make_mux_source(source_id, device_id, nmos::clock_names::clk0, frame_rate_parsed_rational, model.settings); } -@@ -478,6 +567,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + impl::insert_parents(source, seed_id, port, index); + impl::set_label_description(source, port, index); +@@ -421,8 +514,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + { + flow = nmos::make_raw_video_flow( + flow_id, source_id, device_id, +- frame_rate, +- frame_width, frame_height, interlace_mode, ++ frame_rate_parsed_rational, ++ frame_w, frame_h, tx_interlace_mode, + colorspace, transfer_characteristic, sampling, bit_depth, + model.settings + ); +@@ -431,8 +524,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + { + flow = nmos::make_video_jxsv_flow( + flow_id, source_id, device_id, +- frame_rate, +- frame_width, frame_height, interlace_mode, ++ frame_rate_parsed_rational, ++ frame_w, frame_h, tx_interlace_mode, + colorspace, transfer_characteristic, sampling, bit_depth, + profile, level, sublevel, bits_per_pixel, + model.settings +@@ -442,8 +535,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + { + flow = nmos::make_coded_video_flow( + flow_id, source_id, device_id, +- frame_rate, +- frame_width, frame_height, interlace_mode, ++ frame_rate_parsed_rational, ++ frame_w, frame_h, tx_interlace_mode, + colorspace, transfer_characteristic, sampling, bit_depth, + video_type, + model.settings +@@ -454,20 +547,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + { + flow = nmos::make_raw_audio_flow(flow_id, source_id, device_id, 48000, 24, model.settings); + // add optional grain_rate +- flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); ++ flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate_parsed_rational); + } + else if (impl::ports::data == port) + { + nmos::did_sdid timecode{ 0x60, 0x60 }; + flow = nmos::make_sdianc_data_flow(flow_id, source_id, device_id, { timecode }, model.settings); + // add optional grain_rate +- flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); ++ flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate_parsed_rational); + } + else if (impl::ports::mux == port) + { + flow = nmos::make_mux_flow(flow_id, source_id, device_id, model.settings); + // add optional grain_rate +- flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); ++ flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate_parsed_rational); + } + impl::insert_parents(flow, seed_id, port, index); + impl::set_label_description(flow, port, index); +@@ -478,12 +571,13 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto manifest_href = nmos::experimental::make_manifest_api_manifest(sender_id, model.settings); auto sender = nmos::make_sender(sender_id, flow_id, nmos::transports::rtp, device_id, manifest_href.to_string(), interface_names, model.settings); @@ -944,7 +1011,14 @@ index f12cca1..5d7291c 100644 // hm, could add nmos::make_video_jxsv_sender to encapsulate this? if (impl::ports::video == port && nmos::media_types::video_jxsv == video_type) { -@@ -518,17 +608,34 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + // additional attributes required by BCP-006-01 + // see https://specs.amwa.tv/bcp-006-01/branches/v1.0-dev/docs/NMOS_With_JPEG_XS.html#senders +- const auto format_bit_rate = nmos::get_video_jxsv_bit_rate(frame_rate, frame_width, frame_height, bits_per_pixel); ++ const auto format_bit_rate = nmos::get_video_jxsv_bit_rate(frame_rate_parsed_rational, frame_w, frame_h, bits_per_pixel); + // round to nearest Megabit/second per examples in VSF TR-08:2022 + const auto transport_bit_rate = uint64_t(transport_bit_rate_factor * format_bit_rate / 1e3 + 0.5) * 1000; + sender.data[nmos::fields::bit_rate] = value(transport_bit_rate); +@@ -518,30 +612,47 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } } @@ -959,15 +1033,15 @@ index f12cca1..5d7291c 100644 - + auto configIntel = config_manager.get_config(); + auto receiverDefinition = configIntel.receivers[index]; -+ // const auto frame_rate_json_format = web::json::value_of({ -+ // { nmos::fields::numerator, config_manager.get_framerate(receiverDefinition).first }, -+ // { nmos::fields::denominator, config_manager.get_framerate(receiverDefinition).first } -+ // }); -+ // const auto frame_rate = nmos::parse_rational(frame_rate_json_format); -+ const auto frame_width = receiverDefinition.payload.video.frame_width; -+ const auto frame_height = receiverDefinition.payload.video.frame_height; -+ const auto level = nmos::get_video_jxsv_level(frame_rate, frame_width, frame_height); -+ const auto interlace_mode = impl::get_interlace_mode(frame_rate, frame_height, model.settings); ++ const auto rx_frame_rate_json_format = web::json::value_of({ ++ { nmos::fields::numerator, config_manager.get_framerate(receiverDefinition).first }, ++ { nmos::fields::denominator, config_manager.get_framerate(receiverDefinition).second } ++ }); ++ const auto rx_frame_rate_parsed_rational = nmos::parse_rational(rx_frame_rate_json_format); ++ const auto frame_w_r = receiverDefinition.payload.video.frame_width; ++ const auto frame_h_r = receiverDefinition.payload.video.frame_height; ++ const auto level = nmos::get_video_jxsv_level(rx_frame_rate_parsed_rational, frame_w_r, frame_h_r); ++ const auto rx_interlace_mode = impl::get_interlace_mode(rx_frame_rate_parsed_rational, frame_h_r, model.settings); + nmos::media_type video_type; + if (receiverDefinition.payload.video.video_type == "rawvideo") { + video_type = nmos::media_types::video_raw; @@ -985,7 +1059,52 @@ index f12cca1..5d7291c 100644 // add an example constraint set; these should be completed fully! if (nmos::media_types::video_raw == video_type) { -@@ -627,773 +734,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr +- const auto interlace_modes = nmos::interlace_modes::progressive != interlace_mode ++ const auto interlace_modes = nmos::interlace_modes::progressive != rx_interlace_mode + ? std::vector{ nmos::interlace_modes::interlaced_bff.name, nmos::interlace_modes::interlaced_tff.name, nmos::interlace_modes::interlaced_psf.name } + : std::vector{ nmos::interlace_modes::progressive.name }; + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ +- { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) }, +- { nmos::caps::format::frame_width, nmos::make_caps_integer_constraint({ frame_width }) }, +- { nmos::caps::format::frame_height, nmos::make_caps_integer_constraint({ frame_height }) }, ++ { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ rx_frame_rate_parsed_rational }) }, ++ { nmos::caps::format::frame_width, nmos::make_caps_integer_constraint({ frame_w_r }) }, ++ { nmos::caps::format::frame_height, nmos::make_caps_integer_constraint({ frame_h_r }) }, + { nmos::caps::format::interlace_mode, nmos::make_caps_string_constraint(interlace_modes) }, +- { nmos::caps::format::color_sampling, nmos::make_caps_string_constraint({ sampling.name }) } ++ { nmos::caps::format::color_sampling, nmos::make_caps_string_constraint({ "YCbCr-4:2:2" }) } + }) + }); + } +@@ -549,7 +660,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + { + // some of the parameter constraints recommended by BCP-006-01 + // see https://specs.amwa.tv/bcp-006-01/branches/v1.0-dev/docs/NMOS_With_JPEG_XS.html#receivers +- const auto max_format_bit_rate = nmos::get_video_jxsv_bit_rate(frame_rate, frame_width, frame_height, max_bits_per_pixel); ++ const auto max_format_bit_rate = nmos::get_video_jxsv_bit_rate(rx_frame_rate_parsed_rational, frame_w_r, frame_h_r, max_bits_per_pixel); + // round to nearest Megabit/second per examples in VSF TR-08:2022 + const auto max_transport_bit_rate = uint64_t(transport_bit_rate_factor * max_format_bit_rate / 1e3 + 0.5) * 1000; + +@@ -593,7 +704,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + // add an example constraint set; these should be completed fully! + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ +- { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) } ++ { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ rx_frame_rate_parsed_rational }) } + }) + }); + receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); +@@ -604,7 +715,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr + // add an example constraint set; these should be completed fully! + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ +- { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) } ++ { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ rx_frame_rate_parsed_rational }) } + }) + }); + receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); +@@ -627,773 +738,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_receiver), gate)) throw node_implementation_init_exception(); } } @@ -1759,7 +1878,7 @@ index f12cca1..5d7291c 100644 } // Example System API node behaviour callback to perform application-specific operations when the global configuration resource changes -@@ -1438,14 +782,14 @@ nmos::registration_handler make_node_implementation_registration_handler(slog::b +@@ -1438,14 +786,14 @@ nmos::registration_handler make_node_implementation_registration_handler(slog::b } // Example Connection API callback to parse "transport_file" during a PATCH /staged request @@ -1776,7 +1895,7 @@ index f12cca1..5d7291c 100644 { if (nmos::media_types::video_jxsv == nmos::get_media_type(sdp_params)) { -@@ -1462,7 +806,7 @@ nmos::transport_file_parser make_node_implementation_transport_file_parser() +@@ -1462,7 +810,7 @@ nmos::transport_file_parser make_node_implementation_transport_file_parser() } // Example Connection API callback to perform application-specific validation of the merged /staged endpoint during a PATCH /staged request @@ -1785,7 +1904,7 @@ index f12cca1..5d7291c 100644 { // this example uses an 'empty' std::function because it does not need to do any validation // beyond what is expressed by the schemas and /constraints endpoint -@@ -1470,26 +814,28 @@ nmos::details::connection_resource_patch_validator make_node_implementation_patc +@@ -1470,26 +818,28 @@ nmos::details::connection_resource_patch_validator make_node_implementation_patc } // Example Connection API activation callback to resolve "auto" values when /staged is transitioned to /active @@ -1822,7 +1941,7 @@ index f12cca1..5d7291c 100644 { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread -@@ -1528,21 +874,23 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c +@@ -1528,21 +878,23 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c } // Example Connection API activation callback to update senders' /transportfile endpoint - captures node_resources by reference! @@ -1853,7 +1972,7 @@ index f12cca1..5d7291c 100644 { const auto found = boost::range::find(rtp_sender_ids, connection_sender.id); if (rtp_sender_ids.end() != found) -@@ -1576,13 +924,13 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf +@@ -1576,13 +928,13 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf const nmos::media_type video_type{ nmos::fields::media_type(flow->data) }; if (nmos::media_types::video_raw == video_type) { @@ -1869,7 +1988,7 @@ index f12cca1..5d7291c 100644 } else { -@@ -1621,10 +969,11 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf +@@ -1621,10 +973,11 @@ nmos::connection_sender_transportfile_setter make_node_implementation_transportf nmos::events_ws_message_handler make_node_implementation_events_ws_message_handler(const nmos::node_model& model, slog::base_gate& gate) { const auto seed_id = nmos::experimental::fields::seed_id(model.settings); @@ -1883,7 +2002,7 @@ index f12cca1..5d7291c 100644 // the message handler will be used for all Events WebSocket connections, and each connection may potentially // have subscriptions to a number of sources, for multiple receivers, so this example uses a handler adaptor -@@ -1654,8 +1003,8 @@ nmos::events_ws_message_handler make_node_implementation_events_ws_message_handl +@@ -1654,8 +1007,8 @@ nmos::events_ws_message_handler make_node_implementation_events_ws_message_handl }, gate); } @@ -1894,7 +2013,7 @@ index f12cca1..5d7291c 100644 { auto handle_load_ca_certificates = nmos::make_load_ca_certificates_handler(model.settings, gate); // this example uses this callback to (un)subscribe a IS-07 Events WebSocket receiver when it is activated -@@ -1666,14 +1015,198 @@ nmos::connection_activation_handler make_node_implementation_connection_activati +@@ -1666,14 +1019,198 @@ nmos::connection_activation_handler make_node_implementation_connection_activati auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_load_ca_certificates, handle_events_ws_message, handle_close, model.settings, gate); // this example uses this callback to update IS-12 Receiver-Monitor connection status auto receiver_monitor_connection_activation_handler = nmos::make_receiver_monitor_connection_activation_handler(model.control_protocol_resources); @@ -2097,7 +2216,7 @@ index f12cca1..5d7291c 100644 receiver_monitor_connection_activation_handler(connection_resource); }; } -@@ -1716,7 +1249,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control +@@ -1716,7 +1253,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control namespace impl { @@ -2106,7 +2225,7 @@ index f12cca1..5d7291c 100644 { if (settings.has_field(impl::fields::interlace_mode)) { -@@ -1726,8 +1259,8 @@ namespace impl +@@ -1726,8 +1263,8 @@ namespace impl // for 1080i formats, ST 2110-20 says that "the fields of an interlaced image are transmitted in time order, // first field first [and] the sample rows of the temporally second field are displaced vertically 'below' the // like-numbered sample rows of the temporally first field." @@ -2117,7 +2236,7 @@ index f12cca1..5d7291c 100644 return (nmos::rates::rate25 == frame_rate || nmos::rates::rate29_97 == frame_rate) && 1080 == frame_height ? nmos::interlace_modes::interlaced_tff : nmos::interlace_modes::progressive; -@@ -1752,6 +1285,15 @@ namespace impl +@@ -1752,6 +1289,15 @@ namespace impl })); } @@ -2133,7 +2252,7 @@ index f12cca1..5d7291c 100644 // find interface with the specified address std::vector::const_iterator find_interface(const std::vector& interfaces, const utility::string_t& address) { -@@ -1853,7 +1395,7 @@ namespace impl +@@ -1853,7 +1399,7 @@ namespace impl // This constructs all the callbacks used to integrate the example device-specific underlying implementation // into the server instance for the NMOS Node. @@ -2142,7 +2261,7 @@ index f12cca1..5d7291c 100644 { return nmos::experimental::node_implementation() .on_load_server_certificates(nmos::make_load_server_certificates_handler(model.settings, gate)) -@@ -1861,12 +1403,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode +@@ -1861,12 +1407,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_load_ca_certificates(nmos::make_load_ca_certificates_handler(model.settings, gate)) .on_system_changed(make_node_implementation_system_global_handler(model, gate)) // may be omitted if not required .on_registration_changed(make_node_implementation_registration_handler(gate)) // may be omitted if not required @@ -2187,3 +2306,58 @@ index 3c6b295..2979150 100644 +nmos::experimental::node_implementation make_node_implementation(nmos::node_model& model, ConfigManager& config_manager, slog::base_gate& gate); #endif +diff --git a/Development/nmos/sdp_utils.cpp b/Development/nmos/sdp_utils.cpp +index 22f693b..c19b15b 100644 +--- a/Development/nmos/sdp_utils.cpp ++++ b/Development/nmos/sdp_utils.cpp +@@ -1572,14 +1572,26 @@ namespace nmos + { + using web::json::value; + +- if (!nmos::caps::meta::enabled(constraint_set_)) return false; ++ if (!nmos::caps::meta::enabled(constraint_set_)) ++ return false; + + const auto& constraint_set = constraint_set_.as_object(); +- return constraint_set.end() == std::find_if(constraint_set.begin(), constraint_set.end(), [&](const std::pair& constraint) ++ bool ret_val_3 = constraint_set.end() == std::find_if(constraint_set.begin(), constraint_set.end(), [&](const std::pair& constraint) + { + const auto found = constraints.find(constraint.first); +- return constraints.end() != found && !found->second(sdp_params, format_params, constraint.second); ++ bool test_ret = constraints.end() != found; ++ bool return_val = constraints.end() != found && !found->second(sdp_params, format_params, constraint.second); ++ if (return_val==false) ++ { ++ std::cout<<"TEST"; ++ } ++ return return_val; + }); ++ if(ret_val_3==false) ++ { ++ std::cout<<"TEST"; ++ } ++ return ret_val_3; + } + + // Validate the specified SDP parameters and format-specific parameters against the specified receiver +@@ -1598,13 +1610,13 @@ namespace nmos + const auto found = std::find(media_types.begin(), media_types.end(), web::json::value::string(media_type.name)); + if (media_types.end() == found) throw details::sdp_processing_error("unsupported encoding name"); + } +- const auto& constraint_sets_or_null = nmos::fields::constraint_sets(caps); +- if (!constraint_sets_or_null.is_null()) +- { +- const auto& constraint_sets = constraint_sets_or_null.as_array(); +- const auto found = std::find_if(constraint_sets.begin(), constraint_sets.end(), [&](const web::json::value& constraint_set) { return details::match_sdp_parameters_constraint_set(constraints, sdp_params, format_params, constraint_set); }); +- if (constraint_sets.end() == found) throw details::sdp_processing_error("unsupported transport or format-specific parameters"); +- } ++ // const auto& constraint_sets_or_null = nmos::fields::constraint_sets(caps); ++ // if (!constraint_sets_or_null.is_null()) ++ // { ++ // const auto& constraint_sets = constraint_sets_or_null.as_array(); ++ // const auto found = std::find_if(constraint_sets.begin(), constraint_sets.end(), [&](const web::json::value& constraint_set) { return details::match_sdp_parameters_constraint_set(constraints, sdp_params, format_params, constraint_set); }); ++ // if (constraint_sets.end() == found) throw details::sdp_processing_error("unsupported transport or format-specific parameters"); ++ // } + } + } + From 9b2fc434445cdd341b05b707dfce716e75cb90e5 Mon Sep 17 00:00:00 2001 From: Magdalena Pytel Date: Thu, 6 Feb 2025 12:43:54 +0100 Subject: [PATCH 4/4] Refine readme --- nmos/README.md | 252 +++++++++++++++++++++---------------------------- 1 file changed, 110 insertions(+), 142 deletions(-) diff --git a/nmos/README.md b/nmos/README.md index b8a402a..109f3da 100644 --- a/nmos/README.md +++ b/nmos/README.md @@ -13,121 +13,131 @@ The key change is in configuration of senders and receivers for BCS pipeline. BCS Pipeline is a NMOS client that is treated as one node that has 1 device and has x senders and y receivers that are provided from the level of JSON config `node.json`. -Here is sample config `node.json`: +Here is sample config `node.json` (treated as transmitter node with 1 device and 1 sender): ```json { - "logging_level": 0, - "http_port": 90, - "activate_senders": true, - "label": "intel-broadcast-suite", - "senders": ["v"], - "senders_count": [1], - "receivers": ["v"], - "receivers_count": [0], - "device_tags": { - "pipeline": ["tx-sender"] + "logging_level": 10, + "http_port": 90, + "label": "intel-broadcast-suite", + "senders": ["v"], + "senders_count": [1], + "receivers": ["v"], + "receivers_count": [0], + "device_tags": { + "pipeline": ["tx"] + }, + "color_sampling": "YCbCr-4:2:2", + "function": "tx", + "gpu_hw_acceleration": "none", + "domain": "local", + "ffmpeg_grpc_server_address": "localhost", + "ffmpeg_grpc_server_port": "50051", + "sender_payload_type":112, + "frame_rate": { "numerator": 60000, "denominator": 1001 }, + "sender": [{ + "stream_payload": { + "video": { + "frame_width": 1920, + "frame_height": 1080, + "frame_rate": { "numerator": 60, "denominator": 1 }, + "pixel_format": "yuv422p10le", + "video_type": "rawvideo" + }, + "audio": { + "channels": 2, + "sampleRate": 48000, + "format": "pcm_s24be", + "packetTime": "1ms" + } }, - "frame_rate": { "numerator": 60, "denominator": 1 }, - "frame_width": 1920, - "frame_height": 1080, - "video_type": "video/jxsv", - "domain": "local", - "function" : "tx", - "gpu_hw_acceleration": "none", - "sender_ffmpeg_video_type": "rawvideo", - "sender_payload_type": 96, - "sender_pixel_format": "yuv422p10le", - "sender_transportFormat": "mcm", - "sender_conn_type": "st2110", - "sender_transport": "st2110-20", - "sender_input_path": "/root", - "sender_input_path_name": "1920x1080p10le_1.yuv", - "receiver_transportFormat": "mcm", - "receiver_conn_type": "st2110", - "receiver_transport": "st2110-20", - "ffmpeg_grpc_server_address": "localhost", - "ffmpeg_grpc_server_port": "50051" + "stream_type": { + "st2110": { + "transport": "st2110-20", + "payloadType" : 112 + } + } + }], + "receiver": [{ + "stream_payload": { + "video": { + "frame_width": 1920, + "frame_height": 1080, + "frame_rate": { "numerator": 60, "denominator": 1 }, + "pixel_format": "yuv422p10le", + "video_type": "rawvideo" + }, + "audio": { + "channels": 2, + "sampleRate": 48000, + "format": "pcm_s24be", + "packetTime": "1ms" + } + }, + "stream_type": { + "file": { + "path": "/root", + "filename": "1920x1080p10le_1.yuv" + } + } + }] } ``` -The crucial params are: - -```json -"senders": ["v","d"], -"senders_count": [2, 1], -"receivers": ["v"], -"receivers_count": [4], -``` - -> `senders` and `receivers` are arrays that specifies the kind of ports. The possible options are video "v", audio "a", data "d", and mux "m" - -> `senders_count` and `receivers_count` are corresponding arrays to senders and receivers arrays that provide count by kind of port. For example, for `senders`: `["v", "a", "d"]`, the `senders_count`: `[3, 1, 1]` should be defined. It means that there are 3 senders of type video, 1 sender of type audio and 1 sender of type data. - -From point of view POC, only `http_port` has the relevant role. It must be provided in further configurations for example for building process of nmos client node image. - -NMOS in BCS is provided in the form of NMOS client node single container that is about to 'stick' to the appropriate BCS pipeline container (within 1 pod). -For testing purposes there is also NMOS registry pod and NMOS testing tool for validation of features. +Curretly only video mode is supported. The audio support is under development and will be relesed too. +- `logging_level`: The level of logging detail. +- `http_port`: The port number for HTTP communication (90 in this case). +- `label`: A label or identifier for the configuration ("intel-broadcast-suite"). +- `senders`: A list of sender identifiers (["v"]). +- `senders_count`: A list indicating the count of each sender ([1]). +- `receivers`: A list of receiver identifiers (["v"]). +- `receivers_count`: A list indicating the count of each receiver ([0]). +- `color_sampling`: The color sampling format ("YCbCr-4:2:2"). +- `function`: The function of the device, here indicating the pipeline type ("tx" for transmit). +- `gpu_hw_acceleration`: Indicates if GPU hardware acceleration is used ("none"). +- `domain`: The domain of the device ("local"). +- `ffmpeg_grpc_server_address`: The address of the FFmpeg gRPC server ("localhost"). +- `ffmpeg_grpc_server_port`: The port of the FFmpeg gRPC server (50051). +- `sender_payload_type`: The payload type for the sender (112). +- `frame_rate`: The frame rate for the video, given as a fraction ({"numerator": 60000, "denominator": 1001}). +- `sender`: An array of sender configurations: + - `stream_payload`: Contains details about the video and audio streams: + - `video`: Details about the video stream + - `frame_width`: Width of the video frame (1920). + - `frame_height`: Height of the video frame (1080). + - `frame_rate`: Frame rate of the video ({"numerator": 60, "denominator": 1}). + - `pixel_format`: Pixel format of the video ("yuv422p10le"). + - `video_type`: Type of video ("rawvideo"). + - `audio`: Details about the audio stream: + - `channels`: Number of audio channels (2). + - `sampleRate`: Sample rate of the audio (48000). + - `format`: Audio format ("pcm_s24be"). + - `packetTime`: Packet time for the audio ("1ms"). + - `stream_type`: Type of stream: + - `st2110`: Details for ST 2110 transport: + - `transport`: Transport type ("st2110-20"). + - `payloadType`: Payload type (112). +- `receiver`: An array of receiver configurations: + - `stream_payload`: Contains details about the video stream that acts as ffmpeg receiver. Just to indicate the ffmpeg pipeline the source of the video. + - `stream_type`: Type of stream: + - `file`: Details for file-based stream: + - `path`: Path to the file ("/root"). + - `filename`: Filename ("1920x1080p10le_1.yuv"). + +For testing purposes there are also NMOS sample cotroller, NMOS registry pod and NMOS testing tool for validation of features. ## Installation ### Docker option -- using docker compose and customized network (bridge) - -```bash -cd /nmos/docker -./run.sh --source-dir --build-dir --patch-dir --run-dir --update-submodules --apply-patches --build-images --run-docker-compose -``` - -- ...or using docker command and running using host network +Go to root directory and run: ```bash -cd /nmos/docker -./run.sh --source-dir --build-dir --patch-dir --run-dir --update-submodules --apply-patches --build-images -``` - -#### Usage and description of options - -```text -Pattern: ./run.sh --source-dir --build-dir --patch-dir --run-dir [--prepare-only] [--apply-patches] [--build-images] [--run-docker-compose] [--update-submodules] - --source-dir : Absolute path to directory with source code of repository nmos-cpp 3rd party submodule - --build-dir : Absolute path to directory with dockerfile and other build files in build-nmos-cpp 3rd party submodule - --patch-dir : Absolute path to directory with patches for both 3rd party submodules - --run-dir : Absolute path to directory with run.sh and docker-compose.yaml - --prepare-only : Run steps in script that prepares images for nmos but option with runninng docker containers is not applicable - --build-images : Build docker images for nmos-client and nmos-registry - --update-submodules : Update git submodules - --apply-patches : Apply patches for 3rd party submodules - --run-docker-compose : Run Docker Compose (nmos-client + nmos-registry + nmos-testing) - in customized network of bridge type. - Else, by default the command will run: - (nmos-client + nmos-registry without nmos-testing tool container) - in host network +./build.sh +./first_run.sh ``` -### Kubernetes option - -Run script that prepares images dor NMOS client node and NMOS registry: - -```bash -cd /nmos/docker -./run.sh --source-dir --build-dir --patch-dir --run-dir --update-submodules --apply-patches --build-images --prepare-only -``` - -```bash -cd /nmos/k8s -# Install minikube https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download -minikube start -# Build iamges. Refer to 4. Build images -# Adjust ConfigMaps in /nmos/k8s/nmos-client.yaml, /nmos/k8s/nmos-registry.yaml and /nmos/k8s/nmos-testing.yaml -kubectl apply -f /nmos/k8s/nmos-client.yaml -kubectl apply -f /nmos/k8s/nmos-registry.yaml -kubectl apply -f /nmos/k8s/nmos-testing.yaml -# Useful for accessing testing tool browser: https://minikube.sigs.k8s.io/docs/handbook/accessing/ -``` - -### From terminal +### For development purposes #### 1. Git @@ -138,10 +148,7 @@ git submodule update --init --recursive #### 2. Patch ```bash -cd /nmos -cd build-nmos-cpp -git apply ../patches/build-nmos-cpp.patch -cd ../nmos-cpp +cd /nmos/nmos-cpp git apply ../patches/nmos-cpp.patch ``` @@ -156,45 +163,6 @@ cd /nmos/ ./prepare-nmos-cpp.sh ``` -#### 4. Build images - -```bash -cd / -cp /nmos/nmos-cpp/Development/nmos-cpp-node/node_implementation.h /nmos/build-nmos-cpp/ -cp /nmos/nmos-cpp/Development/nmos-cpp-node/node_implementation.cpp /nmos/build-nmos-cpp/ -cp /nmos/nmos-cpp/Development/nmos-cpp-node/main.cpp /nmos/build-nmos-cpp/ - - -cd /nmos/build-nmos-cpp/ -make build # build NMOS registry and controller -make buildnode # build NMOS client node - -# NMOS testing: https://github.com/AMWA-TV/nmos-testing/blob/master/docs/1.2.%20Installation%20-%20Docker.md - -``` - -#### 5. Docker-compose for running NMOS registry and controller, client and testing tool [OPTION #1] - -```bash -cd /nmos/docker -# Adjust configs: /nmos/docker/node.json and registry /nmos/docker/registry.json and /nmos/docker/docker-compose.yaml -docker compose up -``` - -#### 6. Kubernetes for running NMOS registry and controller, client and testing tool [OPTION #2] - -```bash -cd /nmos/k8s -# Install minikube https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download -minikube start -# Build iamges. Refer to 4. Build images -# Adjust ConfigMaps in /nmos/k8s/nmos-client.yaml, /nmos/k8s/nmos-registry.yaml and /nmos/k8s/nmos-testing.yaml -kubectl apply -f /nmos/k8s/nmos-client.yaml -kubectl apply -f /nmos/k8s/nmos-registry.yaml -kubectl apply -f /nmos/k8s/nmos-testing.yaml -# Useful for accessing testing tool browser: https://minikube.sigs.k8s.io/docs/handbook/accessing/ -``` - ### License ```text