From f756c1e5c4ff27265cf137124ee0545992226b0a Mon Sep 17 00:00:00 2001 From: Clemens Meinhart Date: Mon, 23 May 2022 12:07:01 +0200 Subject: [PATCH] rtmp-services/UI: Add Bitmovin --- UI/data/locale/en-US.ini | 4 + UI/streaming-helpers.cpp | 59 ++++ UI/streaming-helpers.hpp | 7 +- UI/window-basic-auto-config.cpp | 2 +- UI/window-basic-settings-stream.cpp | 12 +- plugins/rtmp-services/CMakeLists.txt | 3 + plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 14 + plugins/rtmp-services/rtmp-common.c | 26 +- plugins/rtmp-services/rtmp-services-main.c | 3 + .../service-specific/bitmovin-constants.h | 10 + .../rtmp-services/service-specific/bitmovin.c | 334 ++++++++++++++++++ .../rtmp-services/service-specific/bitmovin.h | 12 + 13 files changed, 480 insertions(+), 10 deletions(-) create mode 100644 plugins/rtmp-services/service-specific/bitmovin-constants.h create mode 100644 plugins/rtmp-services/service-specific/bitmovin.c create mode 100644 plugins/rtmp-services/service-specific/bitmovin.h diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index f783e7cc603609..2605d320a146f3 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -190,6 +190,10 @@ Basic.AutoConfig.StreamPage.Server="Server" Basic.AutoConfig.StreamPage.StreamKey="Stream Key" Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" Basic.AutoConfig.StreamPage.EncoderKey="Encoder Key" +Basic.AutoConfig.StreamPage.RunningStream="Running Stream" +Basic.AutoConfig.StreamPage.GetApiKey="Get API Key" +Basic.AutoConfig.StreamPage.ApiKey="API Key" +Basic.AutoConfig.StreamPage.None="None" Basic.AutoConfig.StreamPage.ConnectedAccount="Connected account" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Estimate bitrate with bandwidth test (may take a few minutes)" Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Prefer hardware encoding" diff --git a/UI/streaming-helpers.cpp b/UI/streaming-helpers.cpp index 763f9d2331ef8f..0f4692df122548 100644 --- a/UI/streaming-helpers.cpp +++ b/UI/streaming-helpers.cpp @@ -3,6 +3,7 @@ #include "obs-app.hpp" #include "../plugins/rtmp-services/rtmp-format-ver.h" +#include "../plugins/rtmp-services/service-specific/bitmovin-constants.h" #include #include @@ -90,6 +91,40 @@ void StreamSettingsUI::UpdateMoreInfoLink() } } +void StreamSettingsUI::UpdateKey(const QString &key) +{ + QString serviceName = ui_service->currentText(); + if (serviceName == BITMOVIN_SERVICE_NAME) { + obs_service_t *service = obs_frontend_get_streaming_service(); + obs_data_t *settings = obs_service_get_settings(service); + obs_data_set_string(settings, "key", QT_TO_UTF8(key)); + obs_service_update(service, settings); + + BitmovinFillLiveStreamList(); + } +} + +void StreamSettingsUI::BitmovinFillLiveStreamList() +{ + obs_service_t *service = obs_frontend_get_streaming_service(); + obs_properties_t *props = obs_service_properties(service); + obs_property_t *bitmovin_prop = obs_properties_get( + props, BITMOVIN_RUNNING_LIVE_STREAMS_LIST_PROPERTY_NAME); + + ui_server->clear(); + auto count = obs_property_list_item_count(bitmovin_prop); + if (count <= 0) { + ui_server->addItem(QTStr("Basic.AutoConfig.StreamPage.None")); + return; + } + + for (size_t i = 0; i < count; i++) { + ui_server->addItem( + obs_property_list_item_name(bitmovin_prop, i), + obs_property_list_item_string(bitmovin_prop, i)); + } +} + void StreamSettingsUI::UpdateKeyLink() { QString serviceName = ui_service->currentText(); @@ -107,6 +142,9 @@ void StreamSettingsUI::UpdateKeyLink() if (serviceName == "Dacast") { ui_streamKeyLabel->setText( QTStr("Basic.AutoConfig.StreamPage.EncoderKey")); + } else if (serviceName == BITMOVIN_SERVICE_NAME) { + ui_streamKeyLabel->setText( + QTStr("Basic.AutoConfig.StreamPage.ApiKey")); } else { ui_streamKeyLabel->setText( QTStr("Basic.AutoConfig.StreamPage.StreamKey")); @@ -116,6 +154,10 @@ void StreamSettingsUI::UpdateKeyLink() ui_streamKeyButton->hide(); } else { ui_streamKeyButton->setTargetUrl(QUrl(streamKeyLink.c_str())); + if (serviceName == BITMOVIN_SERVICE_NAME) { + ui_streamKeyButton->setText( + QTStr("Basic.AutoConfig.StreamPage.GetApiKey")); + } ui_streamKeyButton->show(); } } @@ -179,6 +221,23 @@ void StreamSettingsUI::UpdateServerList() ui_server->clear(); + if (serviceName == BITMOVIN_SERVICE_NAME) { + ui_serverLabel->setText( + QTStr("Basic.AutoConfig.StreamPage.RunningStream")); + + obs_service_t *streaming_service = + obs_frontend_get_streaming_service(); + obs_data_t *settings = + obs_service_get_settings(streaming_service); + obs_data_set_string(settings, "service", BITMOVIN_SERVICE_NAME); + obs_service_update(streaming_service, settings); + + BitmovinFillLiveStreamList(); + return; + } + + ui_serverLabel->setText(QTStr("Basic.AutoConfig.StreamPage.Server")); + auto &servers = service["servers"].array_items(); for (const Json &entry : servers) { ui_server->addItem(entry["name"].string_value().c_str(), diff --git a/UI/streaming-helpers.hpp b/UI/streaming-helpers.hpp index 170ef3282697fd..aae4c76f845b8c 100644 --- a/UI/streaming-helpers.hpp +++ b/UI/streaming-helpers.hpp @@ -20,6 +20,7 @@ class StreamSettingsUI : public QObject { Q_OBJECT QLabel *ui_streamKeyLabel; + QLabel *ui_serverLabel; QComboBox *ui_service; QComboBox *ui_server; QLineEdit *ui_customServer; @@ -34,9 +35,10 @@ class StreamSettingsUI : public QObject { inline void Setup(QLabel *streamKeyLabel, QComboBox *service, QComboBox *server, QLineEdit *customServer, UrlPushButton *moreInfoButton, - UrlPushButton *streamKeyButton) + UrlPushButton *streamKeyButton, QLabel *serverLabel) { ui_streamKeyLabel = streamKeyLabel; + ui_serverLabel = serverLabel; ui_service = service; ui_server = server; ui_customServer = customServer; @@ -63,8 +65,11 @@ class StreamSettingsUI : public QObject { inline const QString &LastService() const { return lastService; } + void BitmovinFillLiveStreamList(); + public slots: void UpdateMoreInfoLink(); + void UpdateKey(const QString &key); void UpdateKeyLink(); void LoadServices(bool showAll); void UpdateServerList(); diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 6976083319985c..c27ebefbb8bfd2 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -253,7 +253,7 @@ AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) streamUi.Setup(ui->streamKeyLabel, ui->service, ui->server, ui->customServer, ui->moreInfoButton, - ui->streamKeyButton); + ui->streamKeyButton, ui->serverLabel); ui->connectedAccountLabel->setVisible(false); ui->connectedAccountText->setVisible(false); diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 62cf6b6f6deefe..0b0b341d7b1108 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -7,6 +7,7 @@ #include "window-basic-main.hpp" #include "qt-wrappers.hpp" #include "url-push-button.hpp" +#include "../plugins/rtmp-services/service-specific/bitmovin-constants.h" #ifdef BROWSER_AVAILABLE #include @@ -61,7 +62,7 @@ void OBSBasicSettings::InitStreamPage() streamUi.Setup(ui->streamKeyLabel, ui->service, ui->server, ui->customServer, ui->moreInfoButton, - ui->getStreamKeyButton); + ui->getStreamKeyButton, ui->serverLabel); streamUi.LoadServices(false); @@ -92,6 +93,8 @@ void OBSBasicSettings::InitStreamPage() SLOT(UpdateResFPSLimits())); connect(ui->service, SIGNAL(currentIndexChanged(int)), &streamUi, SLOT(UpdateMoreInfoLink())); + connect(ui->key, SIGNAL(textChanged(QString)), &streamUi, + SLOT(UpdateKey(QString))); } void OBSBasicSettings::LoadStream1Settings() @@ -139,19 +142,20 @@ void OBSBasicSettings::LoadStream1Settings() } streamUi.UpdateServerList(); + ui->key->setText(key); if (strcmp(type, "rtmp_common") == 0) { int idx = ui->server->findData(server); if (idx == -1) { - if (server && *server) + if (server && *server && + strcmp(service, BITMOVIN_SERVICE_NAME) != 0) { ui->server->insertItem(0, server, server); + } idx = 0; } ui->server->setCurrentIndex(idx); } - ui->key->setText(key); - streamUi.ClearLastService(); on_service_currentIndexChanged(0); diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index d9fef9a886bb31..30a96db5c9699f 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -26,6 +26,9 @@ target_sources( service-specific/showroom.h service-specific/dacast.c service-specific/dacast.h + service-specific/bitmovin.c + service-specific/bitmovin.h + service-specific/bitmovin-constants.h rtmp-common.c rtmp-custom.c rtmp-services-main.c diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 0d072019bb563a..7161a333c56f04 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 199, + "version": 200, "files": [ { "name": "services.json", - "version": 199 + "version": 200 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 6dee9594453f9c..d05e5c552b1e01 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2337,6 +2337,20 @@ "max video bitrate": 5000, "max audio bitrate": 160 } + }, + { + "name": "Bitmovin", + "more_info_link": "https://bitmovin.com/docs/encoding/tutorials/contribution-encoder-obs-studio-example", + "stream_key_link": "https://bitmovin.com/dashboard/account", + "servers": [ + { + "name": "dummy", + "url": "rtmp://" + } + ], + "recommended": { + "keyint": 2 + } } ] } diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 2d461b8c28e319..74f86ef01181f6 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -10,6 +10,7 @@ #include "service-specific/nimotv.h" #include "service-specific/showroom.h" #include "service-specific/dacast.h" +#include "service-specific/bitmovin.h" struct rtmp_common { char *service; @@ -51,6 +52,8 @@ static void ensure_valid_url(struct rtmp_common *service, json_t *json, return; if (astrstri(service->service, "Facebook") == NULL) return; + if (astrstri(service->service, BITMOVIN_SERVICE_NAME) == NULL) + return; json_array_foreach (servers, index, server) { const char *url = get_string_val(server, "url"); @@ -150,6 +153,10 @@ static void rtmp_common_update(void *data, obs_data_t *settings) } } json_decref(root); + if (service->service && + strcmp(service->service, BITMOVIN_SERVICE_NAME) == 0) { + bitmovin_update(service->key); + } if (!service->output) service->output = bstrdup("rtmp_output"); @@ -518,9 +525,9 @@ static bool show_all_services_toggled(obs_properties_t *ppts, obs_property_t *p, return true; } -static obs_properties_t *rtmp_common_properties(void *unused) +static obs_properties_t *rtmp_common_properties(void *data) { - UNUSED_PARAMETER(unused); + struct rtmp_common *service = data; obs_properties_t *ppts = obs_properties_create(); obs_property_t *p; @@ -546,6 +553,11 @@ static obs_properties_t *rtmp_common_properties(void *unused) obs_properties_add_text(ppts, "key", obs_module_text("StreamKey"), OBS_TEXT_PASSWORD); + + if (strcmp(service->service, BITMOVIN_SERVICE_NAME) == 0) { + bitmovin_get_obs_properties(ppts); + } + return ppts; } @@ -716,6 +728,11 @@ static const char *rtmp_common_url(void *data) return ingest->url; } } + + if (service->service && + strcmp(service->service, BITMOVIN_SERVICE_NAME) == 0) { + return bitmovin_get_ingest(service->key, service->server); + } return service->server; } @@ -738,6 +755,11 @@ static const char *rtmp_common_key(void *data) return ingest->streamkey; } } + + if (service->service && + strcmp(service->service, BITMOVIN_SERVICE_NAME) == 0) { + return bitmovin_get_stream_key(); + } return service->key; } diff --git a/plugins/rtmp-services/rtmp-services-main.c b/plugins/rtmp-services/rtmp-services-main.c index 56ddf061646951..3ccac56d3df43d 100644 --- a/plugins/rtmp-services/rtmp-services-main.c +++ b/plugins/rtmp-services/rtmp-services-main.c @@ -10,6 +10,7 @@ #include "service-specific/showroom.h" #include "service-specific/dacast.h" +#include "service-specific/bitmovin.h" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("rtmp-services", "en-US") @@ -75,6 +76,7 @@ bool obs_module_load(void) { init_twitch_data(); init_dacast_data(); + init_bitmovin_data(); dstr_copy(&module_name, "rtmp-services plugin (libobs "); dstr_cat(&module_name, obs_get_version_string()); @@ -115,5 +117,6 @@ void obs_module_unload(void) unload_twitch_data(); free_showroom_data(); unload_dacast_data(); + unload_bitmovin_data(); dstr_free(&module_name); } diff --git a/plugins/rtmp-services/service-specific/bitmovin-constants.h b/plugins/rtmp-services/service-specific/bitmovin-constants.h new file mode 100644 index 00000000000000..b268bf8a127bcc --- /dev/null +++ b/plugins/rtmp-services/service-specific/bitmovin-constants.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef BITMOVIN_SERVICE_NAME +#define BITMOVIN_SERVICE_NAME "Bitmovin" +#endif + +#ifndef BITMOVIN_RUNNING_LIVE_STREAMS_LIST_PROPERTY_NAME +#define BITMOVIN_RUNNING_LIVE_STREAMS_LIST_PROPERTY_NAME \ + "bitmovin_running_live_streams_list" +#endif diff --git a/plugins/rtmp-services/service-specific/bitmovin.c b/plugins/rtmp-services/service-specific/bitmovin.c new file mode 100644 index 00000000000000..e682aac498fa23 --- /dev/null +++ b/plugins/rtmp-services/service-specific/bitmovin.c @@ -0,0 +1,334 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bitmovin.h" +#include "bitmovin-constants.h" + +#ifndef SEC_TO_NSEC +#define SEC_TO_NSEC 1000000000ULL +#endif + +static char *bitmovin_base_url = "https://api.bitmovin.com/v1/encoding"; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static u_int cache_timeout_in_seconds = 10; + +struct bitmovin_ingest_info { + struct dstr encoding_id; + struct dstr ingest_url; + struct dstr stream_key; + struct dstr encoding_name; +}; + +uint64_t last_time; + +static DARRAY(struct bitmovin_ingest_info) running_live_encodings; + +void free_live_encodings(void) +{ + last_time = 0; + for (size_t i = 0; i < running_live_encodings.num; i++) { + dstr_free(&running_live_encodings.array[i].encoding_id); + dstr_free(&running_live_encodings.array[i].ingest_url); + dstr_free(&running_live_encodings.array[i].stream_key); + dstr_free(&running_live_encodings.array[i].encoding_name); + } + + da_free(running_live_encodings); +} + +void reset_live_encodings(void) +{ + free_live_encodings(); + da_init(running_live_encodings); +} + +static struct bitmovin_ingest_info *find_live_encoding(const char *encoding_id) +{ + struct bitmovin_ingest_info *ret = NULL; + for (size_t i = 0; i < running_live_encodings.num; i++) { + struct bitmovin_ingest_info *info = + &running_live_encodings.array[i]; + if (strcmp(info->encoding_id.array, encoding_id) == 0) { + ret = info; + break; + } + } + + return ret; +} + +static size_t write_cb(void *data, size_t size, size_t nmemb, void *ptr) +{ + struct dstr *json = ptr; + size_t realsize = size * nmemb; + dstr_ncat(json, data, realsize); + return realsize; +} + +static json_t *bitmovin_json_get_request(char *request_url, + struct curl_slist *header) +{ + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if (!curl) { + return NULL; + } + struct dstr response = {0}; + + header = curl_slist_append(header, "accept: application/json"); + header = curl_slist_append(header, "User-Agent: obs-studio"); + + curl_easy_setopt(curl, CURLOPT_URL, request_url); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + + res = curl_easy_perform(curl); + + json_error_t error; + json_t *json_response = json_loads(response.array, 0, &error); + + if (res != CURLE_OK) { + blog(LOG_WARNING, "bitmovin_json_get_request: %s\n", + curl_easy_strerror(res)); + curl_easy_cleanup(curl); + dstr_free(&response); + return NULL; + } + long response_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code != 200) { + blog(LOG_WARNING, "bitmovin_json_get_request: status code: %ld", + response_code); + curl_easy_cleanup(curl); + dstr_free(&response); + return NULL; + } + + curl_easy_cleanup(curl); + dstr_free(&response); + blog(LOG_DEBUG, "bitmovin_json_get_request: %s\n", + json_dumps(json_response, 0)); + return json_response; +} + +void bitmovin_get_stream_details(const char *key, + struct bitmovin_ingest_info *info) +{ + struct curl_slist *header = NULL; + struct dstr api_header = {0}; + const char *key_label = "X-Api-Key: "; + dstr_copy(&api_header, key_label); + dstr_cat(&api_header, key); + + header = curl_slist_append(header, api_header.array); + struct dstr url = {0}; + const char *encodings_url = "/encodings/"; + dstr_copy(&url, bitmovin_base_url); + dstr_cat(&url, encodings_url); + + dstr_cat(&url, info->encoding_id.array); + + const char *live_url = "/live"; + dstr_cat(&url, live_url); + + struct json_t *response = bitmovin_json_get_request(url.array, header); + + curl_slist_free_all(header); + dstr_free(&api_header); + dstr_free(&url); + if (response == NULL) { + return; + } + const char *status = + json_string_value(json_object_get(response, "status")); + if (strcmp(status, "SUCCESS") != 0) { + blog(LOG_INFO, "bitmovin_get_stream_details: STATUS: %s", + status); + return; + } + + struct json_t *data = json_object_get(response, "data"); + struct json_t *result = json_object_get(data, "result"); + const char *encoderIp = + json_string_value(json_object_get(result, "encoderIp")); + const char *application = + json_string_value(json_object_get(result, "application")); + const char *stream_key = + json_string_value(json_object_get(result, "streamKey")); + + dstr_copy(&(info->ingest_url), "rtmp://"); + dstr_cat(&(info->ingest_url), encoderIp); + dstr_cat(&(info->ingest_url), "/"); + dstr_cat(&(info->ingest_url), application); + dstr_copy(&(info->stream_key), stream_key); +} + +void bitmovin_retrieve_running_live_streams(const char *key) +{ + if (key == NULL) { + return; + } + if (running_live_encodings.num > 0) { + // just like the showroom plugin did it: + // 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 - last_time < cache_timeout_in_seconds) { + return; + } + } + + struct curl_slist *header = NULL; + struct dstr api_header = {0}; + const char *key_label = "X-Api-Key: "; + dstr_copy(&api_header, key_label); + dstr_cat(&api_header, key); + + header = curl_slist_append(header, api_header.array); + struct dstr url = {0}; + const char *encodings_url_query = + "/encodings?sort=createdAt%3Adesc&type=LIVE&status=RUNNING"; + dstr_copy(&url, bitmovin_base_url); + dstr_cat(&url, encodings_url_query); + + struct json_t *response = bitmovin_json_get_request(url.array, header); + + curl_slist_free_all(header); + dstr_free(&api_header); + dstr_free(&url); + if (response == NULL) { + return; + } + const char *status = + json_string_value(json_object_get(response, "status")); + if (strcmp(status, "SUCCESS") != 0) { + blog(LOG_WARNING, + "bitmovin_retrieve_running_live_streams: STATUS: %s", + status); + return; + } + struct json_t *data = json_object_get(response, "data"); + struct json_t *result = json_object_get(data, "result"); + struct json_t *items = json_object_get(result, "items"); + + reset_live_encodings(); + + size_t index; + json_t *value; + json_array_foreach (items, index, value) { + const char *encoding_id = + json_string_value(json_object_get(value, "id")); + const char *encoding_name = + json_string_value(json_object_get(value, "name")); + if (encoding_id != NULL) { + blog(LOG_DEBUG, + "bitmovin_retrieve_running_live_streams: encoding_id=%s", + encoding_id); + struct bitmovin_ingest_info *info = + find_live_encoding(encoding_id); + if (!info) { + info = da_push_back_new(running_live_encodings); + } + dstr_copy(&(info->encoding_id), encoding_id); + if (encoding_name != NULL) { + dstr_copy(&(info->encoding_name), + encoding_name); + } + + bitmovin_get_stream_details(key, info); + blog(LOG_DEBUG, + "bitmovin_retrieve_running_live_streams: ingest_url=%s streamkey=%s", + info->ingest_url.array, info->stream_key.array); + } + } + last_time = os_gettime_ns() / SEC_TO_NSEC; +} + +void init_bitmovin_data(void) +{ + pthread_mutex_lock(&mutex); + da_init(running_live_encodings); + pthread_mutex_unlock(&mutex); +} + +void unload_bitmovin_data(void) +{ + pthread_mutex_lock(&mutex); + free_live_encodings(); + pthread_mutex_unlock(&mutex); +} + +void bitmovin_update(const char *key) +{ + if (strlen(key) != 36) { + return; + } + pthread_mutex_lock(&mutex); + bitmovin_retrieve_running_live_streams(key); + pthread_mutex_unlock(&mutex); +} + +const char *bitmovin_get_ingest(const char *key, const char *encoding_id) +{ + pthread_mutex_lock(&mutex); + bitmovin_retrieve_running_live_streams(key); + if (running_live_encodings.num <= 0) { + pthread_mutex_unlock(&mutex); + return NULL; + } + struct bitmovin_ingest_info *info = find_live_encoding(encoding_id); + char *url = NULL; + if (info != NULL) { + url = info->ingest_url.array; + } + pthread_mutex_unlock(&mutex); + blog(LOG_DEBUG, "bitmovin_get_ingest: %s", url); + return url; +} + +const char *bitmovin_get_stream_key(void) +{ + char *result = NULL; + pthread_mutex_lock(&mutex); + if (running_live_encodings.num > 0) { + struct bitmovin_ingest_info *info = + &running_live_encodings.array[0]; + result = info->stream_key.array; + } + pthread_mutex_unlock(&mutex); + blog(LOG_INFO, "bitmovin_get_stream_key: %s", result); + return result; +} + +void bitmovin_get_obs_properties(obs_properties_t *props) +{ + obs_property_t *list = obs_properties_add_list( + props, BITMOVIN_RUNNING_LIVE_STREAMS_LIST_PROPERTY_NAME, + "Running live streams", OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + + for (size_t i = 0; i < running_live_encodings.num; i++) { + struct bitmovin_ingest_info *info = + &running_live_encodings.array[i]; + struct dstr name = {0}; + dstr_copy(&name, info->encoding_name.array); + dstr_cat(&name, " ("); + dstr_cat(&name, info->encoding_id.array); + dstr_cat(&name, ")"); + obs_property_list_insert_string(list, i, name.array, + info->encoding_id.array); + dstr_free(&name); + } +} diff --git a/plugins/rtmp-services/service-specific/bitmovin.h b/plugins/rtmp-services/service-specific/bitmovin.h new file mode 100644 index 00000000000000..74d323c8be52ad --- /dev/null +++ b/plugins/rtmp-services/service-specific/bitmovin.h @@ -0,0 +1,12 @@ +#pragma once + +#include "bitmovin-constants.h" + +extern void init_bitmovin_data(void); +extern void unload_bitmovin_data(void); + +extern void bitmovin_update(const char *key); +extern const char *bitmovin_get_ingest(const char *key, + const char *encoding_id); +extern const char *bitmovin_get_stream_key(void); +extern void bitmovin_get_obs_properties(obs_properties_t *props);