diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 07e7250766274ad9849cbbdea910c336c9039f57..a799f00ceae21a74865323efdd00a1a717023fb8 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -1,15 +1,21 @@ project(rtmp-services) +find_package(Libcurl REQUIRED) + +include_directories(${LIBCURL_INCLUDE_DIRS}) + include_directories(${OBS_JANSSON_INCLUDE_DIRS}) set(rtmp-services_SOURCES twitch.c + younow.c rtmp-common.c rtmp-custom.c rtmp-services-main.c) set(rtmp-services_HEADERS twitch.h + younow.h rtmp-format-ver.h) set(RTMP_SERVICES_URL @@ -28,10 +34,12 @@ add_library(rtmp-services MODULE ${rtmp-services_SOURCES} ${rtmp-services_HEADERS} ${rtmp-services_config_HEADERS}) + target_link_libraries(rtmp-services libobs file-updater - ${OBS_JANSSON_IMPORT}) + ${OBS_JANSSON_IMPORT} + ${LIBCURL_LIBRARIES}) target_include_directories(rtmp-services PUBLIC diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index ebe245ab868d30f15a8c530d463544a3ef96efe0..724e6e2ab62f1d4e4d5ef4b9368e88a6a62b401e 100755 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1514,6 +1514,24 @@ "x264opts": "tune=zerolatency" } }, + { + "name": "YouNow", + "common": false, + "servers": [ + { + "name": "younow.com", + "url": "https://signaling-api.younow-prod.video.propsproject.com/api/v1/ingest/server/" + } + ], + "recommended": { + "keyint": 2, + "output": "ftl_output", + "max audio bitrate": 160, + "max video bitrate": 7000, + "profile": "main", + "bframes": 0 + } + }, { "name": "Steam", "common": false, diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index bf0b178919b68d9d325accc85f554ddb3c91cf33..3363be5588479dfcbb6aa5dcc80abd766d44d776 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -5,6 +5,7 @@ #include "rtmp-format-ver.h" #include "twitch.h" +#include "younow.h" struct rtmp_common { char *service; @@ -594,6 +595,12 @@ static const char *rtmp_common_url(void *data) } } + if (service->service && strcmp(service->service, "YouNow") == 0) { + if (service->server && service->key) { + return younow_get_ingest(service->server, service->key); + } + } + return service->server; } diff --git a/plugins/rtmp-services/younow.c b/plugins/rtmp-services/younow.c new file mode 100644 index 0000000000000000000000000000000000000000..253660f07e161813a0c9762f6c1efd4422570ecf --- /dev/null +++ b/plugins/rtmp-services/younow.c @@ -0,0 +1,113 @@ +#include +#include +#include + +#include +#include "util/base.h" +#include "younow.h" + +struct younow_mem_struct { + char *memory; + size_t size; +}; + +static char *current_ingest = NULL; + +static size_t younow_write_cb(void *contents, size_t size, size_t nmemb, + void *userp) +{ + size_t realsize = size * nmemb; + struct younow_mem_struct *mem = (struct younow_mem_struct *)userp; + + mem->memory = realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory == NULL) { + blog(LOG_WARNING, "yyounow_write_cb: realloc returned NULL"); + return 0; + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +const char *younow_get_ingest(const char *server, const char *key) +{ + CURL *curl_handle; + CURLcode res; + struct younow_mem_struct chunk; + struct dstr uri; + long response_code; + + // find the delimiter in stream key + const char *delim = strchr(key, '_'); + if (delim == NULL) { + blog(LOG_WARNING, + "younow_get_ingest: delimiter not found in stream key"); + return server; + } + + curl_handle = curl_easy_init(); + + chunk.memory = malloc(1); /* will be grown as needed by realloc */ + chunk.size = 0; /* no data at this point */ + + dstr_init(&uri); + dstr_copy(&uri, server); + dstr_ncat(&uri, key, delim - 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, 3L); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, younow_write_cb); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + +#if LIBCURL_VERSION_NUM >= 0x072400 + // A lot of servers don't yet support ALPN + 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, + "younow_get_ingest: curl_easy_perform() failed: %s", + curl_easy_strerror(res)); + curl_easy_cleanup(curl_handle); + free(chunk.memory); + return server; + } + + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code != 200) { + blog(LOG_WARNING, + "younow_get_ingest: curl_easy_perform() returned code: %ld", + response_code); + curl_easy_cleanup(curl_handle); + free(chunk.memory); + return server; + } + + curl_easy_cleanup(curl_handle); + + if (chunk.size == 0) { + blog(LOG_WARNING, + "younow_get_ingest: curl_easy_perform() returned empty response"); + free(chunk.memory); + return server; + } + + if (current_ingest) { + free(current_ingest); + current_ingest = NULL; + } + + current_ingest = strdup(chunk.memory); + free(chunk.memory); + blog(LOG_INFO, "younow_get_ingest: returning ingest: %s", + current_ingest); + return current_ingest; +} diff --git a/plugins/rtmp-services/younow.h b/plugins/rtmp-services/younow.h new file mode 100644 index 0000000000000000000000000000000000000000..427ebfe30796f5912bf9b4f1bfbc12bdae86b82c --- /dev/null +++ b/plugins/rtmp-services/younow.h @@ -0,0 +1,3 @@ +#pragma once + +extern const char *younow_get_ingest(const char *server, const char *key); \ No newline at end of file