PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/janus_videocall.c

https://bitbucket.org/abdul-codedesk/janus-gataeway-hub
C | 1549 lines | 1156 code | 52 blank | 341 comment | 319 complexity | 9756de633c7174f32af92769db7b922c MD5 | raw file
Possible License(s): GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /*! \file janus_videocall.c
  2. * \author Lorenzo Miniero <lorenzo@meetecho.com>
  3. * \copyright GNU General Public License v3
  4. * \brief Janus VideoCall plugin
  5. * \details This is a simple video call plugin for Janus, allowing two
  6. * WebRTC peers to call each other through the gateway. The idea is to
  7. * provide a similar service as the well known AppRTC demo (https://apprtc.appspot.com),
  8. * but with the media flowing through the gateway rather than being peer-to-peer.
  9. *
  10. * The plugin provides a simple fake registration mechanism. A peer attaching
  11. * to the plugin needs to specify a username, which acts as a "phone number":
  12. * if the username is free, it is associated with the peer, which means
  13. * he/she can be "called" using that username by another peer. Peers can
  14. * either "call" another peer, by specifying their username, or wait for a call.
  15. * The approach used by this plugin is similar to the one employed by the
  16. * echo test one: all frames (RTP/RTCP) coming from one peer are relayed
  17. * to the other.
  18. *
  19. * Just as in the janus_videocall.c plugin, there are knobs to control
  20. * whether audio and/or video should be muted or not, and if the bitrate
  21. * of the peer needs to be capped by means of REMB messages.
  22. *
  23. * \section vcallapi Video Call API
  24. *
  25. * All requests you can send in the Video Call API are asynchronous,
  26. * which means all responses (successes and errors) will be delivered
  27. * as events with the same transaction.
  28. *
  29. * The supported requests are \c list , \c register , \c call ,
  30. * \c accept , \c set and \c hangup . \c list allows you to get a list
  31. * of all the registered peers; \c register can be used to register
  32. * a username to call and be called; \c call is used to start a video
  33. * call with somebody through the plugin, while \c accept is used to
  34. * accept the call in case one is invited instead of inviting; \c set
  35. * can be used to configure some call-related settings (e.g., a cap on
  36. * the send bandwidth); finally, \c hangup can be used to terminate the
  37. * communication at any time, either to hangup an ongoing call or to
  38. * cancel/decline a call that hasn't started yet.
  39. *
  40. * The \c list request has to be formatted as follows:
  41. *
  42. \verbatim
  43. {
  44. "request" : "list"
  45. }
  46. \endverbatim
  47. *
  48. * A successful request will result in an array of peers to be returned:
  49. *
  50. \verbatim
  51. {
  52. "videocall" : "event",
  53. "result" : {
  54. "list": [ // Array of peers
  55. "alice78",
  56. "bob51",
  57. // others
  58. ]
  59. }
  60. }
  61. \endverbatim
  62. *
  63. * An error instead (and the same applies to all other requests, so this
  64. * won't be repeated) would provide both an error code and a more verbose
  65. * description of the cause of the issue:
  66. *
  67. \verbatim
  68. {
  69. "videocall" : "event",
  70. "error_code" : <numeric ID, check Macros below>,
  71. "error" : "<error description as a string>"
  72. }
  73. \endverbatim
  74. *
  75. * To register a username to call and be called, the \c register request
  76. * can be used. This works on a "first come, first served" basis: there's
  77. * no authetication involved, you just specify the username you'd like
  78. * to use and, if free, it's assigned to you. The \c request has to be
  79. * formatted as follows:
  80. *
  81. \verbatim
  82. {
  83. "request" : "register",
  84. "username" : "<desired unique username>"
  85. }
  86. \endverbatim
  87. *
  88. * If successul, this will result in a \c registered event:
  89. *
  90. \verbatim
  91. {
  92. "videocall" : "event",
  93. "result" : {
  94. "event" : "registered",
  95. "username" : "<same username, registered>"
  96. }
  97. }
  98. \endverbatim
  99. *
  100. * Once you're registered, you can either start a new call or wait to
  101. * be called by someone else who knows your username. To start a new
  102. * call, the \c call request can be used: this request must be attached
  103. * to a JSEP offer containing the WebRTC-related info to setup a new
  104. * media session. A \c call request has to be formatted as follows:
  105. *
  106. \verbatim
  107. {
  108. "request" : "call",
  109. "username" : "<username to call>"
  110. }
  111. \endverbatim
  112. *
  113. * If successul, this will result in a \c calling event:
  114. *
  115. \verbatim
  116. {
  117. "videocall" : "event",
  118. "result" : {
  119. "event" : "calling",
  120. "username" : "<same username, registered>"
  121. }
  122. }
  123. \endverbatim
  124. *
  125. * At the same time, the user being called will receive an
  126. * \c incomingcall event
  127. *
  128. \verbatim
  129. {
  130. "videocall" : "event",
  131. "result" : {
  132. "event" : "incomingcall",
  133. "username" : "<your username>"
  134. }
  135. }
  136. \endverbatim
  137. *
  138. * To accept the call, the \c accept request can be used. This request
  139. * must be attached to a JSEP answer containing the WebRTC-related
  140. * information to complete the actual PeerConnection setup. A \c accept
  141. * request has to be formatted as follows:
  142. *
  143. \verbatim
  144. {
  145. "request" : "accept"
  146. }
  147. \endverbatim
  148. *
  149. * If successul, both the caller and the callee will receive an
  150. * \c accepted event to notify them about the success of the signalling:
  151. *
  152. \verbatim
  153. {
  154. "videocall" : "event",
  155. "result" : {
  156. "event" : "accepted",
  157. "username" : "<caller username>"
  158. }
  159. }
  160. \endverbatim
  161. *
  162. * At this point, the media-related settings of the call can be modified
  163. * on either side by means of a \c set request, which acts pretty much
  164. * as the one in the \ref echoapi . The \c set request has to be
  165. * formatted as follows. All the attributes (except \c request) are
  166. * optional, so any request can contain a subset of them:
  167. *
  168. \verbatim
  169. {
  170. "request" : "set",
  171. "audio" : true|false,
  172. "video" : true|false,
  173. "bitrate" : <numeric bitrate value>,
  174. "record" : true|false,
  175. "filename" : <base path/filename to use for the recording>
  176. }
  177. \endverbatim
  178. *
  179. * \c audio instructs the plugin to do or do not relay audio frames;
  180. * \c video does the same for video; \c bitrate caps the bandwidth to
  181. * force on the browser encoding side (e.g., 128000 for 128kbps);
  182. * \c record enables or disables the recording of this peer; in case
  183. * recording is enabled, \c filename allows to specify a base
  184. * path/filename to use for the files (-audio.mjr and -video.mjr are
  185. * automatically appended). Beware that enabling the recording only
  186. * records this user's contribution, and not the whole call: to record
  187. * both sides, you need to enable recording for both the peers in the
  188. * call.
  189. *
  190. * A successful request will result in a \c set event:
  191. *
  192. \verbatim
  193. {
  194. "videocall" : "event",
  195. "result" : {
  196. "event" : "set"
  197. }
  198. }
  199. \endverbatim
  200. *
  201. * To decline an incoming call, cancel an attempt to call or simply
  202. * hangup an ongoing conversation, the \c hangup request can be used,
  203. * which has to be formatted as follows:
  204. *
  205. \verbatim
  206. {
  207. "request" : "hangup"
  208. }
  209. \endverbatim
  210. *
  211. * Whatever the reason of a call being closed (e.g., a \c hangup request,
  212. * a PeerConnection being closed, or something else), both parties in
  213. * the communication will receive a \c hangup event:
  214. *
  215. \verbatim
  216. {
  217. "videocall" : "event",
  218. "result" : {
  219. "event" : "hangup",
  220. "username" : "<username of who closed the communication>",
  221. "reason" : "<description of what happened>"
  222. }
  223. }
  224. \endverbatim
  225. *
  226. * \ingroup plugins
  227. * \ref plugins
  228. */
  229. #include "plugin.h"
  230. #include <jansson.h>
  231. #include "../debug.h"
  232. #include "../apierror.h"
  233. #include "../config.h"
  234. #include "../mutex.h"
  235. #include "../record.h"
  236. #include "../rtp.h"
  237. #include "../rtcp.h"
  238. #include "../utils.h"
  239. /* Plugin information */
  240. #define JANUS_VIDEOCALL_VERSION 6
  241. #define JANUS_VIDEOCALL_VERSION_STRING "0.0.6"
  242. #define JANUS_VIDEOCALL_DESCRIPTION "This is a simple video call plugin for Janus, allowing two WebRTC peers to call each other through the gateway."
  243. #define JANUS_VIDEOCALL_NAME "JANUS VideoCall plugin"
  244. #define JANUS_VIDEOCALL_AUTHOR "Meetecho s.r.l."
  245. #define JANUS_VIDEOCALL_PACKAGE "janus.plugin.videocall"
  246. /* Plugin methods */
  247. janus_plugin *create(void);
  248. int janus_videocall_init(janus_callbacks *callback, const char *config_path);
  249. void janus_videocall_destroy(void);
  250. int janus_videocall_get_api_compatibility(void);
  251. int janus_videocall_get_version(void);
  252. const char *janus_videocall_get_version_string(void);
  253. const char *janus_videocall_get_description(void);
  254. const char *janus_videocall_get_name(void);
  255. const char *janus_videocall_get_author(void);
  256. const char *janus_videocall_get_package(void);
  257. void janus_videocall_create_session(janus_plugin_session *handle, int *error);
  258. struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
  259. void janus_videocall_setup_media(janus_plugin_session *handle);
  260. void janus_videocall_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len);
  261. void janus_videocall_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len);
  262. void janus_videocall_incoming_data(janus_plugin_session *handle, char *buf, int len);
  263. void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video);
  264. void janus_videocall_hangup_media(janus_plugin_session *handle);
  265. void janus_videocall_destroy_session(janus_plugin_session *handle, int *error);
  266. json_t *janus_videocall_query_session(janus_plugin_session *handle);
  267. /* Plugin setup */
  268. static janus_plugin janus_videocall_plugin =
  269. JANUS_PLUGIN_INIT (
  270. .init = janus_videocall_init,
  271. .destroy = janus_videocall_destroy,
  272. .get_api_compatibility = janus_videocall_get_api_compatibility,
  273. .get_version = janus_videocall_get_version,
  274. .get_version_string = janus_videocall_get_version_string,
  275. .get_description = janus_videocall_get_description,
  276. .get_name = janus_videocall_get_name,
  277. .get_author = janus_videocall_get_author,
  278. .get_package = janus_videocall_get_package,
  279. .create_session = janus_videocall_create_session,
  280. .handle_message = janus_videocall_handle_message,
  281. .setup_media = janus_videocall_setup_media,
  282. .incoming_rtp = janus_videocall_incoming_rtp,
  283. .incoming_rtcp = janus_videocall_incoming_rtcp,
  284. .incoming_data = janus_videocall_incoming_data,
  285. .slow_link = janus_videocall_slow_link,
  286. .hangup_media = janus_videocall_hangup_media,
  287. .destroy_session = janus_videocall_destroy_session,
  288. .query_session = janus_videocall_query_session,
  289. );
  290. /* Plugin creator */
  291. janus_plugin *create(void) {
  292. JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_VIDEOCALL_NAME);
  293. return &janus_videocall_plugin;
  294. }
  295. /* Parameter validation */
  296. static struct janus_json_parameter request_parameters[] = {
  297. {"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
  298. };
  299. static struct janus_json_parameter username_parameters[] = {
  300. {"username", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
  301. };
  302. static struct janus_json_parameter set_parameters[] = {
  303. {"audio", JANUS_JSON_BOOL, 0},
  304. {"video", JANUS_JSON_BOOL, 0},
  305. {"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
  306. {"record", JANUS_JSON_BOOL, 0},
  307. {"filename", JSON_STRING, 0}
  308. };
  309. /* Useful stuff */
  310. static volatile gint initialized = 0, stopping = 0;
  311. static gboolean notify_events = TRUE;
  312. static janus_callbacks *gateway = NULL;
  313. static GThread *handler_thread;
  314. static GThread *watchdog;
  315. static void *janus_videocall_handler(void *data);
  316. typedef struct janus_videocall_message {
  317. janus_plugin_session *handle;
  318. char *transaction;
  319. json_t *message;
  320. json_t *jsep;
  321. } janus_videocall_message;
  322. static GAsyncQueue *messages = NULL;
  323. static janus_videocall_message exit_message;
  324. static void janus_videocall_message_free(janus_videocall_message *msg) {
  325. if(!msg || msg == &exit_message)
  326. return;
  327. msg->handle = NULL;
  328. g_free(msg->transaction);
  329. msg->transaction = NULL;
  330. if(msg->message)
  331. json_decref(msg->message);
  332. msg->message = NULL;
  333. if(msg->jsep)
  334. json_decref(msg->jsep);
  335. msg->jsep = NULL;
  336. g_free(msg);
  337. }
  338. typedef struct janus_videocall_session {
  339. janus_plugin_session *handle;
  340. gchar *username;
  341. gboolean has_audio;
  342. gboolean has_video;
  343. gboolean has_data;
  344. gboolean audio_active;
  345. gboolean video_active;
  346. uint32_t bitrate;
  347. guint16 slowlink_count;
  348. struct janus_videocall_session *peer;
  349. janus_rtp_switching_context context;
  350. uint32_t ssrc[3]; /* Only needed in case VP8 simulcasting is involved */
  351. int rtpmapid_extmap_id; /* Only needed in case Firefox's RID-based simulcasting is involved */
  352. char *rid[3]; /* Only needed in case Firefox's RID-based simulcasting is involved */
  353. int substream; /* Which simulcast substream we should forward back */
  354. int substream_target; /* As above, but to handle transitions (e.g., wait for keyframe) */
  355. int templayer; /* Which simulcast temporal layer we should forward back */
  356. int templayer_target; /* As above, but to handle transitions (e.g., wait for keyframe) */
  357. gint64 last_relayed; /* When we relayed the last packet (used to detect when substreams become unavailable) */
  358. janus_vp8_simulcast_context simulcast_context;
  359. janus_recorder *arc; /* The Janus recorder instance for this user's audio, if enabled */
  360. janus_recorder *vrc; /* The Janus recorder instance for this user's video, if enabled */
  361. janus_recorder *drc; /* The Janus recorder instance for this user's data, if enabled */
  362. janus_mutex rec_mutex; /* Mutex to protect the recorders from race conditions */
  363. volatile gint hangingup;
  364. gint64 destroyed; /* Time at which this session was marked as destroyed */
  365. } janus_videocall_session;
  366. static GHashTable *sessions;
  367. static GList *old_sessions;
  368. static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
  369. /* Error codes */
  370. #define JANUS_VIDEOCALL_ERROR_UNKNOWN_ERROR 499
  371. #define JANUS_VIDEOCALL_ERROR_NO_MESSAGE 470
  372. #define JANUS_VIDEOCALL_ERROR_INVALID_JSON 471
  373. #define JANUS_VIDEOCALL_ERROR_INVALID_REQUEST 472
  374. #define JANUS_VIDEOCALL_ERROR_REGISTER_FIRST 473
  375. #define JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT 474
  376. #define JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT 475
  377. #define JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN 476
  378. #define JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED 477
  379. #define JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME 478
  380. #define JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST 479
  381. #define JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL 480
  382. #define JANUS_VIDEOCALL_ERROR_NO_CALL 481
  383. #define JANUS_VIDEOCALL_ERROR_MISSING_SDP 482
  384. /* VideoCall watchdog/garbage collector (sort of) */
  385. static void *janus_videocall_watchdog(void *data) {
  386. JANUS_LOG(LOG_INFO, "VideoCall watchdog started\n");
  387. gint64 now = 0;
  388. while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
  389. janus_mutex_lock(&sessions_mutex);
  390. /* Iterate on all the sessions */
  391. now = janus_get_monotonic_time();
  392. if(old_sessions != NULL) {
  393. GList *sl = old_sessions;
  394. JANUS_LOG(LOG_HUGE, "Checking %d old VideoCall sessions...\n", g_list_length(old_sessions));
  395. while(sl) {
  396. janus_videocall_session *session = (janus_videocall_session *)sl->data;
  397. if(!session) {
  398. sl = sl->next;
  399. continue;
  400. }
  401. if(now-session->destroyed >= 5*G_USEC_PER_SEC) {
  402. /* We're lazy and actually get rid of the stuff only after a few seconds */
  403. JANUS_LOG(LOG_VERB, "Freeing old VideoCall session\n");
  404. GList *rm = sl->next;
  405. old_sessions = g_list_delete_link(old_sessions, sl);
  406. sl = rm;
  407. session->handle = NULL;
  408. g_free(session);
  409. session = NULL;
  410. continue;
  411. }
  412. sl = sl->next;
  413. }
  414. }
  415. janus_mutex_unlock(&sessions_mutex);
  416. g_usleep(500000);
  417. }
  418. JANUS_LOG(LOG_INFO, "VideoCall watchdog stopped\n");
  419. return NULL;
  420. }
  421. /* Plugin implementation */
  422. int janus_videocall_init(janus_callbacks *callback, const char *config_path) {
  423. if(g_atomic_int_get(&stopping)) {
  424. /* Still stopping from before */
  425. return -1;
  426. }
  427. if(callback == NULL || config_path == NULL) {
  428. /* Invalid arguments */
  429. return -1;
  430. }
  431. /* Read configuration */
  432. char filename[255];
  433. g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE);
  434. JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
  435. janus_config *config = janus_config_parse(filename);
  436. if(config != NULL) {
  437. janus_config_print(config);
  438. janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
  439. if(events != NULL && events->value != NULL)
  440. notify_events = janus_is_true(events->value);
  441. if(!notify_events && callback->events_is_enabled()) {
  442. JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOCALL_NAME);
  443. }
  444. }
  445. janus_config_destroy(config);
  446. config = NULL;
  447. sessions = g_hash_table_new(g_str_hash, g_str_equal);
  448. messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free);
  449. /* This is the callback we'll need to invoke to contact the gateway */
  450. gateway = callback;
  451. g_atomic_int_set(&initialized, 1);
  452. GError *error = NULL;
  453. /* Start the sessions watchdog */
  454. watchdog = g_thread_try_new("videocall watchdog", &janus_videocall_watchdog, NULL, &error);
  455. if(error != NULL) {
  456. g_atomic_int_set(&initialized, 0);
  457. JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall watchdog thread...\n", error->code, error->message ? error->message : "??");
  458. return -1;
  459. }
  460. /* Launch the thread that will handle incoming messages */
  461. handler_thread = g_thread_try_new("videocall handler", janus_videocall_handler, NULL, &error);
  462. if(error != NULL) {
  463. g_atomic_int_set(&initialized, 0);
  464. JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall handler thread...\n", error->code, error->message ? error->message : "??");
  465. return -1;
  466. }
  467. JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME);
  468. return 0;
  469. }
  470. void janus_videocall_destroy(void) {
  471. if(!g_atomic_int_get(&initialized))
  472. return;
  473. g_atomic_int_set(&stopping, 1);
  474. g_async_queue_push(messages, &exit_message);
  475. if(handler_thread != NULL) {
  476. g_thread_join(handler_thread);
  477. handler_thread = NULL;
  478. }
  479. if(watchdog != NULL) {
  480. g_thread_join(watchdog);
  481. watchdog = NULL;
  482. }
  483. /* FIXME We should destroy the sessions cleanly */
  484. janus_mutex_lock(&sessions_mutex);
  485. g_hash_table_destroy(sessions);
  486. janus_mutex_unlock(&sessions_mutex);
  487. g_async_queue_unref(messages);
  488. messages = NULL;
  489. sessions = NULL;
  490. g_atomic_int_set(&initialized, 0);
  491. g_atomic_int_set(&stopping, 0);
  492. JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_VIDEOCALL_NAME);
  493. }
  494. int janus_videocall_get_api_compatibility(void) {
  495. /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
  496. return JANUS_PLUGIN_API_VERSION;
  497. }
  498. int janus_videocall_get_version(void) {
  499. return JANUS_VIDEOCALL_VERSION;
  500. }
  501. const char *janus_videocall_get_version_string(void) {
  502. return JANUS_VIDEOCALL_VERSION_STRING;
  503. }
  504. const char *janus_videocall_get_description(void) {
  505. return JANUS_VIDEOCALL_DESCRIPTION;
  506. }
  507. const char *janus_videocall_get_name(void) {
  508. return JANUS_VIDEOCALL_NAME;
  509. }
  510. const char *janus_videocall_get_author(void) {
  511. return JANUS_VIDEOCALL_AUTHOR;
  512. }
  513. const char *janus_videocall_get_package(void) {
  514. return JANUS_VIDEOCALL_PACKAGE;
  515. }
  516. void janus_videocall_create_session(janus_plugin_session *handle, int *error) {
  517. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
  518. *error = -1;
  519. return;
  520. }
  521. janus_videocall_session *session = (janus_videocall_session *)g_malloc0(sizeof(janus_videocall_session));
  522. session->handle = handle;
  523. session->has_audio = FALSE;
  524. session->has_video = FALSE;
  525. session->has_data = FALSE;
  526. session->audio_active = TRUE;
  527. session->video_active = TRUE;
  528. session->bitrate = 0; /* No limit */
  529. session->peer = NULL;
  530. session->username = NULL;
  531. janus_rtp_switching_context_reset(&session->context);
  532. session->ssrc[0] = 0;
  533. session->ssrc[1] = 0;
  534. session->ssrc[2] = 0;
  535. session->substream = -1;
  536. session->substream_target = 0;
  537. session->templayer = -1;
  538. session->templayer_target = 0;
  539. session->last_relayed = 0;
  540. janus_vp8_simulcast_context_reset(&session->simulcast_context);
  541. janus_mutex_init(&session->rec_mutex);
  542. session->destroyed = 0;
  543. g_atomic_int_set(&session->hangingup, 0);
  544. handle->plugin_handle = session;
  545. return;
  546. }
  547. void janus_videocall_destroy_session(janus_plugin_session *handle, int *error) {
  548. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
  549. *error = -1;
  550. return;
  551. }
  552. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  553. if(!session) {
  554. JANUS_LOG(LOG_ERR, "No VideoCall session associated with this handle...\n");
  555. *error = -2;
  556. return;
  557. }
  558. janus_mutex_lock(&sessions_mutex);
  559. if(!session->destroyed) {
  560. JANUS_LOG(LOG_VERB, "Removing VideoCall user %s session...\n", session->username ? session->username : "'unknown'");
  561. janus_videocall_hangup_media(handle);
  562. session->destroyed = janus_get_monotonic_time();
  563. if(session->username != NULL) {
  564. int res = g_hash_table_remove(sessions, (gpointer)session->username);
  565. JANUS_LOG(LOG_VERB, " -- Removed: %d\n", res);
  566. }
  567. /* Cleaning up and removing the session is done in a lazy way */
  568. old_sessions = g_list_append(old_sessions, session);
  569. }
  570. janus_mutex_unlock(&sessions_mutex);
  571. return;
  572. }
  573. json_t *janus_videocall_query_session(janus_plugin_session *handle) {
  574. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
  575. return NULL;
  576. }
  577. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  578. if(!session) {
  579. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  580. return NULL;
  581. }
  582. /* Provide some generic info, e.g., if we're in a call and with whom */
  583. json_t *info = json_object();
  584. json_object_set_new(info, "state", json_string(session->peer ? "incall" : "idle"));
  585. json_object_set_new(info, "username", session->username ? json_string(session->username) : NULL);
  586. if(session->peer) {
  587. json_object_set_new(info, "peer", session->peer->username ? json_string(session->peer->username) : NULL);
  588. json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
  589. json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
  590. json_object_set_new(info, "bitrate", json_integer(session->bitrate));
  591. json_object_set_new(info, "slowlink_count", json_integer(session->slowlink_count));
  592. }
  593. if(session->ssrc[0] != 0) {
  594. json_object_set_new(info, "simulcast", json_true());
  595. }
  596. if(session->peer && session->peer->ssrc[0] != 0) {
  597. json_object_set_new(info, "simulcast-peer", json_true());
  598. json_object_set_new(info, "substream", json_integer(session->substream));
  599. json_object_set_new(info, "substream-target", json_integer(session->substream_target));
  600. json_object_set_new(info, "temporal-layer", json_integer(session->templayer));
  601. json_object_set_new(info, "temporal-layer-target", json_integer(session->templayer_target));
  602. }
  603. if(session->arc || session->vrc || session->drc) {
  604. json_t *recording = json_object();
  605. if(session->arc && session->arc->filename)
  606. json_object_set_new(recording, "audio", json_string(session->arc->filename));
  607. if(session->vrc && session->vrc->filename)
  608. json_object_set_new(recording, "video", json_string(session->vrc->filename));
  609. if(session->drc && session->drc->filename)
  610. json_object_set_new(recording, "data", json_string(session->drc->filename));
  611. json_object_set_new(info, "recording", recording);
  612. }
  613. json_object_set_new(info, "destroyed", json_integer(session->destroyed));
  614. return info;
  615. }
  616. struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
  617. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  618. return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
  619. janus_videocall_message *msg = g_malloc0(sizeof(janus_videocall_message));
  620. msg->handle = handle;
  621. msg->transaction = transaction;
  622. msg->message = message;
  623. msg->jsep = jsep;
  624. g_async_queue_push(messages, msg);
  625. /* All the requests to this plugin are handled asynchronously */
  626. return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
  627. }
  628. void janus_videocall_setup_media(janus_plugin_session *handle) {
  629. JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
  630. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  631. return;
  632. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  633. if(!session) {
  634. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  635. return;
  636. }
  637. if(session->destroyed)
  638. return;
  639. g_atomic_int_set(&session->hangingup, 0);
  640. /* We really don't care, as we only relay RTP/RTCP we get in the first place anyway */
  641. }
  642. void janus_videocall_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) {
  643. if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  644. return;
  645. if(gateway) {
  646. /* Honour the audio/video active flags */
  647. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  648. if(!session) {
  649. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  650. return;
  651. }
  652. if(!session->peer) {
  653. JANUS_LOG(LOG_ERR, "Session has no peer...\n");
  654. return;
  655. }
  656. if(session->destroyed || session->peer->destroyed)
  657. return;
  658. if(video && session->video_active && session->rtpmapid_extmap_id != -1) {
  659. /* FIXME Just a way to debug Firefox simulcasting */
  660. janus_rtp_header *header = (janus_rtp_header *)buf;
  661. uint32_t seq_number = ntohs(header->seq_number);
  662. uint32_t timestamp = ntohl(header->timestamp);
  663. uint32_t ssrc = ntohl(header->ssrc);
  664. char sdes_item[16];
  665. if(janus_rtp_header_extension_parse_rtp_stream_id(buf, len, session->rtpmapid_extmap_id, sdes_item, sizeof(sdes_item)) == 0) {
  666. JANUS_LOG(LOG_DBG, "%"SCNu32"/%"SCNu16"/%"SCNu32"/%d: RTP stream ID extension: %s\n",
  667. ssrc, seq_number, timestamp, header->padding, sdes_item);
  668. }
  669. }
  670. if(video && session->video_active && session->ssrc[0] != 0) {
  671. /* Handle simulcast: don't relay if it's not the SSRC we wanted to handle */
  672. janus_rtp_header *header = (janus_rtp_header *)buf;
  673. uint32_t seq_number = ntohs(header->seq_number);
  674. uint32_t timestamp = ntohl(header->timestamp);
  675. uint32_t ssrc = ntohl(header->ssrc);
  676. /* Access the packet payload */
  677. int plen = 0;
  678. char *payload = janus_rtp_payload(buf, len, &plen);
  679. if(payload == NULL)
  680. return;
  681. gboolean switched = FALSE;
  682. if(session->peer->substream != session->peer->substream_target) {
  683. /* There has been a change: let's wait for a keyframe on the target */
  684. int step = (session->peer->substream < 1 && session->peer->substream_target == 2);
  685. if((ssrc == session->ssrc[session->peer->substream_target]) || (step && ssrc == session->ssrc[step])) {
  686. //~ if(janus_vp8_is_keyframe(payload, plen)) {
  687. uint32_t ssrc_old = 0;
  688. if(session->peer->substream != -1)
  689. ssrc_old = session->ssrc[session->peer->substream];
  690. JANUS_LOG(LOG_VERB, "Received keyframe on SSRC %"SCNu32", switching (was %"SCNu32")\n", ssrc, ssrc_old);
  691. session->peer->substream = (ssrc == session->ssrc[session->peer->substream_target] ? session->peer->substream_target : step);
  692. switched = TRUE;
  693. /* Notify the peer */
  694. json_t *event = json_object();
  695. json_object_set_new(event, "videocall", json_string("event"));
  696. json_t *result = json_object();
  697. json_object_set_new(result, "event", json_string("simulcast"));
  698. json_object_set_new(result, "substream", json_integer(session->peer->substream));
  699. json_object_set_new(event, "result", result);
  700. gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, event, NULL);
  701. json_decref(event);
  702. //~ } else {
  703. //~ JANUS_LOG(LOG_WARN, "Not a keyframe on SSRC %"SCNu32" yet, waiting before switching\n", ssrc);
  704. //~ }
  705. }
  706. }
  707. /* If we haven't received our desired substream yet, let's drop temporarily */
  708. if(session->last_relayed == 0) {
  709. /* Let's start slow */
  710. session->last_relayed = janus_get_monotonic_time();
  711. } else {
  712. /* Check if 250ms went by with no packet relayed */
  713. gint64 now = janus_get_monotonic_time();
  714. if(now-session->last_relayed >= 250000) {
  715. session->last_relayed = now;
  716. int substream = session->peer->substream-1;
  717. if(substream < 0)
  718. substream = 0;
  719. if(session->peer->substream != substream) {
  720. JANUS_LOG(LOG_WARN, "No packet received on substream %d for a while, falling back to %d\n",
  721. session->peer->substream, substream);
  722. session->peer->substream = substream;
  723. /* Send a PLI to the user */
  724. JANUS_LOG(LOG_VERB, "Just (re-)enabled video, sending a PLI to recover it\n");
  725. char rtcpbuf[12];
  726. memset(rtcpbuf, 0, 12);
  727. janus_rtcp_pli((char *)&rtcpbuf, 12);
  728. gateway->relay_rtcp(handle, 1, rtcpbuf, 12);
  729. /* Notify the peer */
  730. json_t *event = json_object();
  731. json_object_set_new(event, "videocall", json_string("event"));
  732. json_t *result = json_object();
  733. json_object_set_new(result, "event", json_string("simulcast"));
  734. json_object_set_new(result, "substream", json_integer(session->peer->substream));
  735. json_object_set_new(event, "result", result);
  736. gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, event, NULL);
  737. json_decref(event);
  738. }
  739. }
  740. }
  741. /* Do we need to drop this? */
  742. if(ssrc != session->ssrc[session->peer->substream]) {
  743. JANUS_LOG(LOG_HUGE, "Dropping packet (it's from SSRC %"SCNu32", but we're only relaying to the peer the SSRC %"SCNu32" now\n",
  744. ssrc, session->ssrc[session->peer->substream]);
  745. return;
  746. }
  747. session->last_relayed = janus_get_monotonic_time();
  748. /* Check if there's any temporal scalability to take into account */
  749. uint16_t picid = 0;
  750. uint8_t tlzi = 0;
  751. uint8_t tid = 0;
  752. uint8_t ybit = 0;
  753. uint8_t keyidx = 0;
  754. if(janus_vp8_parse_descriptor(payload, plen, &picid, &tlzi, &tid, &ybit, &keyidx) == 0) {
  755. //~ JANUS_LOG(LOG_WARN, "%"SCNu16", %u, %u, %u, %u\n", picid, tlzi, tid, ybit, keyidx);
  756. if(session->peer->templayer != session->peer->templayer_target) {
  757. /* FIXME We should be smarter in deciding when to switch */
  758. session->peer->templayer = session->peer->templayer_target;
  759. /* Notify the peer */
  760. json_t *event = json_object();
  761. json_object_set_new(event, "videocall", json_string("event"));
  762. json_t *result = json_object();
  763. json_object_set_new(result, "event", json_string("simulcast"));
  764. json_object_set_new(result, "temporal", json_integer(session->peer->templayer));
  765. json_object_set_new(event, "result", result);
  766. gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, event, NULL);
  767. json_decref(event);
  768. }
  769. if(tid > session->peer->templayer) {
  770. JANUS_LOG(LOG_HUGE, "Dropping packet (it's temporal layer %d, but we're capping at %d)\n",
  771. tid, session->peer->templayer);
  772. /* We increase the base sequence number, or there will be gaps when delivering later */
  773. session->peer->context.v_base_seq++;
  774. return;
  775. }
  776. }
  777. /* If we got here, update the RTP header and send the packet */
  778. janus_rtp_header_update(header, &session->peer->context, TRUE, 4500);
  779. janus_vp8_simulcast_descriptor_update(payload, plen, &session->peer->simulcast_context, switched);
  780. /* Save the frame if we're recording */
  781. janus_recorder_save_frame(session->vrc, buf, len);
  782. /* Send the frame back */
  783. gateway->relay_rtp(session->peer->handle, video, buf, len);
  784. /* Restore header or core statistics will be messed up */
  785. header->timestamp = htonl(timestamp);
  786. header->seq_number = htons(seq_number);
  787. } else {
  788. if((!video && session->audio_active) || (video && session->video_active)) {
  789. /* Save the frame if we're recording */
  790. janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len);
  791. /* Forward the packet to the peer */
  792. gateway->relay_rtp(session->peer->handle, video, buf, len);
  793. }
  794. }
  795. }
  796. }
  797. void janus_videocall_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) {
  798. if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  799. return;
  800. if(gateway) {
  801. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  802. if(!session) {
  803. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  804. return;
  805. }
  806. if(!session->peer) {
  807. JANUS_LOG(LOG_ERR, "Session has no peer...\n");
  808. return;
  809. }
  810. if(session->destroyed || session->peer->destroyed)
  811. return;
  812. guint32 bitrate = janus_rtcp_get_remb(buf, len);
  813. if(bitrate > 0) {
  814. /* If a REMB arrived, make sure we cap it to our configuration, and send it as a video RTCP */
  815. if(session->bitrate > 0)
  816. janus_rtcp_cap_remb(buf, len, session->bitrate);
  817. gateway->relay_rtcp(session->peer->handle, 1, buf, len);
  818. return;
  819. }
  820. gateway->relay_rtcp(session->peer->handle, video, buf, len);
  821. }
  822. }
  823. void janus_videocall_incoming_data(janus_plugin_session *handle, char *buf, int len) {
  824. if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  825. return;
  826. if(gateway) {
  827. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  828. if(!session) {
  829. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  830. return;
  831. }
  832. if(!session->peer) {
  833. JANUS_LOG(LOG_ERR, "Session has no peer...\n");
  834. return;
  835. }
  836. if(session->destroyed || session->peer->destroyed)
  837. return;
  838. if(buf == NULL || len <= 0)
  839. return;
  840. char *text = g_malloc0(len+1);
  841. memcpy(text, buf, len);
  842. *(text+len) = '\0';
  843. JANUS_LOG(LOG_VERB, "Got a DataChannel message (%zu bytes) to forward: %s\n", strlen(text), text);
  844. gateway->relay_data(session->peer->handle, text, strlen(text));
  845. g_free(text);
  846. }
  847. }
  848. void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video) {
  849. /* The core is informing us that our peer got or sent too many NACKs, are we pushing media too hard? */
  850. if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  851. return;
  852. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  853. if(!session) {
  854. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  855. return;
  856. }
  857. if(session->destroyed)
  858. return;
  859. session->slowlink_count++;
  860. if(uplink && !video && !session->audio_active) {
  861. /* We're not relaying audio and the peer is expecting it, so NACKs are normal */
  862. JANUS_LOG(LOG_VERB, "Getting a lot of NACKs (slow uplink) for audio, but that's expected, a configure disabled the audio forwarding\n");
  863. } else if(uplink && video && !session->video_active) {
  864. /* We're not relaying video and the peer is expecting it, so NACKs are normal */
  865. JANUS_LOG(LOG_VERB, "Getting a lot of NACKs (slow uplink) for video, but that's expected, a configure disabled the video forwarding\n");
  866. } else {
  867. /* Slow uplink or downlink, maybe we set the bitrate cap too high? */
  868. if(video) {
  869. /* Halve the bitrate, but don't go too low... */
  870. if(!uplink) {
  871. /* Downlink issue, user has trouble sending, halve this user's bitrate cap */
  872. session->bitrate = session->bitrate > 0 ? session->bitrate : 512*1024;
  873. session->bitrate = session->bitrate/2;
  874. if(session->bitrate < 64*1024)
  875. session->bitrate = 64*1024;
  876. } else {
  877. /* Uplink issue, user has trouble receiving, halve this user's peer's bitrate cap */
  878. if(session->peer == NULL || session->peer->handle == NULL)
  879. return; /* Nothing to do */
  880. session->peer->bitrate = session->peer->bitrate > 0 ? session->peer->bitrate : 512*1024;
  881. session->peer->bitrate = session->peer->bitrate/2;
  882. if(session->peer->bitrate < 64*1024)
  883. session->peer->bitrate = 64*1024;
  884. }
  885. JANUS_LOG(LOG_WARN, "Getting a lot of NACKs (slow %s) for %s, forcing a lower REMB: %"SCNu32"\n",
  886. uplink ? "uplink" : "downlink", video ? "video" : "audio", uplink ? session->peer->bitrate : session->bitrate);
  887. /* ... and send a new REMB back */
  888. char rtcpbuf[24];
  889. janus_rtcp_remb((char *)(&rtcpbuf), 24, uplink ? session->peer->bitrate : session->bitrate);
  890. gateway->relay_rtcp(uplink ? session->peer->handle : handle, 1, rtcpbuf, 24);
  891. /* As a last thing, notify the affected user about this */
  892. json_t *event = json_object();
  893. json_object_set_new(event, "videocall", json_string("event"));
  894. json_t *result = json_object();
  895. json_object_set_new(result, "status", json_string("slow_link"));
  896. json_object_set_new(result, "bitrate", json_integer(uplink ? session->peer->bitrate : session->bitrate));
  897. json_object_set_new(event, "result", result);
  898. gateway->push_event(uplink ? session->peer->handle : handle, &janus_videocall_plugin, NULL, event, NULL);
  899. json_decref(event);
  900. }
  901. }
  902. }
  903. void janus_videocall_hangup_media(janus_plugin_session *handle) {
  904. JANUS_LOG(LOG_INFO, "No WebRTC media anymore\n");
  905. if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
  906. return;
  907. janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
  908. if(!session) {
  909. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  910. return;
  911. }
  912. if(session->destroyed)
  913. return;
  914. if(g_atomic_int_add(&session->hangingup, 1))
  915. return;
  916. /* Get rid of the recorders, if available */
  917. janus_mutex_lock(&session->rec_mutex);
  918. if(session->arc) {
  919. janus_recorder_close(session->arc);
  920. JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", session->arc->filename ? session->arc->filename : "??");
  921. janus_recorder_free(session->arc);
  922. }
  923. session->arc = NULL;
  924. if(session->vrc) {
  925. janus_recorder_close(session->vrc);
  926. JANUS_LOG(LOG_INFO, "Closed video recording %s\n", session->vrc->filename ? session->vrc->filename : "??");
  927. janus_recorder_free(session->vrc);
  928. }
  929. session->vrc = NULL;
  930. if(session->drc) {
  931. janus_recorder_close(session->drc);
  932. JANUS_LOG(LOG_INFO, "Closed data recording %s\n", session->drc->filename ? session->drc->filename : "??");
  933. janus_recorder_free(session->drc);
  934. }
  935. session->drc = NULL;
  936. janus_mutex_unlock(&session->rec_mutex);
  937. if(session->peer) {
  938. /* Send event to our peer too */
  939. json_t *call = json_object();
  940. json_object_set_new(call, "videocall", json_string("event"));
  941. json_t *calling = json_object();
  942. json_object_set_new(calling, "event", json_string("hangup"));
  943. json_object_set_new(calling, "username", json_string(session->username));
  944. json_object_set_new(calling, "reason", json_string("Remote WebRTC hangup"));
  945. json_object_set_new(call, "result", calling);
  946. gateway->close_pc(session->peer->handle);
  947. int ret = gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, call, NULL);
  948. JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
  949. json_decref(call);
  950. /* Also notify event handlers */
  951. if(notify_events && gateway->events_is_enabled()) {
  952. json_t *info = json_object();
  953. json_object_set_new(info, "event", json_string("hangup"));
  954. json_object_set_new(info, "reason", json_string("Remote WebRTC hangup"));
  955. gateway->notify_event(&janus_videocall_plugin, session->peer->handle, info);
  956. }
  957. }
  958. session->peer = NULL;
  959. /* Reset controls */
  960. session->has_audio = FALSE;
  961. session->has_video = FALSE;
  962. session->has_data = FALSE;
  963. session->audio_active = TRUE;
  964. session->video_active = TRUE;
  965. session->bitrate = 0;
  966. janus_rtp_switching_context_reset(&session->context);
  967. }
  968. /* Thread to handle incoming messages */
  969. static void *janus_videocall_handler(void *data) {
  970. JANUS_LOG(LOG_VERB, "Joining VideoCall handler thread\n");
  971. janus_videocall_message *msg = NULL;
  972. int error_code = 0;
  973. char error_cause[512];
  974. json_t *root = NULL;
  975. while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
  976. msg = g_async_queue_pop(messages);
  977. if(msg == NULL)
  978. continue;
  979. if(msg == &exit_message)
  980. break;
  981. if(msg->handle == NULL) {
  982. janus_videocall_message_free(msg);
  983. continue;
  984. }
  985. janus_mutex_lock(&sessions_mutex);
  986. janus_videocall_session *session = (janus_videocall_session *)msg->handle->plugin_handle;
  987. if(!session) {
  988. JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
  989. janus_mutex_unlock(&sessions_mutex);
  990. janus_videocall_message_free(msg);
  991. continue;
  992. }
  993. if(session->destroyed) {
  994. janus_mutex_unlock(&sessions_mutex);
  995. janus_videocall_message_free(msg);
  996. continue;
  997. }
  998. janus_mutex_unlock(&sessions_mutex);
  999. /* Handle request */
  1000. error_code = 0;
  1001. root = msg->message;
  1002. if(msg->message == NULL) {
  1003. JANUS_LOG(LOG_ERR, "No message??\n");
  1004. error_code = JANUS_VIDEOCALL_ERROR_NO_MESSAGE;
  1005. g_snprintf(error_cause, 512, "%s", "No message??");
  1006. goto error;
  1007. }
  1008. if(!json_is_object(root)) {
  1009. JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
  1010. error_code = JANUS_VIDEOCALL_ERROR_INVALID_JSON;
  1011. g_snprintf(error_cause, 512, "JSON error: not an object");
  1012. goto error;
  1013. }
  1014. JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
  1015. error_code, error_cause, TRUE,
  1016. JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
  1017. if(error_code != 0)
  1018. goto error;
  1019. const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type"));
  1020. const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp"));
  1021. json_t *request = json_object_get(root, "request");
  1022. const char *request_text = json_string_value(request);
  1023. json_t *result = NULL;
  1024. if(!strcasecmp(request_text, "list")) {
  1025. result = json_object();
  1026. json_t *list = json_array();
  1027. JANUS_LOG(LOG_VERB, "Request for the list of peers\n");
  1028. /* Return a list of all available mountpoints */
  1029. janus_mutex_lock(&sessions_mutex);
  1030. GHashTableIter iter;
  1031. gpointer value;
  1032. g_hash_table_iter_init(&iter, sessions);
  1033. while (g_hash_table_iter_next(&iter, NULL, &value)) {
  1034. janus_videocall_session *user = value;
  1035. if(user != NULL && user->username != NULL)
  1036. json_array_append_new(list, json_string(user->username));
  1037. }
  1038. json_object_set_new(result, "list", list);
  1039. janus_mutex_unlock(&sessions_mutex);
  1040. } else if(!strcasecmp(request_text, "register")) {
  1041. /* Map this handle to a username */
  1042. if(session->username != NULL) {
  1043. JANUS_LOG(LOG_ERR, "Already registered (%s)\n", session->username);
  1044. error_code = JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED;
  1045. g_snprintf(error_cause, 512, "Already registered (%s)", session->username);
  1046. goto error;
  1047. }
  1048. JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
  1049. error_code, error_cause, TRUE,
  1050. JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
  1051. if(error_code != 0)
  1052. goto error;
  1053. json_t *username = json_object_get(root, "username");
  1054. const char *username_text = json_string_value(username);
  1055. janus_mutex_lock(&sessions_mutex);
  1056. if(g_hash_table_lookup(sessions, username_text) != NULL) {
  1057. janus_mutex_unlock(&sessions_mutex);
  1058. JANUS_LOG(LOG_ERR, "Username '%s' already taken\n", username_text);
  1059. error_code = JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN;
  1060. g_snprintf(error_cause, 512, "Username '%s' already taken", username_text);
  1061. goto error;
  1062. }
  1063. janus_mutex_unlock(&sessions_mutex);
  1064. session->username = g_strdup(username_text);
  1065. janus_mutex_lock(&sessions_mutex);
  1066. g_hash_table_insert(sessions, (gpointer)session->username, session);
  1067. janus_mutex_unlock(&sessions_mutex);
  1068. result = json_object();
  1069. json_object_set_new(result, "event", json_string("registered"));
  1070. json_object_set_new(result, "username", json_string(username_text));
  1071. /* Also notify event handlers */
  1072. if(notify_events && gateway->events_is_enabled()) {
  1073. json_t *info = json_object();
  1074. json_object_set_new(info, "event", json_string("registered"));
  1075. json_object_set_new(info, "username", json_string(username_text));
  1076. gateway->notify_event(&janus_videocall_plugin, session->handle, info);
  1077. }
  1078. } else if(!strcasecmp(request_text, "call")) {
  1079. /* Call another peer */
  1080. if(session->username == NULL) {
  1081. JANUS_LOG(LOG_ERR, "Register a username first\n");
  1082. error_code = JANUS_VIDEOCALL_ERROR_REGISTER_FIRST;
  1083. g_snprintf(error_cause, 512, "Register a username first");
  1084. goto error;
  1085. }
  1086. if(session->peer != NULL) {
  1087. JANUS_LOG(LOG_ERR, "Already in a call\n");
  1088. error_code = JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL;
  1089. g_snprintf(error_cause, 512, "Already in a call");
  1090. goto error;
  1091. }
  1092. JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
  1093. error_code, error_cause, TRUE,
  1094. JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
  1095. if(error_code != 0)
  1096. goto error;
  1097. json_t *username = json_object_get(root, "username");
  1098. const char *username_text = json_string_value(username);
  1099. if(!strcmp(username_text, session->username)) {
  1100. JANUS_LOG(LOG_ERR, "You can't call yourself... use the EchoTest for that\n");
  1101. error_code = JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST;
  1102. g_snprintf(error_cause, 512, "You can't call yourself... use the EchoTest for that");
  1103. goto error;
  1104. }
  1105. janus_mutex_lock(&sessions_mutex);
  1106. janus_videocall_session *peer = g_hash_table_lookup(sessions, username_text);
  1107. if(peer == NULL || peer->destroyed) {
  1108. janus_mutex_unlock(&sessions_mutex);
  1109. JANUS_LOG(LOG_ERR, "Username '%s' doesn't exist\n", username_text);
  1110. error_code = JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME;
  1111. g_snprintf(error_cause, 512, "Username '%s' doesn't exist", username_text);
  1112. goto error;
  1113. }
  1114. if(peer->peer != NULL) {
  1115. janus_mutex_unlock(&sessions_mutex);
  1116. JANUS_LOG(LOG_VERB, "%s is busy\n", username_text);
  1117. result = json_object();
  1118. json_object_set_new(result, "event", json_string("hangup"));
  1119. json_object_set_new(result, "username", json_string(session->username));
  1120. json_object_set_new(result, "reason", json_string("User busy"));
  1121. /* Also notify event handlers */
  1122. if(notify_events && gateway->events_is_enabled()) {
  1123. json_t *info = json_object();
  1124. json_object_set_new(info, "event", json_string("hangup"));
  1125. json_object_set_new(info, "reason", json_string("User busy"));
  1126. gateway->notify_event(&janus_videocall_plugin, session->handle, info);
  1127. }
  1128. gateway->close_pc(session->handle);
  1129. } else {
  1130. janus_mutex_unlock(&sessions_mutex);
  1131. /* Any SDP to handle? if not, something's wrong */
  1132. if(!msg_sdp) {
  1133. JANUS_LOG(LOG_ERR, "Missing SDP\n");
  1134. error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
  1135. g_snprintf(error_cause, 512, "Missing SDP");
  1136. goto error;
  1137. }
  1138. janus_mutex_lock(&sessions_mutex);
  1139. session->peer = peer;
  1140. peer->peer = session;
  1141. session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
  1142. session->has_video = (strstr(msg_sdp, "m=video") != NULL);
  1143. session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
  1144. janus_mutex_unlock(&sessions_mutex);
  1145. JANUS_LOG(LOG_VERB, "%s is calling %s\n", session->username, session->peer->username);
  1146. JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
  1147. /* Check if this user will simulcast */
  1148. json_t *msg_simulcast = json_object_get(msg->jsep, "simulcast");
  1149. if(msg_simulcast && janus_get_codec_pt(msg_sdp, "vp8") > 0) {
  1150. JANUS_LOG(LOG_VERB, "VideoCall caller (%s) is going to do simulcasting\n", session->username);
  1151. session->ssrc[0] = json_integer_value(json_object_get(msg_simulcast, "ssrc-0"));
  1152. session->ssrc[1] = json_integer_value(json_object_get(msg_simulcast, "ssrc-1"));
  1153. session->ssrc[2] = json_integer_value(json_object_get(msg_simulcast, "ssrc-2"));
  1154. }
  1155. /* Send SDP to our peer */
  1156. json_t *call = json_object();
  1157. json_object_set_new(call, "videocall", json_string("event"));
  1158. json_t *calling = json_object();
  1159. json_object_set_new(calling, "event", json_string("incomingcall"));
  1160. json_object_set_new(calling, "username", json_string(session->username));
  1161. json_object_set_new(call, "result", calling);
  1162. json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
  1163. g_atomic_int_set(&session->hangingup, 0);
  1164. int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, jsep);
  1165. JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
  1166. json_decref(call);
  1167. json_decref(jsep);
  1168. /* Send an ack back */
  1169. result = json_object();
  1170. json_object_set_new(result, "event", json_string("calling"));
  1171. /* Also notify event handlers */
  1172. if(notify_events && gateway->events_is_enabled()) {
  1173. json_t *info = json_object();
  1174. json_object_set_new(info, "event", json_string("calling"));
  1175. gateway->notify_event(&janus_videocall_plugin, session->handle, info);
  1176. }
  1177. }
  1178. } else if(!strcasecmp(request_text, "accept")) {
  1179. /* Accept a call from another peer */
  1180. if(session->peer == NULL) {
  1181. JANUS_LOG(LOG_ERR, "No incoming call to accept\n");
  1182. error_code = JANUS_VIDEOCALL_ERROR_NO_CALL;
  1183. g_snprintf(error_cause, 512, "No incoming call to accept");
  1184. goto error;
  1185. }
  1186. /* Any SDP to handle? if not, something's wrong */
  1187. if(!msg_sdp) {
  1188. JANUS_LOG(LOG_ERR, "Missing SDP\n");
  1189. error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
  1190. g_snprintf(error_cause, 512, "Missing SDP");
  1191. goto error;
  1192. }
  1193. JANUS_LOG(LOG_VERB, "%s is accepting a call from %s\n", session->username, session->peer->username);
  1194. JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
  1195. session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
  1196. session->has_video = (strstr(msg_sdp, "m=video") != NULL);
  1197. session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
  1198. /* Check if this user will simulcast */
  1199. json_t *msg_simulcast = json_object_get(msg->jsep, "simulcast");
  1200. if(msg_simulcast && janus_get_codec_pt(msg_sdp, "vp8") > 0) {
  1201. JANUS_LOG(LOG_VERB, "VideoCall callee (%s) is going to do simulcasting\n", session->username);
  1202. session->ssrc[0] = json_integer_value(json_object_get(msg_simulcast, "ssrc-0"));
  1203. session->ssrc[1] = json_integer_value(json_object_get(msg_simulcast, "ssrc-1"));
  1204. session->ssrc[2] = json_integer_value(json_object_get(msg_simulcast, "ssrc-2"));
  1205. } else {
  1206. session->ssrc[0] = 0;
  1207. session->ssrc[1] = 0;
  1208. session->ssrc[2] = 0;
  1209. if(session->peer) {
  1210. session->peer->ssrc[0] = 0;
  1211. session->peer->ssrc[1] = 0;
  1212. session->peer->ssrc[2] = 0;
  1213. }
  1214. }
  1215. /* Send SDP to our peer */
  1216. json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
  1217. json_t *call = json_object();
  1218. json_object_set_new(call, "videocall", json_string("event"));
  1219. json_t *calling = json_object();
  1220. json_object_set_

Large files files are truncated, but you can click here to view the full file