فهرست منبع

rtmp-services: Add SHOWROOM

Toasterapp 5 سال پیش
والد
کامیت
80547455ca

+ 2 - 0
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

+ 16 - 0
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"
+            }
         }
     ]
 }

+ 18 - 0
plugins/rtmp-services/rtmp-common.c

@@ -2,11 +2,13 @@
 #include <util/dstr.h>
 #include <obs-module.h>
 #include <jansson.h>
+#include <obs-config.h>
 
 #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;
 }
 

+ 2 - 0
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);
 }

+ 163 - 0
plugins/rtmp-services/showroom.c

@@ -0,0 +1,163 @@
+#include <util/curl/curl-helper.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jansson.h>
+#include <util/dstr.h>
+#include <util/darray.h>
+#include "util/base.h"
+#include <obs-module.h>
+#include <util/platform.h>
+#include "showroom.h"
+#include <util/threading.h>
+
+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;
+}

+ 11 - 0
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();