Skip to content

Commit

Permalink
Add ST2110-30 receiver to the gstreamer and demo
Browse files Browse the repository at this point in the history
  • Loading branch information
joaofigueiredobisect committed Sep 25, 2024
1 parent 6a069a4 commit b6e5738
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 17 deletions.
14 changes: 14 additions & 0 deletions cpp/demos/ossrf-nmos-api/config/nmos_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/L24"
]
}
],
"senders": [
Expand Down
41 changes: 33 additions & 8 deletions cpp/demos/ossrf-nmos-api/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& sdp, bool master_enable) {
if(i == 1)
{
auto receiver_activation_callback = [r = (*it).dump(), &gst_receiver_uptr](
const std::optional<std::string>& sdp, bool master_enable) {
if(sdp.has_value())
{
fmt::print("nmos_receiver_callback: {} {}\n", master_enable, sdp.value());
Expand All @@ -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<std::string>& 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;
Expand Down
24 changes: 20 additions & 4 deletions cpp/libs/ossrf_gstreamer_api/lib/src/receiver/receiver_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <nlohmann/json.hpp>

using namespace bisect;
Expand Down Expand Up @@ -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<receiver_settings> translate_json(const json& config, sdp_settings_t sdp_settings)
{
receiver_settings s;
Expand All @@ -75,10 +86,10 @@ namespace
auto v = std::get<video_sender_info_t>(sdp_settings.format);
s.format = translate_sdp_video_settings(v);
}
else if(c == "audio/raw" && std::holds_alternative<audio_sender_info_t>(sdp_settings.format))
else if(c == "audio/L24" && std::holds_alternative<audio_sender_info_t>(sdp_settings.format))
{
audio_info_t info;
s.format = info;
auto a = std::get<audio_sender_info_t>(sdp_settings.format);
s.format = translate_sdp_audio_settings(a);
}
else
{
Expand All @@ -97,6 +108,11 @@ namespace
{
return create_gst_st2110_20_plugin(settings, format);
}

expected<gst_receiver_plugin_uptr> do_create_plugin(const audio_info_t& format, const receiver_settings& settings)
{
return create_gst_st2110_30_plugin(settings, format);
}
} // namespace

expected<gst_receiver_plugin_uptr> plugins::create_gst_receiver_plugin(const std::string& config,
Expand All @@ -108,4 +124,4 @@ expected<gst_receiver_plugin_uptr> 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); }});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "bisect/expected/macros.h"
#include "bisect/pipeline.h"
#include <gst/gst.h>
#include "fmt/format.h"

using namespace bisect;
using namespace ossrf::gst::receiver;
Expand Down Expand Up @@ -56,9 +57,12 @@ struct gst_st2110_20_receiver_impl : gst_receiver_plugin_t
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, media=(string)video, clock-rate=(int)90000, encoding-name=(string)RAW, "
"sampling=(string)RGB, width=(string)640, height=(string)480");
constexpr auto t =
R"(application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)RAW, "
"sampling=(string){sampling}, width=(string){width}, height=(string){height})";
GstCaps* caps = gst_caps_from_string(fmt::format(t, fmt::arg("sampling", f_.chroma_sub_sampling),
fmt::arg("width", f_.width), fmt::arg("height", f_.height))
.c_str());
g_object_set(G_OBJECT(source), "caps", caps, NULL);

// Add pipeline rtpjitterbuffer
Expand Down Expand Up @@ -112,7 +116,6 @@ struct gst_st2110_20_receiver_impl : gst_receiver_plugin_t
}
};

// TODO this function will need to receive an SDP and use the information in it to build the GST pipeline
expected<gst_receiver_plugin_uptr> ossrf::gst::plugins::create_gst_st2110_20_plugin(receiver_settings settings,
video_info_t format) noexcept
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 <gst/gst.h>

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
constexpr auto t = R"(application/x-rtp, clock-rate={rate}, channels={channels})";
GstCaps* caps = gst_caps_from_string(
fmt::format(t, fmt::arg("rate", f_.sampling_rate), fmt::arg("channels", f_.number_of_channels)).c_str());
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<gst_receiver_plugin_uptr> ossrf::gst::plugins::create_gst_st2110_30_plugin(receiver_settings settings,
audio_info_t format) noexcept
{
auto i = std::make_unique<gst_st2110_30_receiver_impl>(settings, format);

BST_CHECK(i->create_gstreamer_pipeline());

return i;
}
Original file line number Diff line number Diff line change
@@ -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<gst_receiver_plugin_uptr>
create_gst_st2110_30_plugin(ossrf::gst::receiver::receiver_settings settings,
ossrf::gst::receiver::audio_info_t format) noexcept;
}
2 changes: 1 addition & 1 deletion cpp/libs/ossrf_nmos_api/lib/src/serialization/receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace
receiver.media_types = {nmos::media_types::video_raw};
receiver.format = nmos::formats::video;
}
else if(media_type == "audio/raw")
else if(media_type == "audio/L24")
{
receiver.media_types = {nmos::media_types::audio_L24};
receiver.format = nmos::formats::audio;
Expand Down

0 comments on commit b6e5738

Please sign in to comment.