obs-ffmpeg-rist.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * The following code is a port of FFmpeg/avformat/librist.c for obs-studio.
  3. * Port by [email protected].
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #pragma once
  18. #include <obs-module.h>
  19. #include "obs-ffmpeg-url.h"
  20. #include <librist/librist.h>
  21. #include <librist/version.h>
  22. // RIST_MAX_PACKET_SIZE - 28 minimum protocol overhead
  23. #define RIST_MAX_PAYLOAD_SIZE (10000 - 28)
  24. #define FF_LIBRIST_MAKE_VERSION(major, minor, patch) \
  25. ((patch) + ((minor)*0x100) + ((major)*0x10000))
  26. #define FF_LIBRIST_VERSION \
  27. FF_LIBRIST_MAKE_VERSION(LIBRIST_API_VERSION_MAJOR, \
  28. LIBRIST_API_VERSION_MINOR, \
  29. LIBRIST_API_VERSION_PATCH)
  30. #define FF_LIBRIST_VERSION_41 FF_LIBRIST_MAKE_VERSION(4, 1, 0)
  31. #define FF_LIBRIST_VERSION_42 FF_LIBRIST_MAKE_VERSION(4, 2, 0)
  32. #define FF_LIBRIST_FIFO_DEFAULT_SHIFT 13
  33. typedef struct RISTContext {
  34. int profile;
  35. int buffer_size;
  36. int packet_size;
  37. int log_level;
  38. int encryption;
  39. int fifo_shift;
  40. bool overrun_nonfatal;
  41. char *secret;
  42. struct rist_logging_settings logging_settings;
  43. struct rist_peer_config peer_config;
  44. struct rist_peer *peer;
  45. struct rist_ctx *ctx;
  46. int statsinterval;
  47. struct rist_stats_sender_peer *stats_list;
  48. } RISTContext;
  49. static int risterr2ret(int err)
  50. {
  51. switch (err) {
  52. case RIST_ERR_MALLOC:
  53. return AVERROR(ENOMEM);
  54. default:
  55. return AVERROR_EXTERNAL;
  56. }
  57. }
  58. static int log_cb(void *arg, enum rist_log_level log_level, const char *msg)
  59. {
  60. int level;
  61. switch (log_level) {
  62. case RIST_LOG_ERROR:
  63. level = AV_LOG_ERROR;
  64. break;
  65. case RIST_LOG_WARN:
  66. level = AV_LOG_WARNING;
  67. break;
  68. case RIST_LOG_NOTICE:
  69. level = AV_LOG_INFO;
  70. break;
  71. case RIST_LOG_INFO:
  72. level = AV_LOG_VERBOSE;
  73. break;
  74. case RIST_LOG_DEBUG:
  75. level = AV_LOG_DEBUG;
  76. break;
  77. case RIST_LOG_DISABLE:
  78. level = AV_LOG_QUIET;
  79. break;
  80. default:
  81. level = AV_LOG_WARNING;
  82. }
  83. av_log(arg, level, "%s", msg);
  84. return 0;
  85. }
  86. static int librist_close(URLContext *h)
  87. {
  88. RISTContext *s = h->priv_data;
  89. int ret = 0;
  90. s->peer = NULL;
  91. if (s->ctx)
  92. ret = rist_destroy(s->ctx);
  93. if (ret < 0) {
  94. blog(LOG_ERROR,
  95. "[obs-ffmpeg mpegts muxer / librist] : failed to close properly %s\n",
  96. h->url);
  97. return -1;
  98. }
  99. s->ctx = NULL;
  100. return 0;
  101. }
  102. static int cb_stats(void *arg, const struct rist_stats *stats_container)
  103. {
  104. RISTContext *s = (RISTContext *)arg;
  105. rist_log(&s->logging_settings, RIST_LOG_INFO, "%s\n",
  106. stats_container->stats_json);
  107. if (stats_container->stats_type == RIST_STATS_SENDER_PEER) {
  108. blog(LOG_DEBUG,
  109. "[obs-ffmpeg mpegts muxer / librist] RIST STATS\n\n"
  110. "bandwidth [%.3f Mbps]\npackets sent [%llu]\npkts received [%llu]\n"
  111. "pkts retransmitted [%llu]\nquality (pkt sent over sent+retransmitted+skipped) [%.2f] \n"
  112. "rtt [%" PRIu32 " ms]\n\n",
  113. (double)(stats_container->stats.sender_peer.bandwidth) /
  114. 1000000.0,
  115. stats_container->stats.sender_peer.sent,
  116. stats_container->stats.sender_peer.received,
  117. stats_container->stats.sender_peer.retransmitted,
  118. stats_container->stats.sender_peer.quality,
  119. stats_container->stats.sender_peer.rtt);
  120. }
  121. rist_stats_free(stats_container);
  122. return 0;
  123. }
  124. static int librist_open(URLContext *h, const char *uri)
  125. {
  126. RISTContext *s = h->priv_data;
  127. struct rist_logging_settings *logging_settings = &s->logging_settings;
  128. struct rist_peer_config *peer_config = &s->peer_config;
  129. int ret;
  130. s->buffer_size = 3000;
  131. s->profile = RIST_PROFILE_MAIN;
  132. s->packet_size = 1316;
  133. s->log_level = RIST_LOG_INFO;
  134. s->encryption = 0;
  135. s->secret = NULL;
  136. s->overrun_nonfatal = 0;
  137. s->fifo_shift = FF_LIBRIST_FIFO_DEFAULT_SHIFT;
  138. s->logging_settings =
  139. (struct rist_logging_settings)LOGGING_SETTINGS_INITIALIZER;
  140. s->statsinterval = 60000; // log stats every 60 seconds
  141. ret = rist_logging_set(&logging_settings, s->log_level, log_cb, h, NULL,
  142. NULL);
  143. if (ret < 0) {
  144. blog(LOG_ERROR,
  145. "[obs-ffmpeg mpegts muxer / librist] : Failed to initialize logging settings.");
  146. return OBS_OUTPUT_CONNECT_FAILED;
  147. }
  148. blog(LOG_INFO,
  149. "[obs-ffmpeg mpegts muxer / librist] : \n librist version %s & API = %s .",
  150. librist_version(), librist_api_version());
  151. h->max_packet_size = s->packet_size;
  152. ret = rist_sender_create(&s->ctx, s->profile, 0, logging_settings);
  153. if (ret < 0) {
  154. blog(LOG_ERROR,
  155. "[obs-ffmpeg mpegts muxer / librist] : failed to create a sender \n");
  156. goto err;
  157. }
  158. ret = rist_peer_config_defaults_set(peer_config);
  159. if (ret < 0) {
  160. blog(LOG_ERROR,
  161. "[obs-ffmpeg mpegts muxer / librist] : failed to set peer config defaults.\n");
  162. goto err;
  163. }
  164. #if FF_LIBRIST_VERSION < FF_LIBRIST_VERSION_41
  165. ret = rist_parse_address(
  166. uri, (const struct rist_peer_config **)&peer_config);
  167. #else
  168. ret = rist_parse_address2(uri, &peer_config);
  169. #endif
  170. if (ret < 0) {
  171. blog(LOG_ERROR,
  172. "[obs-ffmpeg mpegts muxer / librist] : failed to parse the url %s\n",
  173. uri);
  174. librist_close(h);
  175. return OBS_OUTPUT_INVALID_STREAM;
  176. }
  177. if (((s->encryption == 128 || s->encryption == 256) && !s->secret) ||
  178. ((peer_config->key_size == 128 || peer_config->key_size == 256) &&
  179. !peer_config->secret[0])) {
  180. blog(LOG_ERROR,
  181. "secret is mandatory if encryption is enabled\n");
  182. librist_close(h);
  183. return OBS_OUTPUT_INVALID_STREAM;
  184. }
  185. if (s->secret && peer_config->secret[0] == 0)
  186. av_strlcpy(peer_config->secret, s->secret,
  187. RIST_MAX_STRING_SHORT);
  188. if (s->secret && (s->encryption == 128 || s->encryption == 256))
  189. peer_config->key_size = s->encryption;
  190. if (s->buffer_size) {
  191. peer_config->recovery_length_min = s->buffer_size;
  192. peer_config->recovery_length_max = s->buffer_size;
  193. }
  194. ret = rist_peer_create(s->ctx, &s->peer, &s->peer_config);
  195. if (ret < 0) {
  196. blog(LOG_ERROR,
  197. "[obs-ffmpeg mpegts muxer / librist] : failed to create a peer. \n");
  198. goto err;
  199. }
  200. ret = rist_start(s->ctx);
  201. if (ret < 0) {
  202. blog(LOG_ERROR,
  203. "[obs-ffmpeg mpegts muxer / librist] : rist failed to start \n");
  204. goto err;
  205. }
  206. if (rist_stats_callback_set(s->ctx, s->statsinterval, cb_stats,
  207. (void *)s) == -1) {
  208. rist_log(&s->logging_settings, RIST_LOG_ERROR,
  209. "Could not enable stats callback\n");
  210. ;
  211. }
  212. return 0;
  213. err:
  214. librist_close(h);
  215. return OBS_OUTPUT_CONNECT_FAILED;
  216. }
  217. static int librist_write(URLContext *h, const uint8_t *buf, int size)
  218. {
  219. RISTContext *s = h->priv_data;
  220. struct rist_data_block data_block = {0};
  221. int ret;
  222. data_block.ts_ntp = 0;
  223. data_block.payload = buf;
  224. data_block.payload_len = size;
  225. ret = rist_sender_data_write(s->ctx, &data_block);
  226. if (ret < 0) {
  227. blog(LOG_WARNING,
  228. "[obs-ffmpeg mpegts muxer / librist] : failed to send data of size %i bytes",
  229. size);
  230. return risterr2ret(ret);
  231. }
  232. return ret;
  233. }