Skip to content

Commit e5b25bb

Browse files
Add kick_all requests and possibility to remove PIN code to both Audiobridge and Streaming plugins (#2978)
1 parent c16a77e commit e5b25bb

File tree

2 files changed

+271
-12
lines changed

2 files changed

+271
-12
lines changed

src/plugins/janus_audiobridge.c

+127-6
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ room-<unique room ID>: {
202202
"secret" : "<room secret, mandatory if configured>",
203203
"new_description" : "<new pretty name of the room, optional>",
204204
"new_secret" : "<new password required to edit/destroy the room, optional>",
205-
"new_pin" : "<new password required to join the room, optional>",
205+
"new_pin" : "<new PIN required to join the room, PIN will be removed if set to an empty string, optional>",
206206
"new_is_private" : <true|false, whether the room should appear in a list request>,
207207
"new_record_dir" : "<new path where new recording files should be saved>",
208208
"new_mjrs_dir" : "<new path where new MJR files should be saved>",
@@ -354,6 +354,29 @@ room-<unique room ID>: {
354354
{
355355
"audiobridge" : "success",
356356
}
357+
\endverbatim
358+
*
359+
* If you're the administrator of a room (that is, you created it and have access
360+
* to the secret) you can kick all participants using the \c kick_all request. Notice
361+
* that this only kicks all users out of the room, but does not prevent them from
362+
* re-joining: to ban them, you need to first remove them from the list of
363+
* authorized users (see \c allowed request) and then perform \c kick_all.
364+
* The \c kick_all request has to be formatted as follows:
365+
*
366+
\verbatim
367+
{
368+
"request" : "kick_all",
369+
"secret" : "<room secret, mandatory if configured>",
370+
"room" : <unique numeric ID of the room>
371+
}
372+
\endverbatim
373+
*
374+
* A successful request will result in a \c success response:
375+
*
376+
\verbatim
377+
{
378+
"audiobridge" : "success",
379+
}
357380
\endverbatim
358381
*
359382
* To get a list of the available rooms (excluded those configured or
@@ -3476,10 +3499,14 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
34763499
audiobridge->room_secret = new_secret;
34773500
g_free(old_secret);
34783501
}
3479-
if(pin && strlen(json_string_value(pin)) > 0) {
3502+
if(pin) {
34803503
char *old_pin = audiobridge->room_pin;
3481-
char *new_pin = g_strdup(json_string_value(pin));
3482-
audiobridge->room_pin = new_pin;
3504+
if(strlen(json_string_value(pin)) > 0) {
3505+
char *new_pin = g_strdup(json_string_value(pin));
3506+
audiobridge->room_pin = new_pin;
3507+
} else {
3508+
audiobridge->room_pin = NULL;
3509+
}
34833510
g_free(old_pin);
34843511
}
34853512
if(recdir) {
@@ -3820,7 +3847,7 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
38203847
janus_mutex_unlock(&participant->rec_mutex);
38213848
}
38223849
}
3823-
}
3850+
}
38243851
janus_mutex_unlock(&audiobridge->mutex);
38253852
janus_refcount_decrease(&audiobridge->ref);
38263853
response = json_object();
@@ -4349,8 +4376,8 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
43494376
goto prepare_response;
43504377
}
43514378
janus_refcount_increase(&audiobridge->ref);
4352-
janus_mutex_lock(&audiobridge->mutex);
43534379
janus_mutex_unlock(&rooms_mutex);
4380+
janus_mutex_lock(&audiobridge->mutex);
43544381
/* A secret may be required for this action */
43554382
JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
43564383
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
@@ -4412,6 +4439,100 @@ static json_t *janus_audiobridge_process_synchronous_request(janus_audiobridge_s
44124439
janus_mutex_unlock(&audiobridge->mutex);
44134440
janus_refcount_decrease(&audiobridge->ref);
44144441
goto prepare_response;
4442+
} else if(!strcasecmp(request_text, "kick_all")) {
4443+
JANUS_LOG(LOG_VERB, "Attempt to kick all participants from an existing AudioBridge room\n");
4444+
JANUS_VALIDATE_JSON_OBJECT(root, secret_parameters,
4445+
error_code, error_cause, TRUE,
4446+
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
4447+
if(error_code != 0)
4448+
goto prepare_response;
4449+
if(!string_ids) {
4450+
JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4451+
error_code, error_cause, TRUE,
4452+
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
4453+
} else {
4454+
JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4455+
error_code, error_cause, TRUE,
4456+
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
4457+
}
4458+
if(error_code != 0)
4459+
goto prepare_response;
4460+
json_t *room = json_object_get(root, "room");
4461+
guint64 room_id = 0;
4462+
char room_id_num[30], *room_id_str = NULL;
4463+
if(!string_ids) {
4464+
room_id = json_integer_value(room);
4465+
g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4466+
room_id_str = room_id_num;
4467+
} else {
4468+
room_id_str = (char *)json_string_value(room);
4469+
}
4470+
janus_mutex_lock(&rooms_mutex);
4471+
janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms,
4472+
string_ids ? (gpointer)room_id_str : (gpointer)&room_id);
4473+
if(audiobridge == NULL) {
4474+
janus_mutex_unlock(&rooms_mutex);
4475+
error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
4476+
JANUS_LOG(LOG_ERR, "No such room (%s)\n", room_id_str);
4477+
g_snprintf(error_cause, 512, "No such room (%s)", room_id_str);
4478+
goto prepare_response;
4479+
}
4480+
janus_refcount_increase(&audiobridge->ref);
4481+
janus_mutex_unlock(&rooms_mutex);
4482+
janus_mutex_lock(&audiobridge->mutex);
4483+
/* A secret may be required for this action */
4484+
JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
4485+
JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
4486+
if(error_code != 0) {
4487+
janus_mutex_unlock(&audiobridge->mutex);
4488+
janus_refcount_decrease(&audiobridge->ref);
4489+
goto prepare_response;
4490+
}
4491+
GHashTableIter kick_iter;
4492+
gpointer kick_value;
4493+
g_hash_table_iter_init(&kick_iter, audiobridge->participants);
4494+
while(g_hash_table_iter_next(&kick_iter, NULL, &kick_value)) {
4495+
janus_audiobridge_participant *participant = kick_value;
4496+
JANUS_LOG(LOG_VERB, "Kicking participant %s (%s)\n",
4497+
participant->user_id_str, participant->display ? participant->display : "??");
4498+
guint64 user_id = 0;
4499+
char user_id_num[30], *user_id_str = NULL;
4500+
if(string_ids) {
4501+
user_id_str = participant->user_id_str;
4502+
} else {
4503+
user_id = participant->user_id;
4504+
g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
4505+
user_id_str = user_id_num;
4506+
}
4507+
/* Notify all participants about the kick */
4508+
json_t *event = json_object();
4509+
json_object_set_new(event, "audiobridge", json_string("event"));
4510+
json_object_set_new(event, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4511+
json_object_set_new(event, "kicked_all", string_ids ? json_string(user_id_str) : json_integer(user_id));
4512+
JANUS_LOG(LOG_VERB, "Notifying participant %s (%s)\n", participant->user_id_str, participant->display ? participant->display : "??");
4513+
int ret = gateway->push_event(participant->session->handle, &janus_audiobridge_plugin, NULL, event, NULL);
4514+
JANUS_LOG(LOG_VERB, " >> %d (%s)\n", ret, janus_get_api_error(ret));
4515+
json_decref(event);
4516+
/* Also notify event handlers */
4517+
if(notify_events && gateway->events_is_enabled()) {
4518+
json_t *info = json_object();
4519+
json_object_set_new(info, "event", json_string("kicked_all"));
4520+
json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4521+
json_object_set_new(info, "id", string_ids ? json_string(user_id_str) : json_integer(user_id));
4522+
gateway->notify_event(&janus_audiobridge_plugin, session ? session->handle : NULL, info);
4523+
}
4524+
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
4525+
if(participant && participant->session)
4526+
gateway->close_pc(participant->session->handle);
4527+
JANUS_LOG(LOG_VERB, "Kicked user %s from room %s\n", user_id_str, room_id_str);
4528+
}
4529+
/* Prepare response */
4530+
response = json_object();
4531+
json_object_set_new(response, "audiobridge", json_string("success"));
4532+
/* Done */
4533+
janus_mutex_unlock(&audiobridge->mutex);
4534+
janus_refcount_decrease(&audiobridge->ref);
4535+
goto prepare_response;
44154536
} else if(!strcasecmp(request_text, "listparticipants")) {
44164537
/* List all participants in a room */
44174538
if(!string_ids) {

src/plugins/janus_streaming.c

+144-6
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ multistream-test: {
464464
"new_description" : "<new description for the mountpoint; optional>",
465465
"new_metadata" : "<new metadata for the mountpoint; optional>",
466466
"new_secret" : "<new secret for the mountpoint; optional>",
467-
"new_pin" : "<new PIN for the mountpoint; optional>",
467+
"new_pin" : "<new PIN for the mountpoint, PIN will be removed if set to an empty string; optional>",
468468
"new_is_private" : <true|false, depending on whether the mountpoint should be now listable; optional>,
469469
"permanent" : <true|false, whether the mountpoint should be saved to configuration file or not; false by default>
470470
}
@@ -548,6 +548,27 @@ multistream-test: {
548548
{
549549
"streaming" : "ok"
550550
}
551+
\endverbatim
552+
*
553+
* You can kick all viewers from a mountpoint using the \c kick_all request. Notice
554+
* that this only removes all viewers, but does not prevent them from starting to watch
555+
* the mountpoint again. Please note this request works with all mountpoint types,
556+
* except for on-demand streaming. The \c kick_all request has to be formatted as follows:
557+
*
558+
\verbatim
559+
{
560+
"request" : "kick_all",
561+
"id" : <unique ID of the mountpoint to disable; mandatory>,
562+
"secret" : "<mountpoint secret; mandatory if configured>",
563+
}
564+
\endverbatim
565+
*
566+
* If successful, a \c kicked_all response is returned:
567+
*
568+
\verbatim
569+
{
570+
"streaming" : "kicked_all",
571+
}
551572
\endverbatim
552573
*
553574
* Finally, you can record a mountpoint to the internal Janus .mjr format
@@ -4224,10 +4245,14 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
42244245
mp->secret = new_secret;
42254246
g_free(old_secret);
42264247
}
4227-
if(pin && strlen(json_string_value(pin)) > 0) {
4248+
if(pin) {
42284249
char *old_pin = mp->pin;
4229-
char *new_pin = g_strdup(json_string_value(pin));
4230-
mp->pin = new_pin;
4250+
if(strlen(json_string_value(pin)) > 0) {
4251+
char *new_pin = g_strdup(json_string_value(pin));
4252+
mp->pin = new_pin;
4253+
} else {
4254+
mp->pin = NULL;
4255+
}
42314256
g_free(old_pin);
42324257
}
42334258
if(save) {
@@ -4404,6 +4429,119 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
44044429
/* Done */
44054430
JANUS_LOG(LOG_VERB, "Streaming mountpoint edited\n");
44064431
goto prepare_response;
4432+
} else if(!strcasecmp(request_text, "kick_all")) {
4433+
/* Note the kick_all request works with all mountpoint types except for on-demand streaming,
4434+
* because each on-demand viewer has their own thread and their own playback context. */
4435+
if(!string_ids) {
4436+
JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
4437+
error_code, error_cause, TRUE,
4438+
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4439+
} else {
4440+
JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
4441+
error_code, error_cause, TRUE,
4442+
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4443+
}
4444+
if(error_code != 0)
4445+
goto prepare_response;
4446+
json_t *id = json_object_get(root, "id");
4447+
guint64 id_value = 0;
4448+
char id_num[30], *id_value_str = NULL;
4449+
if(!string_ids) {
4450+
id_value = json_integer_value(id);
4451+
g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
4452+
id_value_str = id_num;
4453+
} else {
4454+
id_value_str = (char *)json_string_value(id);
4455+
}
4456+
janus_mutex_lock(&mountpoints_mutex);
4457+
janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
4458+
string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
4459+
if(mp == NULL) {
4460+
janus_mutex_unlock(&mountpoints_mutex);
4461+
JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
4462+
error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
4463+
g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
4464+
goto prepare_response;
4465+
}
4466+
janus_refcount_increase(&mp->ref);
4467+
/* A secret may be required for this action */
4468+
JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
4469+
JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
4470+
if(error_code != 0) {
4471+
janus_refcount_decrease(&mp->ref);
4472+
janus_mutex_unlock(&mountpoints_mutex);
4473+
goto prepare_response;
4474+
}
4475+
JANUS_LOG(LOG_VERB, "Request to kick all viewers from mountpoint/stream %s\n", id_value_str);
4476+
janus_mutex_lock(&mp->mutex);
4477+
GList *viewer = g_list_first(mp->viewers);
4478+
/* Prepare JSON event */
4479+
json_t *event = json_object();
4480+
json_object_set_new(event, "streaming", json_string("event"));
4481+
json_t *result = json_object();
4482+
json_object_set_new(result, "status", json_string("kicked_all"));
4483+
json_object_set_new(event, "result", result);
4484+
while(viewer) {
4485+
janus_streaming_session *s = (janus_streaming_session *)viewer->data;
4486+
if(s == NULL) {
4487+
mp->viewers = g_list_remove_all(mp->viewers, s);
4488+
viewer = g_list_first(mp->viewers);
4489+
continue;
4490+
}
4491+
janus_mutex_lock(&s->mutex);
4492+
if(s->mountpoint != mp) {
4493+
mp->viewers = g_list_remove_all(mp->viewers, s);
4494+
viewer = g_list_first(mp->viewers);
4495+
janus_mutex_unlock(&s->mutex);
4496+
continue;
4497+
}
4498+
g_atomic_int_set(&s->stopping, 1);
4499+
g_atomic_int_set(&s->started, 0);
4500+
g_atomic_int_set(&s->paused, 0);
4501+
s->mountpoint = NULL;
4502+
janus_mutex_unlock(&s->mutex);
4503+
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
4504+
gateway->push_event(s->handle, &janus_streaming_plugin, NULL, event, NULL);
4505+
gateway->close_pc(s->handle);
4506+
if(mp->streaming_source == janus_streaming_source_rtp) {
4507+
/* Remove the viewer from the helper threads too, if any */
4508+
if(mp->helper_threads > 0) {
4509+
GList *l = mp->threads;
4510+
while(l) {
4511+
janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
4512+
janus_mutex_lock(&ht->mutex);
4513+
if(g_list_find(ht->viewers, s) != NULL) {
4514+
ht->num_viewers--;
4515+
ht->viewers = g_list_remove_all(ht->viewers, s);
4516+
janus_mutex_unlock(&ht->mutex);
4517+
JANUS_LOG(LOG_VERB, "Removing viewer from helper thread #%d (destroy)\n", ht->id);
4518+
break;
4519+
}
4520+
janus_mutex_unlock(&ht->mutex);
4521+
l = l->next;
4522+
}
4523+
}
4524+
}
4525+
mp->viewers = g_list_remove_all(mp->viewers, s);
4526+
viewer = g_list_first(mp->viewers);
4527+
janus_refcount_decrease(&s->ref);
4528+
janus_refcount_decrease(&mp->ref);
4529+
}
4530+
json_decref(event);
4531+
janus_mutex_unlock(&mp->mutex);
4532+
janus_refcount_decrease(&mp->ref);
4533+
/* Also notify event handlers */
4534+
if(notify_events && gateway->events_is_enabled()) {
4535+
json_t *info = json_object();
4536+
json_object_set_new(info, "event", json_string("kicked_all"));
4537+
json_object_set_new(info, "id", string_ids ? json_string(id_value_str) : json_integer(id_value));
4538+
gateway->notify_event(&janus_streaming_plugin, session ? session->handle : NULL, info);
4539+
}
4540+
janus_mutex_unlock(&mountpoints_mutex);
4541+
/* Send info back */
4542+
response = json_object();
4543+
json_object_set_new(response, "streaming", json_string("kicked_all"));
4544+
goto prepare_response;
44074545
} else if(!strcasecmp(request_text, "destroy")) {
44084546
/* Get rid of an existing stream (notice this doesn't remove it from the config file, though) */
44094547
JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
@@ -4493,8 +4631,6 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
44934631
/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
44944632
gateway->push_event(s->handle, &janus_streaming_plugin, NULL, event, NULL);
44954633
gateway->close_pc(s->handle);
4496-
janus_refcount_decrease(&s->ref);
4497-
janus_refcount_decrease(&mp->ref);
44984634
if(mp->streaming_source == janus_streaming_source_rtp) {
44994635
/* Remove the viewer from the helper threads too, if any */
45004636
if(mp->helper_threads > 0) {
@@ -4517,6 +4653,8 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
45174653
mp->viewers = g_list_remove_all(mp->viewers, s);
45184654
viewer = g_list_first(mp->viewers);
45194655
janus_mutex_unlock(&s->mutex);
4656+
janus_refcount_decrease(&s->ref);
4657+
janus_refcount_decrease(&mp->ref);
45204658
}
45214659
json_decref(event);
45224660
janus_mutex_unlock(&mp->mutex);

0 commit comments

Comments
 (0)