rtmp-common.c 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. #include <util/platform.h>
  2. #include <util/dstr.h>
  3. #include <obs-module.h>
  4. #include <jansson.h>
  5. #include <obs-config.h>
  6. #include "rtmp-format-ver.h"
  7. #include "service-specific/twitch.h"
  8. #include "service-specific/younow.h"
  9. #include "service-specific/nimotv.h"
  10. #include "service-specific/showroom.h"
  11. #include "service-specific/dacast.h"
  12. struct rtmp_common {
  13. char *service;
  14. char *protocol;
  15. char *server;
  16. char *key;
  17. struct obs_service_resolution *supported_resolutions;
  18. size_t supported_resolutions_count;
  19. int max_fps;
  20. char **video_codecs;
  21. char **audio_codecs;
  22. bool supports_additional_audio_track;
  23. };
  24. static const char *rtmp_common_getname(void *unused)
  25. {
  26. UNUSED_PARAMETER(unused);
  27. return obs_module_text("StreamingServices");
  28. }
  29. static json_t *open_services_file(void);
  30. static inline json_t *find_service(json_t *root, const char *name,
  31. const char **p_new_name);
  32. static inline bool get_bool_val(json_t *service, const char *key);
  33. static inline const char *get_string_val(json_t *service, const char *key);
  34. static inline int get_int_val(json_t *service, const char *key);
  35. extern void twitch_ingests_refresh(int seconds);
  36. static void ensure_valid_url(struct rtmp_common *service, json_t *json,
  37. obs_data_t *settings)
  38. {
  39. json_t *servers = json_object_get(json, "servers");
  40. const char *top_url = NULL;
  41. json_t *server;
  42. size_t index;
  43. if (!service->server || !servers || !json_is_array(servers))
  44. return;
  45. if (astrstri(service->service, "Facebook") == NULL)
  46. return;
  47. json_array_foreach (servers, index, server) {
  48. const char *url = get_string_val(server, "url");
  49. if (!url)
  50. continue;
  51. if (!top_url)
  52. top_url = url;
  53. if (astrcmpi(service->server, url) == 0)
  54. return;
  55. }
  56. /* server was not found in server list, use first server instead */
  57. if (top_url) {
  58. bfree(service->server);
  59. service->server = bstrdup(top_url);
  60. obs_data_set_string(settings, "server", top_url);
  61. }
  62. }
  63. static void update_recommendations(struct rtmp_common *service, json_t *rec)
  64. {
  65. json_t *sr = json_object_get(rec, "supported resolutions");
  66. if (sr && json_is_array(sr)) {
  67. DARRAY(struct obs_service_resolution) res_list;
  68. json_t *res_obj;
  69. size_t index;
  70. da_init(res_list);
  71. json_array_foreach (sr, index, res_obj) {
  72. if (!json_is_string(res_obj))
  73. continue;
  74. const char *res_str = json_string_value(res_obj);
  75. struct obs_service_resolution res;
  76. if (sscanf(res_str, "%dx%d", &res.cx, &res.cy) != 2)
  77. continue;
  78. if (res.cx <= 0 || res.cy <= 0)
  79. continue;
  80. da_push_back(res_list, &res);
  81. }
  82. if (res_list.num) {
  83. service->supported_resolutions = res_list.array;
  84. service->supported_resolutions_count = res_list.num;
  85. }
  86. }
  87. service->max_fps = get_int_val(rec, "max fps");
  88. }
  89. static void rtmp_common_update(void *data, obs_data_t *settings)
  90. {
  91. struct rtmp_common *service = data;
  92. bfree(service->supported_resolutions);
  93. if (service->video_codecs)
  94. bfree(service->video_codecs);
  95. if (service->audio_codecs)
  96. bfree(service->audio_codecs);
  97. bfree(service->service);
  98. bfree(service->protocol);
  99. bfree(service->server);
  100. bfree(service->key);
  101. service->service = bstrdup(obs_data_get_string(settings, "service"));
  102. service->protocol = bstrdup(obs_data_get_string(settings, "protocol"));
  103. service->server = bstrdup(obs_data_get_string(settings, "server"));
  104. service->key = bstrdup(obs_data_get_string(settings, "key"));
  105. service->supports_additional_audio_track = false;
  106. service->video_codecs = NULL;
  107. service->audio_codecs = NULL;
  108. service->supported_resolutions = NULL;
  109. service->supported_resolutions_count = 0;
  110. service->max_fps = 0;
  111. json_t *root = open_services_file();
  112. if (root) {
  113. const char *new_name;
  114. json_t *serv = find_service(root, service->service, &new_name);
  115. if (new_name) {
  116. bfree(service->service);
  117. service->service = bstrdup(new_name);
  118. }
  119. if (serv) {
  120. json_t *rec = json_object_get(serv, "recommended");
  121. if (json_is_object(rec)) {
  122. update_recommendations(service, rec);
  123. }
  124. service->supports_additional_audio_track = get_bool_val(
  125. serv, "supports_additional_audio_track");
  126. ensure_valid_url(service, serv, settings);
  127. }
  128. }
  129. json_decref(root);
  130. }
  131. static void rtmp_common_destroy(void *data)
  132. {
  133. struct rtmp_common *service = data;
  134. bfree(service->supported_resolutions);
  135. if (service->video_codecs)
  136. bfree(service->video_codecs);
  137. if (service->audio_codecs)
  138. bfree(service->audio_codecs);
  139. bfree(service->service);
  140. bfree(service->protocol);
  141. bfree(service->server);
  142. bfree(service->key);
  143. bfree(service);
  144. }
  145. static void *rtmp_common_create(obs_data_t *settings, obs_service_t *service)
  146. {
  147. struct rtmp_common *data = bzalloc(sizeof(struct rtmp_common));
  148. rtmp_common_update(data, settings);
  149. UNUSED_PARAMETER(service);
  150. return data;
  151. }
  152. static inline const char *get_string_val(json_t *service, const char *key)
  153. {
  154. json_t *str_val = json_object_get(service, key);
  155. if (!str_val || !json_is_string(str_val))
  156. return NULL;
  157. return json_string_value(str_val);
  158. }
  159. static inline int get_int_val(json_t *service, const char *key)
  160. {
  161. json_t *integer_val = json_object_get(service, key);
  162. if (!integer_val || !json_is_integer(integer_val))
  163. return 0;
  164. return (int)json_integer_value(integer_val);
  165. }
  166. static inline bool get_bool_val(json_t *service, const char *key)
  167. {
  168. json_t *bool_val = json_object_get(service, key);
  169. if (!bool_val || !json_is_boolean(bool_val))
  170. return false;
  171. return json_is_true(bool_val);
  172. }
  173. #define RTMP_PREFIX "rtmp://"
  174. #define RTMPS_PREFIX "rtmps://"
  175. static bool is_protocol_available(json_t *service)
  176. {
  177. const char *protocol = get_string_val(service, "protocol");
  178. if (protocol)
  179. return obs_is_output_protocol_registered(protocol);
  180. /* Test RTMP and RTMPS if no protocol found */
  181. json_t *servers;
  182. size_t index;
  183. json_t *server;
  184. const char *url;
  185. bool ret = false;
  186. servers = json_object_get(service, "servers");
  187. json_array_foreach (servers, index, server) {
  188. url = get_string_val(server, "url");
  189. if (strncmp(url, RTMP_PREFIX, strlen(RTMP_PREFIX)) == 0)
  190. ret |= obs_is_output_protocol_registered("RTMP");
  191. else if (strncmp(url, RTMPS_PREFIX, strlen(RTMPS_PREFIX)) == 0)
  192. ret |= obs_is_output_protocol_registered("RTMPS");
  193. }
  194. return ret;
  195. }
  196. static void add_service(obs_property_t *list, json_t *service, bool show_all,
  197. const char *cur_service)
  198. {
  199. json_t *servers;
  200. const char *name;
  201. bool common;
  202. if (!json_is_object(service)) {
  203. blog(LOG_WARNING, "rtmp-common.c: [add_service] service "
  204. "is not an object");
  205. return;
  206. }
  207. name = get_string_val(service, "name");
  208. if (!name) {
  209. blog(LOG_WARNING, "rtmp-common.c: [add_service] service "
  210. "has no name");
  211. return;
  212. }
  213. common = get_bool_val(service, "common");
  214. if (!show_all && !common && strcmp(cur_service, name) != 0) {
  215. return;
  216. }
  217. servers = json_object_get(service, "servers");
  218. if (!servers || !json_is_array(servers)) {
  219. blog(LOG_WARNING,
  220. "rtmp-common.c: [add_service] service "
  221. "'%s' has no servers",
  222. name);
  223. return;
  224. }
  225. obs_property_list_add_string(list, name, name);
  226. }
  227. static void add_services(obs_property_t *list, json_t *root, bool show_all,
  228. const char *cur_service)
  229. {
  230. json_t *service;
  231. size_t index;
  232. if (!json_is_array(root)) {
  233. blog(LOG_WARNING, "rtmp-common.c: [add_services] JSON file "
  234. "root is not an array");
  235. return;
  236. }
  237. json_array_foreach (root, index, service) {
  238. /* Skip service with non-available protocol */
  239. if (!is_protocol_available(service))
  240. continue;
  241. add_service(list, service, show_all, cur_service);
  242. }
  243. service = find_service(root, cur_service, NULL);
  244. if (!service && cur_service && *cur_service) {
  245. obs_property_list_insert_string(list, 0, cur_service,
  246. cur_service);
  247. obs_property_list_item_disable(list, 0, true);
  248. }
  249. }
  250. static json_t *open_json_file(const char *file)
  251. {
  252. char *file_data = os_quick_read_utf8_file(file);
  253. json_error_t error;
  254. json_t *root;
  255. json_t *list;
  256. int format_ver;
  257. if (!file_data)
  258. return NULL;
  259. root = json_loads(file_data, JSON_REJECT_DUPLICATES, &error);
  260. bfree(file_data);
  261. if (!root) {
  262. blog(LOG_WARNING,
  263. "rtmp-common.c: [open_json_file] "
  264. "Error reading JSON file (%d): %s",
  265. error.line, error.text);
  266. return NULL;
  267. }
  268. format_ver = get_int_val(root, "format_version");
  269. if (format_ver != RTMP_SERVICES_FORMAT_VERSION) {
  270. blog(LOG_DEBUG,
  271. "rtmp-common.c: [open_json_file] "
  272. "Wrong format version (%d), expected %d",
  273. format_ver, RTMP_SERVICES_FORMAT_VERSION);
  274. json_decref(root);
  275. return NULL;
  276. }
  277. list = json_object_get(root, "services");
  278. if (list)
  279. json_incref(list);
  280. json_decref(root);
  281. if (!list) {
  282. blog(LOG_WARNING, "rtmp-common.c: [open_json_file] "
  283. "No services list");
  284. return NULL;
  285. }
  286. return list;
  287. }
  288. static json_t *open_services_file(void)
  289. {
  290. char *file;
  291. json_t *root = NULL;
  292. file = obs_module_config_path("services.json");
  293. if (file) {
  294. root = open_json_file(file);
  295. bfree(file);
  296. }
  297. if (!root) {
  298. file = obs_module_file("services.json");
  299. if (file) {
  300. root = open_json_file(file);
  301. bfree(file);
  302. }
  303. }
  304. return root;
  305. }
  306. static void build_service_list(obs_property_t *list, json_t *root,
  307. bool show_all, const char *cur_service)
  308. {
  309. obs_property_list_clear(list);
  310. add_services(list, root, show_all, cur_service);
  311. }
  312. static void properties_data_destroy(void *data)
  313. {
  314. json_t *root = data;
  315. if (root)
  316. json_decref(root);
  317. }
  318. static bool fill_twitch_servers_locked(obs_property_t *servers_prop)
  319. {
  320. size_t count = twitch_ingest_count();
  321. obs_property_list_add_string(servers_prop,
  322. obs_module_text("Server.Auto"), "auto");
  323. if (count <= 1)
  324. return false;
  325. for (size_t i = 0; i < count; i++) {
  326. struct twitch_ingest ing = twitch_ingest(i);
  327. obs_property_list_add_string(servers_prop, ing.name, ing.url);
  328. }
  329. return true;
  330. }
  331. static inline bool fill_twitch_servers(obs_property_t *servers_prop)
  332. {
  333. bool success;
  334. twitch_ingests_lock();
  335. success = fill_twitch_servers_locked(servers_prop);
  336. twitch_ingests_unlock();
  337. return success;
  338. }
  339. static void fill_servers(obs_property_t *servers_prop, json_t *service,
  340. const char *name)
  341. {
  342. json_t *servers, *server;
  343. size_t index;
  344. obs_property_list_clear(servers_prop);
  345. servers = json_object_get(service, "servers");
  346. if (!json_is_array(servers)) {
  347. blog(LOG_WARNING,
  348. "rtmp-common.c: [fill_servers] "
  349. "Servers for service '%s' not a valid object",
  350. name);
  351. return;
  352. }
  353. /* Assumption: Twitch should be RTMP only, so no RTMPS check */
  354. if (strcmp(name, "Twitch") == 0) {
  355. if (fill_twitch_servers(servers_prop))
  356. return;
  357. }
  358. /* Assumption: Nimo TV should be RTMP only, so no RTMPS check in the ingest */
  359. if (strcmp(name, "Nimo TV") == 0) {
  360. obs_property_list_add_string(
  361. servers_prop, obs_module_text("Server.Auto"), "auto");
  362. }
  363. json_array_foreach (servers, index, server) {
  364. const char *server_name = get_string_val(server, "name");
  365. const char *url = get_string_val(server, "url");
  366. if (!server_name || !url)
  367. continue;
  368. /* Skip RTMPS server if protocol not registered */
  369. if ((!obs_is_output_protocol_registered("RTMPS")) &&
  370. (strncmp(url, "rtmps://", 8) == 0))
  371. continue;
  372. obs_property_list_add_string(servers_prop, server_name, url);
  373. }
  374. }
  375. static void fill_more_info_link(json_t *service, obs_data_t *settings)
  376. {
  377. const char *more_info_link;
  378. more_info_link = get_string_val(service, "more_info_link");
  379. if (more_info_link)
  380. obs_data_set_string(settings, "more_info_link", more_info_link);
  381. }
  382. static void fill_stream_key_link(json_t *service, obs_data_t *settings)
  383. {
  384. const char *stream_key_link;
  385. stream_key_link = get_string_val(service, "stream_key_link");
  386. if (stream_key_link)
  387. obs_data_set_string(settings, "stream_key_link",
  388. stream_key_link);
  389. }
  390. static void update_protocol(json_t *service, obs_data_t *settings)
  391. {
  392. const char *protocol = get_string_val(service, "protocol");
  393. if (protocol) {
  394. obs_data_set_string(settings, "protocol", protocol);
  395. return;
  396. }
  397. json_t *servers = json_object_get(service, "servers");
  398. if (!json_is_array(servers))
  399. return;
  400. json_t *server = json_array_get(servers, 0);
  401. const char *url = get_string_val(server, "url");
  402. if (strncmp(url, RTMPS_PREFIX, strlen(RTMPS_PREFIX)) == 0) {
  403. obs_data_set_string(settings, "protocol", "RTMPS");
  404. return;
  405. }
  406. obs_data_set_string(settings, "protocol", "RTMP");
  407. }
  408. static inline json_t *find_service(json_t *root, const char *name,
  409. const char **p_new_name)
  410. {
  411. size_t index;
  412. json_t *service;
  413. if (p_new_name)
  414. *p_new_name = NULL;
  415. json_array_foreach (root, index, service) {
  416. /* skip service with non-available protocol */
  417. if (!is_protocol_available(service))
  418. continue;
  419. const char *cur_name = get_string_val(service, "name");
  420. if (strcmp(name, cur_name) == 0)
  421. return service;
  422. /* check for alternate names */
  423. json_t *alt_names = json_object_get(service, "alt_names");
  424. size_t alt_name_idx;
  425. json_t *alt_name_obj;
  426. json_array_foreach (alt_names, alt_name_idx, alt_name_obj) {
  427. const char *alt_name = json_string_value(alt_name_obj);
  428. if (alt_name && strcmp(name, alt_name) == 0) {
  429. if (p_new_name)
  430. *p_new_name = cur_name;
  431. return service;
  432. }
  433. }
  434. }
  435. return NULL;
  436. }
  437. static bool service_selected(obs_properties_t *props, obs_property_t *p,
  438. obs_data_t *settings)
  439. {
  440. const char *name = obs_data_get_string(settings, "service");
  441. json_t *root = obs_properties_get_param(props);
  442. json_t *service;
  443. const char *new_name;
  444. if (!name || !*name)
  445. return false;
  446. service = find_service(root, name, &new_name);
  447. if (!service) {
  448. const char *server = obs_data_get_string(settings, "server");
  449. obs_property_list_insert_string(p, 0, name, name);
  450. obs_property_list_item_disable(p, 0, true);
  451. p = obs_properties_get(props, "server");
  452. obs_property_list_insert_string(p, 0, server, server);
  453. obs_property_list_item_disable(p, 0, true);
  454. return true;
  455. }
  456. if (new_name) {
  457. name = new_name;
  458. obs_data_set_string(settings, "service", name);
  459. }
  460. fill_servers(obs_properties_get(props, "server"), service, name);
  461. fill_more_info_link(service, settings);
  462. fill_stream_key_link(service, settings);
  463. update_protocol(service, settings);
  464. return true;
  465. }
  466. static bool show_all_services_toggled(obs_properties_t *ppts, obs_property_t *p,
  467. obs_data_t *settings)
  468. {
  469. const char *cur_service = obs_data_get_string(settings, "service");
  470. bool show_all = obs_data_get_bool(settings, "show_all");
  471. json_t *root = obs_properties_get_param(ppts);
  472. if (!root)
  473. return false;
  474. build_service_list(obs_properties_get(ppts, "service"), root, show_all,
  475. cur_service);
  476. UNUSED_PARAMETER(p);
  477. return true;
  478. }
  479. static obs_properties_t *rtmp_common_properties(void *unused)
  480. {
  481. UNUSED_PARAMETER(unused);
  482. obs_properties_t *ppts = obs_properties_create();
  483. obs_property_t *p;
  484. json_t *root;
  485. root = open_services_file();
  486. if (root)
  487. obs_properties_set_param(ppts, root, properties_data_destroy);
  488. p = obs_properties_add_list(ppts, "service", obs_module_text("Service"),
  489. OBS_COMBO_TYPE_LIST,
  490. OBS_COMBO_FORMAT_STRING);
  491. obs_property_set_modified_callback(p, service_selected);
  492. p = obs_properties_add_bool(ppts, "show_all",
  493. obs_module_text("ShowAll"));
  494. obs_property_set_modified_callback(p, show_all_services_toggled);
  495. obs_properties_add_list(ppts, "server", obs_module_text("Server"),
  496. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
  497. obs_properties_add_text(ppts, "key", obs_module_text("StreamKey"),
  498. OBS_TEXT_PASSWORD);
  499. return ppts;
  500. }
  501. static int get_bitrate_matrix_max(json_t *array);
  502. static void apply_video_encoder_settings(obs_data_t *settings,
  503. json_t *recommended)
  504. {
  505. json_t *item = json_object_get(recommended, "keyint");
  506. if (json_is_integer(item)) {
  507. int keyint = (int)json_integer_value(item);
  508. obs_data_set_int(settings, "keyint_sec", keyint);
  509. }
  510. obs_data_set_string(settings, "rate_control", "CBR");
  511. item = json_object_get(recommended, "profile");
  512. obs_data_item_t *enc_item = obs_data_item_byname(settings, "profile");
  513. if (json_is_string(item) &&
  514. obs_data_item_gettype(enc_item) == OBS_DATA_STRING) {
  515. const char *profile = json_string_value(item);
  516. obs_data_set_string(settings, "profile", profile);
  517. }
  518. obs_data_item_release(&enc_item);
  519. int max_bitrate = 0;
  520. item = json_object_get(recommended, "bitrate matrix");
  521. if (json_is_array(item)) {
  522. max_bitrate = get_bitrate_matrix_max(item);
  523. }
  524. item = json_object_get(recommended, "max video bitrate");
  525. if (!max_bitrate && json_is_integer(item)) {
  526. max_bitrate = (int)json_integer_value(item);
  527. }
  528. if (max_bitrate &&
  529. obs_data_get_int(settings, "bitrate") > max_bitrate) {
  530. obs_data_set_int(settings, "bitrate", max_bitrate);
  531. obs_data_set_int(settings, "buffer_size", max_bitrate);
  532. }
  533. item = json_object_get(recommended, "bframes");
  534. if (json_is_integer(item)) {
  535. int bframes = (int)json_integer_value(item);
  536. obs_data_set_int(settings, "bf", bframes);
  537. }
  538. item = json_object_get(recommended, "x264opts");
  539. if (json_is_string(item)) {
  540. const char *x264_settings = json_string_value(item);
  541. const char *cur_settings =
  542. obs_data_get_string(settings, "x264opts");
  543. struct dstr opts;
  544. dstr_init_copy(&opts, cur_settings);
  545. if (!dstr_is_empty(&opts))
  546. dstr_cat(&opts, " ");
  547. dstr_cat(&opts, x264_settings);
  548. obs_data_set_string(settings, "x264opts", opts.array);
  549. dstr_free(&opts);
  550. }
  551. }
  552. static void apply_audio_encoder_settings(obs_data_t *settings,
  553. json_t *recommended)
  554. {
  555. json_t *item = json_object_get(recommended, "max audio bitrate");
  556. if (json_is_integer(item)) {
  557. int max_bitrate = (int)json_integer_value(item);
  558. if (obs_data_get_int(settings, "bitrate") > max_bitrate)
  559. obs_data_set_int(settings, "bitrate", max_bitrate);
  560. }
  561. }
  562. static void initialize_output(struct rtmp_common *service, json_t *root,
  563. obs_data_t *video_settings,
  564. obs_data_t *audio_settings)
  565. {
  566. json_t *json_service = find_service(root, service->service, NULL);
  567. json_t *recommended;
  568. if (!json_service) {
  569. if (service->service && *service->service)
  570. blog(LOG_WARNING,
  571. "rtmp-common.c: [initialize_output] "
  572. "Could not find service '%s'",
  573. service->service);
  574. return;
  575. }
  576. recommended = json_object_get(json_service, "recommended");
  577. if (!recommended)
  578. return;
  579. if (video_settings)
  580. apply_video_encoder_settings(video_settings, recommended);
  581. if (audio_settings)
  582. apply_audio_encoder_settings(audio_settings, recommended);
  583. }
  584. static void rtmp_common_apply_settings(void *data, obs_data_t *video_settings,
  585. obs_data_t *audio_settings)
  586. {
  587. struct rtmp_common *service = data;
  588. json_t *root = open_services_file();
  589. if (root) {
  590. initialize_output(service, root, video_settings,
  591. audio_settings);
  592. json_decref(root);
  593. }
  594. }
  595. static const char *rtmp_common_url(void *data)
  596. {
  597. struct rtmp_common *service = data;
  598. if (service->service && strcmp(service->service, "Twitch") == 0) {
  599. if (service->server && strcmp(service->server, "auto") == 0) {
  600. struct twitch_ingest ing;
  601. twitch_ingests_refresh(3);
  602. twitch_ingests_lock();
  603. ing = twitch_ingest(0);
  604. twitch_ingests_unlock();
  605. return ing.url;
  606. }
  607. }
  608. if (service->service && strcmp(service->service, "YouNow") == 0) {
  609. if (service->server && service->key) {
  610. return younow_get_ingest(service->server, service->key);
  611. }
  612. }
  613. if (service->service && strcmp(service->service, "Nimo TV") == 0) {
  614. if (service->server && strcmp(service->server, "auto") == 0) {
  615. return nimotv_get_ingest(service->key);
  616. }
  617. }
  618. if (service->service && strcmp(service->service, "SHOWROOM") == 0) {
  619. if (service->server && service->key) {
  620. struct showroom_ingest *ingest;
  621. ingest = showroom_get_ingest(service->server,
  622. service->key);
  623. return ingest->url;
  624. }
  625. }
  626. if (service->service && strcmp(service->service, "Dacast") == 0) {
  627. if (service->server && service->key) {
  628. dacast_ingests_load_data(service->server, service->key);
  629. struct dacast_ingest *ingest;
  630. ingest = dacast_ingest(service->key);
  631. return ingest->url;
  632. }
  633. }
  634. return service->server;
  635. }
  636. static const char *rtmp_common_key(void *data)
  637. {
  638. struct rtmp_common *service = data;
  639. if (service->service && strcmp(service->service, "SHOWROOM") == 0) {
  640. if (service->server && service->key) {
  641. struct showroom_ingest *ingest;
  642. ingest = showroom_get_ingest(service->server,
  643. service->key);
  644. return ingest->key;
  645. }
  646. }
  647. if (service->service && strcmp(service->service, "Dacast") == 0) {
  648. if (service->key) {
  649. struct dacast_ingest *ingest;
  650. ingest = dacast_ingest(service->key);
  651. return ingest->streamkey;
  652. }
  653. }
  654. return service->key;
  655. }
  656. static bool supports_multitrack(void *data)
  657. {
  658. struct rtmp_common *service = data;
  659. return service->supports_additional_audio_track;
  660. }
  661. static void rtmp_common_get_supported_resolutions(
  662. void *data, struct obs_service_resolution **resolutions, size_t *count)
  663. {
  664. struct rtmp_common *service = data;
  665. if (service->supported_resolutions_count) {
  666. *count = service->supported_resolutions_count;
  667. *resolutions =
  668. bmemdup(service->supported_resolutions,
  669. *count * sizeof(struct obs_service_resolution));
  670. } else {
  671. *count = 0;
  672. *resolutions = NULL;
  673. }
  674. }
  675. static void rtmp_common_get_max_fps(void *data, int *fps)
  676. {
  677. struct rtmp_common *service = data;
  678. *fps = service->max_fps;
  679. }
  680. static int get_bitrate_matrix_max(json_t *array)
  681. {
  682. size_t index;
  683. json_t *item;
  684. struct obs_video_info ovi;
  685. if (!obs_get_video_info(&ovi))
  686. return 0;
  687. double cur_fps = (double)ovi.fps_num / (double)ovi.fps_den;
  688. json_array_foreach (array, index, item) {
  689. if (!json_is_object(item))
  690. continue;
  691. const char *res = get_string_val(item, "res");
  692. double fps = (double)get_int_val(item, "fps") + 0.0000001;
  693. int bitrate = get_int_val(item, "max bitrate");
  694. if (!res)
  695. continue;
  696. int cx, cy;
  697. int c = sscanf(res, "%dx%d", &cx, &cy);
  698. if (c != 2)
  699. continue;
  700. if ((int)ovi.output_width == cx &&
  701. (int)ovi.output_height == cy && cur_fps <= fps)
  702. return bitrate;
  703. }
  704. return 0;
  705. }
  706. static void rtmp_common_get_max_bitrate(void *data, int *video_bitrate,
  707. int *audio_bitrate)
  708. {
  709. struct rtmp_common *service = data;
  710. json_t *root = open_services_file();
  711. json_t *item;
  712. if (!root)
  713. return;
  714. json_t *json_service = find_service(root, service->service, NULL);
  715. if (!json_service) {
  716. goto fail;
  717. }
  718. json_t *recommended = json_object_get(json_service, "recommended");
  719. if (!recommended) {
  720. goto fail;
  721. }
  722. if (audio_bitrate) {
  723. item = json_object_get(recommended, "max audio bitrate");
  724. if (json_is_integer(item))
  725. *audio_bitrate = (int)json_integer_value(item);
  726. }
  727. if (video_bitrate) {
  728. int bitrate = 0;
  729. item = json_object_get(recommended, "bitrate matrix");
  730. if (json_is_array(item)) {
  731. bitrate = get_bitrate_matrix_max(item);
  732. }
  733. if (!bitrate) {
  734. item = json_object_get(recommended,
  735. "max video bitrate");
  736. if (json_is_integer(item))
  737. bitrate = (int)json_integer_value(item);
  738. }
  739. *video_bitrate = bitrate;
  740. }
  741. fail:
  742. json_decref(root);
  743. }
  744. static const char **rtmp_common_get_supported_video_codecs(void *data)
  745. {
  746. struct rtmp_common *service = data;
  747. if (service->video_codecs)
  748. return (const char **)service->video_codecs;
  749. struct dstr codecs = {0};
  750. json_t *root = open_services_file();
  751. if (!root)
  752. return NULL;
  753. json_t *json_service = find_service(root, service->service, NULL);
  754. if (!json_service) {
  755. goto fail;
  756. }
  757. json_t *json_video_codecs =
  758. json_object_get(json_service, "supported video codecs");
  759. if (!json_is_array(json_video_codecs)) {
  760. goto fail;
  761. }
  762. size_t index;
  763. json_t *item;
  764. json_array_foreach (json_video_codecs, index, item) {
  765. char codec[16];
  766. snprintf(codec, sizeof(codec), "%s", json_string_value(item));
  767. if (codecs.len)
  768. dstr_cat(&codecs, ";");
  769. dstr_cat(&codecs, codec);
  770. }
  771. service->video_codecs = strlist_split(codecs.array, ';', false);
  772. dstr_free(&codecs);
  773. fail:
  774. json_decref(root);
  775. return (const char **)service->video_codecs;
  776. }
  777. static const char **rtmp_common_get_supported_audio_codecs(void *data)
  778. {
  779. struct rtmp_common *service = data;
  780. if (service->audio_codecs)
  781. return (const char **)service->audio_codecs;
  782. struct dstr codecs = {0};
  783. json_t *root = open_services_file();
  784. if (!root)
  785. return NULL;
  786. json_t *json_service = find_service(root, service->service, NULL);
  787. if (!json_service) {
  788. goto fail;
  789. }
  790. json_t *json_audio_codecs =
  791. json_object_get(json_service, "supported audio codecs");
  792. if (json_is_array(json_audio_codecs)) {
  793. goto fail;
  794. }
  795. size_t index;
  796. json_t *item;
  797. json_array_foreach (json_audio_codecs, index, item) {
  798. char codec[16];
  799. snprintf(codec, sizeof(codec), "%s", json_string_value(item));
  800. if (codecs.len)
  801. dstr_cat(&codecs, ";");
  802. dstr_cat(&codecs, codec);
  803. }
  804. service->audio_codecs = strlist_split(codecs.array, ';', false);
  805. dstr_free(&codecs);
  806. fail:
  807. json_decref(root);
  808. return (const char **)service->audio_codecs;
  809. }
  810. static const char *rtmp_common_username(void *data)
  811. {
  812. struct rtmp_common *service = data;
  813. if (service->service && strcmp(service->service, "Dacast") == 0) {
  814. if (service->key) {
  815. struct dacast_ingest *ingest;
  816. ingest = dacast_ingest(service->key);
  817. return ingest->username;
  818. }
  819. }
  820. return NULL;
  821. }
  822. static const char *rtmp_common_password(void *data)
  823. {
  824. struct rtmp_common *service = data;
  825. if (service->service && strcmp(service->service, "Dacast") == 0) {
  826. if (service->key) {
  827. struct dacast_ingest *ingest;
  828. ingest = dacast_ingest(service->key);
  829. return ingest->password;
  830. }
  831. }
  832. return NULL;
  833. }
  834. static const char *rtmp_common_get_protocol(void *data)
  835. {
  836. struct rtmp_common *service = data;
  837. return service->protocol ? service->protocol : "RTMP";
  838. }
  839. struct obs_service_info rtmp_common_service = {
  840. .id = "rtmp_common",
  841. .get_name = rtmp_common_getname,
  842. .create = rtmp_common_create,
  843. .destroy = rtmp_common_destroy,
  844. .update = rtmp_common_update,
  845. .get_properties = rtmp_common_properties,
  846. .get_protocol = rtmp_common_get_protocol,
  847. .get_url = rtmp_common_url,
  848. .get_key = rtmp_common_key,
  849. .get_username = rtmp_common_username,
  850. .get_password = rtmp_common_password,
  851. .apply_encoder_settings = rtmp_common_apply_settings,
  852. .get_supported_resolutions = rtmp_common_get_supported_resolutions,
  853. .get_max_fps = rtmp_common_get_max_fps,
  854. .get_max_bitrate = rtmp_common_get_max_bitrate,
  855. .get_supported_video_codecs = rtmp_common_get_supported_video_codecs,
  856. .get_supported_audio_codecs = rtmp_common_get_supported_audio_codecs,
  857. };