From 80547455cab08fd8b08269177c0fb620a17e8d40 Mon Sep 17 00:00:00 2001 From: Toasterapp Date: Tue, 14 Apr 2020 17:19:24 +0900 Subject: [PATCH] rtmp-services: Add SHOWROOM --- plugins/rtmp-services/CMakeLists.txt | 2 + plugins/rtmp-services/data/services.json | 16 ++ plugins/rtmp-services/rtmp-common.c | 18 +++ plugins/rtmp-services/rtmp-services-main.c | 2 + plugins/rtmp-services/showroom.c | 163 +++++++++++++++++++++ plugins/rtmp-services/showroom.h | 11 ++ 6 files changed, 212 insertions(+) create mode 100644 plugins/rtmp-services/showroom.c create mode 100644 plugins/rtmp-services/showroom.h diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index ed8f13634..772d07387 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -10,6 +10,7 @@ set(rtmp-services_SOURCES twitch.c younow.c nimotv.c + showroom.c rtmp-common.c rtmp-custom.c rtmp-services-main.c) @@ -25,6 +26,7 @@ set(rtmp-services_HEADERS twitch.h younow.h nimotv.h + showroom.h rtmp-format-ver.h) set(RTMP_SERVICES_URL diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 009d9ee81..965c5d125 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1756,6 +1756,22 @@ "max video bitrate": 20000, "max audio bitrate": 192 } + }, + { + "name": "SHOWROOM", + "servers": [ + { + "name": "Default", + "url": "https://www.showroom-live.com/api/obs/streaming_info?obs_key=" + } + ], + "recommended": { + "keyint": 2, + "profile": "main", + "max video bitrate": 1500, + "max audio bitrate": 160, + "x264opts": "tune=zerolatency" + } } ] } diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index bf9109fb7..b8610c156 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -2,11 +2,13 @@ #include #include #include +#include #include "rtmp-format-ver.h" #include "twitch.h" #include "younow.h" #include "nimotv.h" +#include "showroom.h" struct rtmp_common { char *service; @@ -619,12 +621,28 @@ static const char *rtmp_common_url(void *data) } } + if (service->service && strcmp(service->service, "SHOWROOM") == 0) { + if (service->server && service->key) { + struct showroom_ingest *ingest; + ingest = showroom_get_ingest(service->server, + service->key); + return ingest->url; + } + } return service->server; } static const char *rtmp_common_key(void *data) { struct rtmp_common *service = data; + if (service->service && strcmp(service->service, "SHOWROOM") == 0) { + if (service->server && service->key) { + struct showroom_ingest *ingest; + ingest = showroom_get_ingest(service->server, + service->key); + return ingest->key; + } + } return service->key; } diff --git a/plugins/rtmp-services/rtmp-services-main.c b/plugins/rtmp-services/rtmp-services-main.c index b9afb59d1..3ccfb8883 100644 --- a/plugins/rtmp-services/rtmp-services-main.c +++ b/plugins/rtmp-services/rtmp-services-main.c @@ -7,6 +7,7 @@ #include "rtmp-format-ver.h" #include "lookup-config.h" +#include "showroom.h" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("rtmp-services", "en-US") @@ -107,5 +108,6 @@ void obs_module_unload(void) { update_info_destroy(update_info); unload_twitch_data(); + free_showroom_data(); dstr_free(&module_name); } diff --git a/plugins/rtmp-services/showroom.c b/plugins/rtmp-services/showroom.c new file mode 100644 index 000000000..dfbcaf187 --- /dev/null +++ b/plugins/rtmp-services/showroom.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include "util/base.h" +#include +#include +#include "showroom.h" +#include + +struct showroom_ingest_info { + char *access_key; + uint64_t last_time; + struct showroom_ingest ingest; +}; + +static DARRAY(struct showroom_ingest_info) cur_ingests = {0}; + +struct showroom_ingest invalid_ingest = {"", ""}; + +void free_showroom_data(void) +{ + for (size_t i = 0; i < cur_ingests.num; i++) { + struct showroom_ingest_info *info = &cur_ingests.array[i]; + bfree(info->access_key); + bfree((void *)info->ingest.key); + bfree((void *)info->ingest.url); + } + + da_free(cur_ingests); +} + +static size_t showroom_write_cb(void *data, size_t size, size_t nmemb, + void *user_pointer) +{ + struct dstr *json = user_pointer; + size_t realsize = size * nmemb; + dstr_ncat(json, data, realsize); + return realsize; +} + +static struct showroom_ingest_info *find_ingest(const char *access_key) +{ + struct showroom_ingest_info *ret = NULL; + for (size_t i = 0; i < cur_ingests.num; i++) { + struct showroom_ingest_info *info = &cur_ingests.array[i]; + if (strcmp(info->access_key, access_key) == 0) { + ret = info; + break; + } + } + + return ret; +} + +#ifndef SEC_TO_NSEC +#define SEC_TO_NSEC 1000000000ULL +#endif + +static struct showroom_ingest_info *get_ingest_from_json(char *str, + const char *access_key) +{ + json_error_t error; + json_t *root; + root = json_loads(str, JSON_REJECT_DUPLICATES, &error); + if (!root) { + return NULL; + } + + const char *url_str = + json_string_value(json_object_get(root, "streaming_url_rtmp")); + const char *key_str = + json_string_value(json_object_get(root, "streaming_key")); + + struct showroom_ingest_info *info = find_ingest(access_key); + if (!info) { + info = da_push_back_new(cur_ingests); + info->access_key = bstrdup(access_key); + } + + bfree((void *)info->ingest.url); + bfree((void *)info->ingest.key); + info->ingest.url = bstrdup(url_str); + info->ingest.key = bstrdup(key_str); + info->last_time = os_gettime_ns() / SEC_TO_NSEC; + + json_decref(root); + return info; +} + +struct showroom_ingest *showroom_get_ingest(const char *server, + const char *access_key) +{ + struct showroom_ingest_info *info = find_ingest(access_key); + CURL *curl_handle; + CURLcode res; + struct dstr json = {0}; + struct dstr uri = {0}; + long response_code; + + if (info) { + /* this function is called a bunch of times for the same data, + * so in order to prevent multiple unnecessary queries in a + * short period of time, return the same data for 10 seconds */ + + uint64_t ts_sec = os_gettime_ns() / SEC_TO_NSEC; + if (ts_sec - info->last_time < 10) { + return &info->ingest; + } else { + info = NULL; + } + } + + curl_handle = curl_easy_init(); + + dstr_copy(&uri, server); + dstr_cat(&uri, access_key); + curl_easy_setopt(curl_handle, CURLOPT_URL, uri.array); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, true); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, showroom_write_cb); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&json); + curl_obs_set_revoke_setting(curl_handle); + +#if LIBCURL_VERSION_NUM >= 0x072400 + curl_easy_setopt(curl_handle, CURLOPT_SSL_ENABLE_ALPN, 0); +#endif + + res = curl_easy_perform(curl_handle); + dstr_free(&uri); + if (res != CURLE_OK) { + blog(LOG_WARNING, + "showroom_get_ingest: curl_easy_perform() failed: %s", + curl_easy_strerror(res)); + goto cleanup; + } + + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code != 200) { + blog(LOG_WARNING, + "showroom_get_ingest: curl_easy_perform() returned " + "code: %ld", + response_code); + goto cleanup; + } + + if (json.len == 0) { + blog(LOG_WARNING, + "showroom_get_ingest: curl_easy_perform() returned " + "empty response"); + goto cleanup; + } + + info = get_ingest_from_json(json.array, access_key); + +cleanup: + curl_easy_cleanup(curl_handle); + dstr_free(&json); + return info ? &info->ingest : &invalid_ingest; +} diff --git a/plugins/rtmp-services/showroom.h b/plugins/rtmp-services/showroom.h new file mode 100644 index 000000000..f10ad1900 --- /dev/null +++ b/plugins/rtmp-services/showroom.h @@ -0,0 +1,11 @@ +#pragma once + +struct showroom_ingest { + const char *url; + const char *key; +}; + +extern struct showroom_ingest *showroom_get_ingest(const char *server, + const char *access_key); + +extern void free_showroom_data(); -- GitLab