#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; }