diff --git a/CMakeLists.txt b/CMakeLists.txt index 35e46e8a..cc2055e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ else() find_package(FFmpeg REQUIRED) set(PLATFORM_TARGET_FILES + sunshine/platform/linux/publish.cpp sunshine/platform/linux/vaapi.h sunshine/platform/linux/vaapi.cpp sunshine/platform/linux/misc.cpp @@ -190,8 +191,6 @@ set(SUNSHINE_TARGET_FILES sunshine/thread_safe.h sunshine/sync.h sunshine/round_robin.h - sunshine/publish.h - sunshine/publish.cpp ${PLATFORM_TARGET_FILES}) include_directories( diff --git a/sunshine/main.cpp b/sunshine/main.cpp index 3dc625ef..b8e17f17 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -21,7 +21,6 @@ #include "httpcommon.h" #include "main.h" #include "nvhttp.h" -#include "publish.h" #include "rtsp.h" #include "thread_pool.h" #include "video.h" @@ -216,7 +215,7 @@ int main(int argc, char *argv[]) { task_pool.start(1); - std::thread publishThread { publish::start }; + std::thread publishThread { platf::publish::start }; std::thread httpThread { nvhttp::start }; std::thread configThread { confighttp::start }; stream::rtpThread(); diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 2686c72c..54d59b44 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -245,6 +245,13 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state); int alloc_gamepad(input_t &input, int nr); void free_gamepad(input_t &input, int nr); +#define SERVICE_NAME "Sunshine" +#define SERVICE_TYPE "_nvstream._tcp" + +namespace publish { +void start(); +} + [[nodiscard]] std::unique_ptr init(); } // namespace platf diff --git a/sunshine/platform/linux/publish.cpp b/sunshine/platform/linux/publish.cpp new file mode 100644 index 00000000..89fe53ff --- /dev/null +++ b/sunshine/platform/linux/publish.cpp @@ -0,0 +1,156 @@ + +// adapted from https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html +#include + +#include +#include +#include +#include +#include +#include + +#include "sunshine/main.h" +#include "sunshine/nvhttp.h" +#include "sunshine/platform/common.h" +#include "sunshine/utility.h" + +using namespace std::literals; +namespace platf::publish { + +template +void free(T *p) { + avahi_free(p); +} + +template +using ptr_t = util::safe_ptr>; +using client_t = util::safe_ptr; +using poll_t = util::safe_ptr; + +AvahiEntryGroup *group = NULL; + +poll_t poll; + +ptr_t name; + +void create_services(AvahiClient *c); + +void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + group = g; + + switch(state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + BOOST_LOG(info) << "Avahi service " << name.get() << " successfully established."; + break; + case AVAHI_ENTRY_GROUP_COLLISION: + name.reset(avahi_alternative_service_name(name.get())); + + BOOST_LOG(info) << "Avahi service name collision, renaming service to " << name.get(); + + create_services(avahi_entry_group_get_client(g)); + break; + case AVAHI_ENTRY_GROUP_FAILURE: + BOOST_LOG(error) << "Avahi entry group failure: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))); + avahi_simple_poll_quit(poll.get()); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING:; + } +} + +void create_services(AvahiClient *c) { + int ret; + + auto fg = util::fail_guard([]() { + avahi_simple_poll_quit(poll.get()); + }); + + if(!group) { + if(!(group = avahi_entry_group_new(c, entry_group_callback, nullptr))) { + BOOST_LOG(error) << "avahi_entry_group_new() failed: "sv << avahi_strerror(avahi_client_errno(c)); + return; + } + } + if(avahi_entry_group_is_empty(group)) { + BOOST_LOG(info) << "Adding avahi service "sv << name.get(); + + ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name.get(), SERVICE_TYPE, nullptr, nullptr, nvhttp::PORT_HTTP, nullptr); + if(ret < 0) { + if(ret == AVAHI_ERR_COLLISION) { + // A service name collision with a local service happened. Let's pick a new name + name.reset(avahi_alternative_service_name(name.get())); + BOOST_LOG(info) << "Service name collision, renaming service to "sv << name.get(); + + avahi_entry_group_reset(group); + + create_services(c); + + fg.disable(); + return; + } + + BOOST_LOG(error) << "Failed to add "sv << SERVICE_TYPE << " service: "sv << avahi_strerror(ret); + return; + } + + ret = avahi_entry_group_commit(group); + if(ret < 0) { + BOOST_LOG(error) << "Failed to commit entry group: "sv << avahi_strerror(ret); + return; + } + } + + fg.disable(); +} + +void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { + switch(state) { + case AVAHI_CLIENT_S_RUNNING: + create_services(c); + break; + case AVAHI_CLIENT_FAILURE: + BOOST_LOG(error) << "Client failure: "sv << avahi_strerror(avahi_client_errno(c)); + avahi_simple_poll_quit(poll.get()); + break; + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + if(group) + avahi_entry_group_reset(group); + break; + case AVAHI_CLIENT_CONNECTING:; + } +} + +void start() { + auto shutdown_event = mail::man->event(mail::shutdown); + + int avhi_error; + + poll.reset(avahi_simple_poll_new()); + if(!poll) { + BOOST_LOG(error) << "Failed to create simple poll object."sv; + return; + } + + name.reset(avahi_strdup(SERVICE_NAME)); + + client_t client { + avahi_client_new(avahi_simple_poll_get(poll.get()), AvahiClientFlags(0), client_callback, NULL, &avhi_error) + }; + + if(!client) { + BOOST_LOG(error) << "Failed to create client: "sv << avahi_strerror(avhi_error); + return; + } + + std::thread poll_thread { avahi_simple_poll_loop, poll.get() }; + + auto fg = util::fail_guard([&]() { + avahi_simple_poll_quit(poll.get()); + poll_thread.join(); + }); + + // Wait for any event + shutdown_event->view(); +} +}; // namespace platf::publish \ No newline at end of file diff --git a/sunshine/publish.cpp b/sunshine/publish.cpp deleted file mode 100644 index 8fc57e86..00000000 --- a/sunshine/publish.cpp +++ /dev/null @@ -1,133 +0,0 @@ - -// adapted from https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html -#include - -#include -#include -#include -#include -#include -#include - -#include "main.h" -#include "nvhttp.h" -#include "publish.h" - -namespace publish { -AvahiEntryGroup *group = NULL; -AvahiSimplePoll *simple_poll = NULL; -char *name = NULL; -void create_services(AvahiClient *c); - -void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { - assert(g == group || group == NULL); - group = g; - switch(state) { - case AVAHI_ENTRY_GROUP_ESTABLISHED: - BOOST_LOG(info) << "Avahi service " << name << " successfully established."; - break; - case AVAHI_ENTRY_GROUP_COLLISION: - char *n; - n = avahi_alternative_service_name(name); - avahi_free(name); - name = n; - BOOST_LOG(info) << "Avahi service name collision, renaming service to " << name; - create_services(avahi_entry_group_get_client(g)); - break; - case AVAHI_ENTRY_GROUP_FAILURE: - BOOST_LOG(error) << "Avahi entry group failure: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))); - avahi_simple_poll_quit(simple_poll); - break; - case AVAHI_ENTRY_GROUP_UNCOMMITED: - case AVAHI_ENTRY_GROUP_REGISTERING:; - } -} - -void create_services(AvahiClient *c) { - char *n; - int ret; - assert(c); - if(!group) { - if(!(group = avahi_entry_group_new(c, entry_group_callback, NULL))) { - BOOST_LOG(error) << "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c)); - goto fail; - } - } - if(avahi_entry_group_is_empty(group)) { - BOOST_LOG(info) << "Adding avahi service " << name; - - if((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name, SERVICE_TYPE, NULL, NULL, nvhttp::PORT_HTTP, NULL)) < 0) { - if(ret == AVAHI_ERR_COLLISION) - goto collision; - BOOST_LOG(error) << "Failed to add " << SERVICE_TYPE << " service: " << avahi_strerror(ret); - goto fail; - } - if((ret = avahi_entry_group_commit(group)) < 0) { - BOOST_LOG(error) << "Failed to commit entry group: " << avahi_strerror(ret); - goto fail; - } - } - return; -collision: - // A service name collision with a local service happened. Let's pick a new name - n = avahi_alternative_service_name(name); - avahi_free(name); - name = n; - BOOST_LOG(info) << "Service name collision, renaming service to " << name; - avahi_entry_group_reset(group); - create_services(c); - return; -fail: - avahi_simple_poll_quit(simple_poll); -} - -void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { - assert(c); - switch(state) { - case AVAHI_CLIENT_S_RUNNING: - create_services(c); - break; - case AVAHI_CLIENT_FAILURE: - BOOST_LOG(error) << "Client failure: " << avahi_strerror(avahi_client_errno(c)); - avahi_simple_poll_quit(simple_poll); - break; - case AVAHI_CLIENT_S_COLLISION: - case AVAHI_CLIENT_S_REGISTERING: - if(group) - avahi_entry_group_reset(group); - break; - case AVAHI_CLIENT_CONNECTING:; - } -} - -void start() { - auto shutdown_event = mail::man->event(mail::shutdown); - - AvahiClient *client = NULL; - int avhi_error; - if(!(simple_poll = avahi_simple_poll_new())) { - BOOST_LOG(error) << "Failed to create simple poll object."; - return; - } - name = avahi_strdup(SERVICE_NAME); - client = avahi_client_new(avahi_simple_poll_get(simple_poll), AvahiClientFlags(0), client_callback, NULL, &avhi_error); - if(!client) { - BOOST_LOG(error) << "Failed to create client: " << avahi_strerror(avhi_error); - avahi_simple_poll_free(simple_poll); - return; - } - - std::thread poll_thread { avahi_simple_poll_loop, simple_poll }; - - // Wait for any event - shutdown_event->view(); - - avahi_simple_poll_quit(simple_poll); - - poll_thread.join(); - - avahi_client_free(client); - avahi_simple_poll_free(simple_poll); - avahi_free(name); -} -}; // namespace publish \ No newline at end of file diff --git a/sunshine/publish.h b/sunshine/publish.h deleted file mode 100644 index 27ee1cd1..00000000 --- a/sunshine/publish.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SUNSHINE_PUBLISH_H -#define SUNSHINE_PUBLISH_H - -#include "thread_safe.h" - -#define SERVICE_NAME "Sunshine" -#define SERVICE_TYPE "_nvstream._tcp" - -namespace publish { -void start(); -} - -#endif //SUNSHINE_PUBLISH_H