diff --git a/cpp/demos/ossrf-nmos-api/config/nmos_config.json b/cpp/demos/ossrf-nmos-api/config/nmos_config.json index cae96fd..c1aefdd 100644 --- a/cpp/demos/ossrf-nmos-api/config/nmos_config.json +++ b/cpp/demos/ossrf-nmos-api/config/nmos_config.json @@ -51,6 +51,20 @@ "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.1.79", + "interface_name": "wlp1s0" + } + }, + "capabilities": [ + "audio/raw" + ] } ], "senders": [ diff --git a/cpp/demos/ossrf-nmos-api/main.cpp b/cpp/demos/ossrf-nmos-api/main.cpp index 137c915..988a833 100644 --- a/cpp/demos/ossrf-nmos-api/main.cpp +++ b/cpp/demos/ossrf-nmos-api/main.cpp @@ -47,18 +47,22 @@ namespace BST_ASSIGN(nmos_client, nmos_client_t::create(node_id, node_configuration.dump())); BST_CHECK(nmos_client->add_device(device.dump())); - ossrf::gst::plugins::gst_sender_plugin_uptr gst_sender_uptr = nullptr; - ossrf::gst::plugins::gst_sender_plugin_uptr gst_sender_uptr_2 = nullptr; - ossrf::gst::plugins::gst_receiver_plugin_uptr gst_receiver_uptr = nullptr; + ossrf::gst::plugins::gst_sender_plugin_uptr gst_sender_uptr = nullptr; + ossrf::gst::plugins::gst_sender_plugin_uptr gst_sender_uptr_2 = nullptr; + ossrf::gst::plugins::gst_receiver_plugin_uptr gst_receiver_uptr = nullptr; + ossrf::gst::plugins::gst_receiver_plugin_uptr gst_receiver_uptr_2 = nullptr; std::string receiver_info_config; auto receivers_it = app_configuration.find("receivers"); if(receivers_it != app_configuration.end()) { + auto i = 1; for(auto it = receivers_it->begin(); it != receivers_it->end(); ++it) { - auto receiver_activation_callback = - [r = (*it).dump(), &gst_receiver_uptr](const std::optional& sdp, bool master_enable) { + if(i == 1) + { + auto receiver_activation_callback = [r = (*it).dump(), &gst_receiver_uptr]( + const std::optional& sdp, bool master_enable) { if(sdp.has_value()) { fmt::print("nmos_receiver_callback: {} {}\n", master_enable, sdp.value()); @@ -73,9 +77,30 @@ namespace } fmt::print("nmos_receiver_callback: {} no sdp\n", master_enable); }; - - BST_CHECK(nmos_client->add_receiver(device_id, (*it).dump(), receiver_activation_callback)); - receiver_info_config = (*it).dump(); + BST_CHECK(nmos_client->add_receiver(device_id, (*it).dump(), receiver_activation_callback)); + receiver_info_config = (*it).dump(); + } + else if(i == 2) + { + auto receiver_activation_callback = [r = (*it).dump(), &gst_receiver_uptr_2]( + const std::optional& sdp, bool master_enable) { + if(sdp.has_value()) + { + fmt::print("nmos_receiver_callback: {} {}\n", master_enable, sdp.value()); + auto plugin = ossrf::gst::plugins::create_gst_receiver_plugin(r, sdp.value()); + if(plugin.has_value()) + { + gst_receiver_uptr_2.reset(plugin.value().release()); + return; + } + fmt::print("failed creating receiver\n"); + return; + } + fmt::print("nmos_receiver_callback: {} no sdp\n", master_enable); + }; + BST_CHECK(nmos_client->add_receiver(device_id, (*it).dump(), receiver_activation_callback)); + } + i++; } } std::string sender_info_config; diff --git a/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/receiver_plugin.cpp b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/receiver_plugin.cpp index 42fb8e2..940493b 100644 --- a/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/receiver_plugin.cpp +++ b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/receiver_plugin.cpp @@ -19,6 +19,7 @@ #include "bisect/json.h" #include "bisect/sdp/reader.h" #include "st2110_20_receiver_plugin.h" +#include "st2110_30_receiver_plugin.h" #include using namespace bisect; @@ -50,6 +51,16 @@ namespace return info; } + audio_info_t translate_sdp_audio_settings(const audio_sender_info_t audio_settings) + { + audio_info_t info; + info.bits_per_sample = audio_settings.bits_per_sample; + info.number_of_channels = audio_settings.number_of_channels; + info.packet_time = audio_settings.packet_time; + info.sampling_rate = audio_settings.sampling_rate; + return info; + } + expected translate_json(const json& config, sdp_settings_t sdp_settings) { receiver_settings s; @@ -77,8 +88,8 @@ namespace } else if(c == "audio/raw" && std::holds_alternative(sdp_settings.format)) { - audio_info_t info; - s.format = info; + auto a = std::get(sdp_settings.format); + s.format = translate_sdp_audio_settings(a); } else { @@ -97,6 +108,11 @@ namespace { return create_gst_st2110_20_plugin(settings, format); } + + expected do_create_plugin(const audio_info_t& format, const receiver_settings& settings) + { + return create_gst_st2110_30_plugin(settings, format); + } } // namespace expected plugins::create_gst_receiver_plugin(const std::string& config, @@ -108,4 +124,4 @@ expected plugins::create_gst_receiver_plugin(const std BST_ASSIGN(s, translate_json(json::parse(config), sdp_settings)); return match(s.format, overload{[&](const auto& f) { return do_create_plugin(f, s); }}); -} \ No newline at end of file +} diff --git a/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.cpp b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.cpp new file mode 100644 index 0000000..3330877 --- /dev/null +++ b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2024 Advanced Media Workflow Association +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "st2110_30_receiver_plugin.h" +#include "bisect/expected/macros.h" +#include "bisect/pipeline.h" +#include + +using namespace bisect; +using namespace ossrf::gst::receiver; +using namespace ossrf::gst::plugins; + +namespace +{ + constexpr auto queue_max_size_time = 200000; + constexpr auto queue_max_size_buffers = 0; + constexpr auto queue_max_size_bytes = 0; +}; // namespace + +struct gst_st2110_30_receiver_impl : gst_receiver_plugin_t +{ + receiver_settings s_; + audio_info_t f_; + gst::pipeline pipeline_; + + gst_st2110_30_receiver_impl(receiver_settings settings, audio_info_t format) : s_(settings), f_(format) {} + + ~gst_st2110_30_receiver_impl() { stop(); } + + maybe_ok create_gstreamer_pipeline() + { + // Create pipeline and check if all elements are created successfully + BST_CHECK_ASSIGN(pipeline_, bisect::gst::pipeline::create(NULL)); + auto* pipeline = pipeline_.get(); + + // Add pipeline udp source + auto* source = gst_element_factory_make("udpsrc", NULL); + BST_ENFORCE(source != nullptr, "Failed creating GStreamer element udpsrc"); + BST_ENFORCE(gst_bin_add(GST_BIN(pipeline), source), "Failed adding udpsrc to the pipeline"); + + // Set udp source params + g_object_set(G_OBJECT(source), "address", s_.primary.source_ip_address.c_str(), NULL); + g_object_set(G_OBJECT(source), "auto-multicast", TRUE, NULL); + g_object_set(G_OBJECT(source), "port", s_.primary.source_port, NULL); + g_object_set(G_OBJECT(source), "multicast-iface", s_.primary.interface_name.c_str(), NULL); + + // Create and set caps for udp source + GstCaps* caps = gst_caps_from_string("application/x-rtp, clock-rate=48000, channels=2"); + g_object_set(G_OBJECT(source), "caps", caps, NULL); + + // Add pipeline rtpjitterbuffer + auto* jitter_buffer = gst_element_factory_make("rtpjitterbuffer", NULL); + BST_ENFORCE(jitter_buffer != nullptr, "Failed creating GStreamer element jitter_buffer"); + BST_ENFORCE(gst_bin_add(GST_BIN(pipeline), jitter_buffer), "Failed adding jitter_buffer to the pipeline"); + + // Add pipeline queue1 + auto* queue1 = gst_element_factory_make("queue", NULL); + BST_ENFORCE(queue1 != nullptr, "Failed creating GStreamer element queue"); + BST_ENFORCE(gst_bin_add(GST_BIN(pipeline), queue1), "Failed adding queue to the pipeline"); + g_object_set(G_OBJECT(queue1), "max-size-time", queue_max_size_time, "max-size-buffers", queue_max_size_buffers, + "max-size-bytes", queue_max_size_bytes, NULL); + + // Add pipeline rtp depay + auto* depay = gst_element_factory_make("rtpL24depay", NULL); + BST_ENFORCE(depay != nullptr, "Failed creating GStreamer element depay"); + BST_ENFORCE(gst_bin_add(GST_BIN(pipeline), depay), "Failed adding depay to the pipeline"); + + // Add pipeline video sink + auto* sink = gst_element_factory_make("pulsesink", NULL); + BST_ENFORCE(sink != nullptr, "Failed creating GStreamer element sink"); + BST_ENFORCE(gst_bin_add(GST_BIN(pipeline), sink), "Failed adding sink to the pipeline"); + + // Link all elements together + BST_ENFORCE(gst_element_link_many(source, jitter_buffer, queue1, depay, sink, NULL), + "Failed linking GStreamer video pipeline"); + + // Setup runner + pipeline_.run_loop(); + + return {}; + } + + void stop() noexcept override + { + pipeline_.stop(); + pipeline_ = {}; + } +}; + +// TODO this function will need to receive an SDP and use the information in it to build the GST pipeline +expected ossrf::gst::plugins::create_gst_st2110_30_plugin(receiver_settings settings, + audio_info_t format) noexcept +{ + auto i = std::make_unique(settings, format); + + BST_CHECK(i->create_gstreamer_pipeline()); + + return i; +} diff --git a/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.h b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.h new file mode 100644 index 0000000..62922b0 --- /dev/null +++ b/cpp/libs/ossrf_gstreamer_api/lib/src/receiver/st2110_30_receiver_plugin.h @@ -0,0 +1,24 @@ +// Copyright (C) 2024 Advanced Media Workflow Association +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "ossrf/gstreamer/api/receiver/receiver_plugin.h" +#include "ossrf/gstreamer/api/receiver/receiver_configuration.h" + +namespace ossrf::gst::plugins +{ + bisect::expected + create_gst_st2110_30_plugin(ossrf::gst::receiver::receiver_settings settings, + ossrf::gst::receiver::audio_info_t format) noexcept; +}