From 2c0d85f5cedb95cf88cf1290535ad3063adeaf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 17 Dec 2024 23:07:51 -0500 Subject: [PATCH] osc: implement support for always sending bundles in OSC protocol --- src/ossia/network/osc/detail/bundle.hpp | 77 +++++++++++++++++++ .../network/osc/detail/osc_common_policy.hpp | 8 ++ .../osc/detail/osc_protocol_common.hpp | 53 +++++++++++++ src/ossia/protocols/osc/osc_factory.cpp | 43 ++++++++--- src/ossia/protocols/osc/osc_factory.hpp | 6 ++ .../protocols/osc/osc_generic_protocol.hpp | 57 +++++++++++--- 6 files changed, 222 insertions(+), 22 deletions(-) diff --git a/src/ossia/network/osc/detail/bundle.hpp b/src/ossia/network/osc/detail/bundle.hpp index 2d4a4714c7f..25e5d0abde4 100644 --- a/src/ossia/network/osc/detail/bundle.hpp +++ b/src/ossia/network/osc/detail/bundle.hpp @@ -145,6 +145,83 @@ catch(...) return {}; } +template +std::optional make_bundle( + NetworkPolicy add_element_to_bundle, const ossia::net::full_parameter_data& param) +try +{ + bundle ret{ + ossia::buffer_pool::instance().acquire(max_osc_message_size), param.critical}; + { + oscpack::OutboundPacketStream str(ret.data.data(), max_osc_message_size); + str << oscpack::BeginBundleImmediate(); + auto val = param.value(); + add_element_to_bundle(str, val, param); + str << oscpack::EndBundle(); + ret.data.resize(str.Size()); + + // TODO useless condition for now. + // But if we know that we are going through ws we can increase the size + // beyond 65k. ret.critical |= str.Size() > max_osc_message_size; + } + return ret; +} +catch(const oscpack::OutOfBufferMemoryException&) +{ + ossia::logger().error( + "make_bundle_client: message too large (limit is {} bytes)", max_osc_message_size); + return {}; +} +catch(const std::runtime_error& e) +{ + ossia::logger().error("make_bundle_client: {}", e.what()); + return {}; +} +catch(...) +{ + ossia::logger().error("make_bundle_client: unknown error"); + return {}; +} + +template +std::optional make_bundle( + NetworkPolicy add_element_to_bundle, const ossia::net::parameter_base& param, + ossia::value& v) +try +{ + bundle ret{ + ossia::buffer_pool::instance().acquire(max_osc_message_size), + param.get_critical()}; + { + oscpack::OutboundPacketStream str(ret.data.data(), max_osc_message_size); + str << oscpack::BeginBundleImmediate(); + add_element_to_bundle(str, v, param); + str << oscpack::EndBundle(); + ret.data.resize(str.Size()); + + // TODO useless condition for now. + // But if we know that we are going through ws we can increase the size + // beyond 65k. ret.critical |= str.Size() > max_osc_message_size; + } + return ret; +} +catch(const oscpack::OutOfBufferMemoryException&) +{ + ossia::logger().error( + "make_bundle_client: message too large (limit is {} bytes)", max_osc_message_size); + return {}; +} +catch(const std::runtime_error& e) +{ + ossia::logger().error("make_bundle_client: {}", e.what()); + return {}; +} +catch(...) +{ + ossia::logger().error("make_bundle_client: unknown error"); + return {}; +} + template std::optional make_bundle( NetworkPolicy add_element_to_bundle, diff --git a/src/ossia/network/osc/detail/osc_common_policy.hpp b/src/ossia/network/osc/detail/osc_common_policy.hpp index 5fcf3ceb80e..a992ad5c4ed 100644 --- a/src/ossia/network/osc/detail/osc_common_policy.hpp +++ b/src/ossia/network/osc/detail/osc_common_policy.hpp @@ -9,6 +9,14 @@ namespace ossia::net { + +template +struct osc_always_bundled_policy : T +{ + using bundled = std::true_type; +}; +template +using osc_never_bundled_policy = T; // Handling for types compatible with all OSC version // This class is an implementation detail. It is used to send things that work diff --git a/src/ossia/network/osc/detail/osc_protocol_common.hpp b/src/ossia/network/osc/detail/osc_protocol_common.hpp index 8ad7fb6c599..1eefe9d0d77 100644 --- a/src/ossia/network/osc/detail/osc_protocol_common.hpp +++ b/src/ossia/network/osc/detail/osc_protocol_common.hpp @@ -14,6 +14,7 @@ namespace ossia::net template struct osc_protocol_common { + using osc_configuration = OscVersion; template static bool push(T& self, const ossia::net::parameter_base& addr, Value_T&& v) { @@ -123,6 +124,32 @@ struct osc_protocol_client : osc_protocol_common return osc_protocol_common::push_raw(self, addr); } + template + static bool push_bundle( + T& self, Writer writer, const ossia::net::parameter_base& addr, ossia::value v) + { + if(auto bundle = make_bundle(bundle_client_policy{}, addr, v)) + { + writer(bundle->data.data(), bundle->data.size()); + ossia::buffer_pool::instance().release(std::move(bundle->data)); + return true; + } + return false; + } + + template + static bool + push_bundle(T& self, Writer writer, const ossia::net::full_parameter_data& addr) + { + if(auto bundle = make_bundle(bundle_client_policy{}, addr)) + { + writer(bundle->data.data(), bundle->data.size()); + ossia::buffer_pool::instance().release(std::move(bundle->data)); + return true; + } + return false; + } + template static bool push_bundle(T& self, Writer writer, const Addresses& addresses) { @@ -168,6 +195,32 @@ struct osc_protocol_server : osc_protocol_common return osc_protocol_common::push_raw(self, addr); } + template + static bool + push_bundle(T& self, Writer writer, const ossia::net::full_parameter_data& addr) + { + if(auto bundle = make_bundle(bundle_server_policy{}, addr)) + { + writer(bundle->data.data(), bundle->data.size()); + ossia::buffer_pool::instance().release(std::move(bundle->data)); + return true; + } + return false; + } + + template + static bool push_bundle( + T& self, Writer writer, const ossia::net::parameter_base& addr, ossia::value v) + { + if(auto bundle = make_bundle(bundle_server_policy{}, addr, v)) + { + writer(bundle->data.data(), bundle->data.size()); + ossia::buffer_pool::instance().release(std::move(bundle->data)); + return true; + } + return false; + } + template static bool push_bundle(T& self, Writer writer, const Addresses& addresses) { diff --git a/src/ossia/protocols/osc/osc_factory.cpp b/src/ossia/protocols/osc/osc_factory.cpp index e8a809841d3..4a906817278 100644 --- a/src/ossia/protocols/osc/osc_factory.cpp +++ b/src/ossia/protocols/osc/osc_factory.cpp @@ -250,20 +250,43 @@ make_osc_protocol(network_context_ptr ctx, osc_protocol_configuration config) { using conf = osc_protocol_configuration; - switch(config.version) + switch(config.bundle_strategy) { - case conf::OSC1_0: - return make_osc_protocol_impl(std::move(ctx), std::move(config)); - case conf::OSC1_1: - return make_osc_protocol_impl(std::move(ctx), std::move(config)); - case conf::EXTENDED: - return make_osc_protocol_impl( - std::move(ctx), std::move(config)); - default: + case conf::NEVER_BUNDLE: + switch(config.version) + { + case conf::OSC1_0: + return make_osc_protocol_impl( + std::move(ctx), std::move(config)); + case conf::OSC1_1: + return make_osc_protocol_impl( + std::move(ctx), std::move(config)); + case conf::EXTENDED: + return make_osc_protocol_impl( + std::move(ctx), std::move(config)); + default: + break; + } + break; + case conf::ALWAYS_BUNDLE: { + switch(config.version) + { + case conf::OSC1_0: + return make_osc_protocol_impl>( + std::move(ctx), std::move(config)); + case conf::OSC1_1: + return make_osc_protocol_impl>( + std::move(ctx), std::move(config)); + case conf::EXTENDED: + return make_osc_protocol_impl>( + std::move(ctx), std::move(config)); + default: + break; + } break; + } } return {}; } - } diff --git a/src/ossia/protocols/osc/osc_factory.hpp b/src/ossia/protocols/osc/osc_factory.hpp index 9f050fcc1cd..d58db0bfc35 100644 --- a/src/ossia/protocols/osc/osc_factory.hpp +++ b/src/ossia/protocols/osc/osc_factory.hpp @@ -40,6 +40,12 @@ struct osc_protocol_configuration SLIP } framing{SLIP}; + enum + { + NEVER_BUNDLE, + ALWAYS_BUNDLE + } bundle_strategy{NEVER_BUNDLE}; + osc_transport_configuration transport; }; diff --git a/src/ossia/protocols/osc/osc_generic_protocol.hpp b/src/ossia/protocols/osc/osc_generic_protocol.hpp index 0567ca8bb02..a5c1f98e693 100644 --- a/src/ossia/protocols/osc/osc_generic_protocol.hpp +++ b/src/ossia/protocols/osc/osc_generic_protocol.hpp @@ -27,10 +27,11 @@ namespace ossia::net { template -class osc_generic_bidir_protocol : public can_learn +class osc_generic_bidir_protocol final : public can_learn { public: - // using socket_type = Socket; + using osc_configuration = typename OscMode::osc_configuration; + static constexpr bool bundled = requires { typename osc_configuration::bundled{}; }; using writer_type = socket_writer; osc_generic_bidir_protocol( @@ -156,6 +157,7 @@ class osc_generic_bidir_protocol : public can_learn { if constexpr(!std::is_same_v) { + // FIXME bundling ? return OscMode::echo_incoming_message(*this, id, addr, val); } else @@ -168,7 +170,10 @@ class osc_generic_bidir_protocol : public can_learn { if constexpr(!std::is_same_v) { - return OscMode::push(*this, addr, v); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, v); + else + return OscMode::push(*this, addr, v); } else { @@ -180,7 +185,10 @@ class osc_generic_bidir_protocol : public can_learn { if constexpr(!std::is_same_v) { - return OscMode::push(*this, addr, std::move(v)); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, std::move(v)); + else + return OscMode::push(*this, addr, std::move(v)); } else { @@ -192,7 +200,10 @@ class osc_generic_bidir_protocol : public can_learn { if constexpr(!std::is_same_v) { - return OscMode::push_raw(*this, addr); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr); + else + return OscMode::push_raw(*this, addr); } else { @@ -273,9 +284,11 @@ class osc_generic_bidir_protocol : public can_learn }; template -class osc_generic_server_protocol : public can_learn +class osc_generic_server_protocol final : public can_learn { public: + using osc_configuration = typename OscMode::osc_configuration; + static constexpr bool bundled = requires { typename osc_configuration::bundled{}; }; using socket_type = Socket; using writer_type = socket_writer; @@ -323,17 +336,26 @@ class osc_generic_server_protocol : public can_learn bool push(const ossia::net::parameter_base& addr, const ossia::value& v) override { - return OscMode::push(*this, addr, v); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, v); + else + return OscMode::push(*this, addr, v); } bool push(const ossia::net::parameter_base& addr, ossia::value&& v) override { - return OscMode::push(*this, addr, std::move(v)); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, std::move(v)); + else + return OscMode::push(*this, addr, std::move(v)); } bool push_raw(const ossia::net::full_parameter_data& addr) override { - return OscMode::push_raw(*this, addr); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr); + else + return OscMode::push_raw(*this, addr); } bool push_bundle(const std::vector& addresses) override @@ -380,6 +402,8 @@ template class osc_generic_client_protocol : public can_learn { public: + using osc_configuration = typename OscMode::osc_configuration; + static constexpr bool bundled = requires { typename osc_configuration::bundled{}; }; using socket_type = Socket; using writer_type = socket_writer; @@ -431,17 +455,26 @@ class osc_generic_client_protocol : public can_learn bool push(const ossia::net::parameter_base& addr, const ossia::value& v) override { - return OscMode::push(*this, addr, v); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, v); + else + return OscMode::push(*this, addr, v); } bool push(const ossia::net::parameter_base& addr, ossia::value&& v) override { - return OscMode::push(*this, addr, std::move(v)); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr, std::move(v)); + else + return OscMode::push(*this, addr, std::move(v)); } bool push_raw(const ossia::net::full_parameter_data& addr) override { - return OscMode::push_raw(*this, addr); + if constexpr(bundled) + return OscMode::push_bundle(*this, writer(), addr); + else + return OscMode::push_raw(*this, addr); } bool push_bundle(const std::vector& addresses) override