mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-03-15 05:08:26 +00:00
FS-10243: [mod_conference] Add conference variables #resolve
This commit is contained in:
parent
3d885ac5c0
commit
b84662ae6b
@ -103,6 +103,7 @@ api_command_t conference_api_sub_commands[] = {
|
|||||||
{"vid-banner", (void_fn_t) & conference_api_sub_vid_banner, CONF_API_SUB_MEMBER_TARGET, "vid-banner", "<member_id|last> <text>"},
|
{"vid-banner", (void_fn_t) & conference_api_sub_vid_banner, CONF_API_SUB_MEMBER_TARGET, "vid-banner", "<member_id|last> <text>"},
|
||||||
{"vid-mute-img", (void_fn_t) & conference_api_sub_vid_mute_img, CONF_API_SUB_MEMBER_TARGET, "vid-mute-img", "<member_id|last> [<path>|clear]"},
|
{"vid-mute-img", (void_fn_t) & conference_api_sub_vid_mute_img, CONF_API_SUB_MEMBER_TARGET, "vid-mute-img", "<member_id|last> [<path>|clear]"},
|
||||||
{"vid-logo-img", (void_fn_t) & conference_api_sub_vid_logo_img, CONF_API_SUB_MEMBER_TARGET, "vid-logo-img", "<member_id|last> [<path>|clear]"},
|
{"vid-logo-img", (void_fn_t) & conference_api_sub_vid_logo_img, CONF_API_SUB_MEMBER_TARGET, "vid-logo-img", "<member_id|last> [<path>|clear]"},
|
||||||
|
{"vid-codec-group", (void_fn_t) & conference_api_sub_vid_codec_group, CONF_API_SUB_MEMBER_TARGET, "vid-codec-group", "<member_id|last> [<group>|clear]"},
|
||||||
{"vid-res-id", (void_fn_t) & conference_api_sub_vid_res_id, CONF_API_SUB_MEMBER_TARGET, "vid-res-id", "<member_id|last> <val>|clear"},
|
{"vid-res-id", (void_fn_t) & conference_api_sub_vid_res_id, CONF_API_SUB_MEMBER_TARGET, "vid-res-id", "<member_id|last> <val>|clear"},
|
||||||
{"vid-role-id", (void_fn_t) & conference_api_sub_vid_role_id, CONF_API_SUB_MEMBER_TARGET, "vid-role-id", "<member_id|last> <val>|clear"},
|
{"vid-role-id", (void_fn_t) & conference_api_sub_vid_role_id, CONF_API_SUB_MEMBER_TARGET, "vid-role-id", "<member_id|last> <val>|clear"},
|
||||||
{"get-uuid", (void_fn_t) & conference_api_sub_get_uuid, CONF_API_SUB_MEMBER_TARGET, "get-uuid", "<member_id|last>"},
|
{"get-uuid", (void_fn_t) & conference_api_sub_get_uuid, CONF_API_SUB_MEMBER_TARGET, "get-uuid", "<member_id|last>"},
|
||||||
@ -1314,8 +1315,11 @@ switch_status_t conference_api_sub_vid_bandwidth(conference_obj_t *conference, s
|
|||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
int32_t video_write_bandwidth;
|
int32_t video_write_bandwidth;
|
||||||
int x = 0;
|
int x = 0, id = -1;
|
||||||
|
char *group = NULL;
|
||||||
|
char *array[4] = {0};
|
||||||
|
int sdiv = 0, fdiv = 0;
|
||||||
|
|
||||||
if (!conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
if (!conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
||||||
stream->write_function(stream, "Bandwidth control not available.\n");
|
stream->write_function(stream, "Bandwidth control not available.\n");
|
||||||
return SWITCH_STATUS_SUCCESS;
|
return SWITCH_STATUS_SUCCESS;
|
||||||
@ -1325,15 +1329,87 @@ switch_status_t conference_api_sub_vid_bandwidth(conference_obj_t *conference, s
|
|||||||
stream->write_function(stream, "Invalid input\n");
|
stream->write_function(stream, "Invalid input\n");
|
||||||
return SWITCH_STATUS_SUCCESS;
|
return SWITCH_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch_split(argv[2], ':', array);
|
||||||
|
|
||||||
video_write_bandwidth = switch_parse_bandwidth_string(argv[2]);
|
if (array[1]) {
|
||||||
for (i = 0; i <= conference->canvas_count; i++) {
|
sdiv = atoi(array[2]);
|
||||||
if (conference->canvases[i]) {
|
if (sdiv < 2 || sdiv > 8) {
|
||||||
stream->write_function(stream, "Set Bandwidth for canvas %d to %d\n", i + 1, video_write_bandwidth);
|
sdiv = 0;
|
||||||
x++;
|
|
||||||
conference->canvases[i]->video_write_bandwidth = video_write_bandwidth;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (array[2]) {
|
||||||
|
fdiv = atoi(array[2]);
|
||||||
|
if (fdiv < 2 || fdiv > 8) {
|
||||||
|
fdiv = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video_write_bandwidth = switch_parse_bandwidth_string(array[0]);
|
||||||
|
|
||||||
|
if (argv[3]) {
|
||||||
|
group = argv[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[4]) {
|
||||||
|
|
||||||
|
if (argv[4]) {
|
||||||
|
id = atoi(argv[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id < 1 || id > MAX_CANVASES+1) {
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id < 1 || id > conference->canvas_count) {
|
||||||
|
stream->write_function(stream, "-ERR Invalid canvas\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_mutex_lock(conference->member_mutex);
|
||||||
|
for (i = 0; i <= conference->canvas_count; i++) {
|
||||||
|
if (i > -1 && i != id - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conference->canvases[i]) {
|
||||||
|
mcu_canvas_t *canvas = conference->canvases[i];
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < canvas->write_codecs_count; j++) {
|
||||||
|
if ((zstr(group) || !strcmp(group, switch_str_nil(canvas->write_codecs[j]->video_codec_group)))) {
|
||||||
|
switch_core_codec_control(&canvas->write_codecs[j]->codec, SCC_VIDEO_BANDWIDTH,
|
||||||
|
SCCT_INT, &video_write_bandwidth, SCCT_NONE, NULL, NULL, NULL);
|
||||||
|
stream->write_function(stream, "Set Bandwidth for canvas %d index %d group[%s] to %d\n", i + 1, j,
|
||||||
|
switch_str_nil(canvas->write_codecs[j]->video_codec_group), video_write_bandwidth);
|
||||||
|
|
||||||
|
if (fdiv) {
|
||||||
|
canvas->write_codecs[j]->fps_divisor = fdiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdiv) {
|
||||||
|
int w = 0, h = 0;
|
||||||
|
|
||||||
|
w = canvas->width;
|
||||||
|
h = canvas->width;
|
||||||
|
|
||||||
|
w /= sdiv;
|
||||||
|
h /= sdiv;
|
||||||
|
|
||||||
|
switch_img_free(&canvas->write_codecs[j]->scaled_img);
|
||||||
|
canvas->write_codecs[j]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, w, h, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch_mutex_unlock(conference->member_mutex);
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
if (!x) {
|
if (!x) {
|
||||||
stream->write_function(stream, "Bandwidth not set\n");
|
stream->write_function(stream, "Bandwidth not set\n");
|
||||||
@ -1903,6 +1979,39 @@ switch_status_t conference_api_sub_vid_logo_img(conference_member_t *member, swi
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch_status_t conference_api_sub_vid_codec_group(conference_member_t *member, switch_stream_handle_t *stream, void *data)
|
||||||
|
{
|
||||||
|
char *text = (char *) data;
|
||||||
|
|
||||||
|
if (member == NULL)
|
||||||
|
return SWITCH_STATUS_GENERR;
|
||||||
|
|
||||||
|
if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
|
||||||
|
if (!strcmp(text, "clear")) {
|
||||||
|
member->video_codec_group = NULL;
|
||||||
|
} else {
|
||||||
|
member->video_codec_group = switch_core_strdup(member->pool, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_mutex_lock(member->conference->member_mutex);
|
||||||
|
member->video_codec_index = -1;
|
||||||
|
switch_mutex_unlock(member->conference->member_mutex);
|
||||||
|
stream->write_function(stream, "Video codec group %s %s\n", member->video_codec_group ? "set" : "cleared", switch_str_nil(member->video_codec_group));
|
||||||
|
} else {
|
||||||
|
stream->write_function(stream, "Video codec group is %s\n", member->video_codec_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data)
|
switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data)
|
||||||
{
|
{
|
||||||
if (member->session) {
|
if (member->session) {
|
||||||
|
@ -745,6 +745,10 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
|
|||||||
conference_member_set_logo(member, var);
|
conference_member_set_logo(member, var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((var = switch_channel_get_variable_dup(member->channel, "video_codec_group", SWITCH_FALSE, -1))) {
|
||||||
|
member->video_codec_group = switch_core_strdup(member->pool, var);
|
||||||
|
}
|
||||||
|
|
||||||
if ((var = switch_channel_get_variable_dup(member->channel, "conference_join_volume_in", SWITCH_FALSE, -1))) {
|
if ((var = switch_channel_get_variable_dup(member->channel, "conference_join_volume_in", SWITCH_FALSE, -1))) {
|
||||||
uint32_t id = atoi(var);
|
uint32_t id = atoi(var);
|
||||||
|
|
||||||
|
@ -1755,11 +1755,15 @@ void conference_video_write_canvas_image_to_codec_group(conference_obj_t *confer
|
|||||||
if (imember->watching_canvas_id != canvas->canvas_id) {
|
if (imember->watching_canvas_id != canvas->canvas_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conference_utils_member_test_flag(imember, MFLAG_NO_MINIMIZE_ENCODING)) {
|
if (conference_utils_member_test_flag(imember, MFLAG_NO_MINIMIZE_ENCODING)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (codec_set->video_codec_group && (!imember->video_codec_group || strcmp(codec_set->video_codec_group, imember->video_codec_group))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (imember->video_codec_index != codec_index) {
|
if (imember->video_codec_index != codec_index) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2854,7 +2858,6 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
|
|||||||
conference_obj_t *conference = canvas->conference;
|
conference_obj_t *conference = canvas->conference;
|
||||||
conference_member_t *imember;
|
conference_member_t *imember;
|
||||||
switch_codec_t *check_codec = NULL;
|
switch_codec_t *check_codec = NULL;
|
||||||
codec_set_t *write_codecs[MAX_MUX_CODECS] = { 0 };
|
|
||||||
int buflen = SWITCH_RTP_MAX_BUF_LEN;
|
int buflen = SWITCH_RTP_MAX_BUF_LEN;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
uint32_t video_key_freq = 10000000;
|
uint32_t video_key_freq = 10000000;
|
||||||
@ -3056,35 +3059,66 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
|
|||||||
|
|
||||||
if (switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
|
if (switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
|
||||||
if (imember->video_codec_index < 0 && (check_codec = switch_core_session_get_video_write_codec(imember->session))) {
|
if (imember->video_codec_index < 0 && (check_codec = switch_core_session_get_video_write_codec(imember->session))) {
|
||||||
for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
for (i = 0; canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
||||||
if (check_codec->implementation->codec_id == write_codecs[i]->codec.implementation->codec_id) {
|
if (check_codec->implementation->codec_id == canvas->write_codecs[i]->codec.implementation->codec_id) {
|
||||||
imember->video_codec_index = i;
|
if ((zstr(imember->video_codec_group) && zstr(canvas->write_codecs[i]->video_codec_group)) ||
|
||||||
imember->video_codec_id = check_codec->implementation->codec_id;
|
(!strcmp(switch_str_nil(imember->video_codec_group), switch_str_nil(canvas->write_codecs[i]->video_codec_group)))) {
|
||||||
need_refresh = SWITCH_TRUE;
|
|
||||||
break;
|
imember->video_codec_index = i;
|
||||||
|
imember->video_codec_id = check_codec->implementation->codec_id;
|
||||||
|
need_refresh = SWITCH_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imember->video_codec_index < 0) {
|
if (imember->video_codec_index < 0) {
|
||||||
write_codecs[i] = switch_core_alloc(conference->pool, sizeof(codec_set_t));
|
canvas->write_codecs[i] = switch_core_alloc(conference->pool, sizeof(codec_set_t));
|
||||||
|
canvas->write_codecs_count = i+1;
|
||||||
|
|
||||||
if (switch_core_codec_copy(check_codec, &write_codecs[i]->codec,
|
if (switch_core_codec_copy(check_codec, &canvas->write_codecs[i]->codec,
|
||||||
&conference->video_codec_settings, conference->pool) == SWITCH_STATUS_SUCCESS) {
|
&conference->video_codec_settings, conference->pool) == SWITCH_STATUS_SUCCESS) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
|
||||||
"Setting up video write codec %s at slot %d\n", write_codecs[i]->codec.implementation->iananame, i);
|
"Setting up video write codec %s at slot %d group %s\n",
|
||||||
|
canvas->write_codecs[i]->codec.implementation->iananame, i,
|
||||||
|
imember->video_codec_group ? imember->video_codec_group : "_none_");
|
||||||
|
|
||||||
imember->video_codec_index = i;
|
imember->video_codec_index = i;
|
||||||
imember->video_codec_id = check_codec->implementation->codec_id;
|
imember->video_codec_id = check_codec->implementation->codec_id;
|
||||||
need_refresh = SWITCH_TRUE;
|
need_refresh = SWITCH_TRUE;
|
||||||
write_codecs[i]->frame.packet = switch_core_alloc(conference->pool, buflen);
|
if (imember->video_codec_group) {
|
||||||
write_codecs[i]->frame.data = ((uint8_t *)write_codecs[i]->frame.packet) + 12;
|
const char *gname = switch_core_sprintf(conference->pool, "group-%s", imember->video_codec_group);
|
||||||
write_codecs[i]->frame.packetlen = buflen;
|
const char *val = NULL;
|
||||||
write_codecs[i]->frame.buflen = buflen - 12;
|
|
||||||
|
canvas->write_codecs[i]->video_codec_group = switch_core_strdup(conference->pool, imember->video_codec_group);
|
||||||
|
|
||||||
|
if ((val = conference_get_variable(conference, gname))) {
|
||||||
|
switch_stream_handle_t stream = { 0 };
|
||||||
|
char *argv[5] = {0};
|
||||||
|
char cid[32] = "";
|
||||||
|
|
||||||
|
SWITCH_STANDARD_STREAM(stream);
|
||||||
|
switch_snprintf(cid, sizeof(cid), "%d", canvas->canvas_id + 1);
|
||||||
|
|
||||||
|
argv[2] = (char *)val;
|
||||||
|
argv[3] = imember->video_codec_group;
|
||||||
|
argv[4] = cid;
|
||||||
|
conference_api_sub_vid_bandwidth(conference, &stream, 5, argv);
|
||||||
|
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "codec group init [%s]\n", (char *)stream.data);
|
||||||
|
free(stream.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
canvas->write_codecs[i]->frame.packet = switch_core_alloc(conference->pool, buflen);
|
||||||
|
canvas->write_codecs[i]->frame.data = ((uint8_t *)canvas->write_codecs[i]->frame.packet) + 12;
|
||||||
|
canvas->write_codecs[i]->frame.packetlen = buflen;
|
||||||
|
canvas->write_codecs[i]->frame.buflen = buflen - 12;
|
||||||
if (conference->scale_h264_canvas_width > 0 && conference->scale_h264_canvas_height > 0 && !strcmp(check_codec->implementation->iananame, "H264")) {
|
if (conference->scale_h264_canvas_width > 0 && conference->scale_h264_canvas_height > 0 && !strcmp(check_codec->implementation->iananame, "H264")) {
|
||||||
int32_t bw = -1;
|
int32_t bw = -1;
|
||||||
|
|
||||||
write_codecs[i]->fps_divisor = conference->scale_h264_canvas_fps_divisor;
|
canvas->write_codecs[i]->fps_divisor = conference->scale_h264_canvas_fps_divisor;
|
||||||
write_codecs[i]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, 16);
|
canvas->write_codecs[i]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, 16);
|
||||||
|
|
||||||
if (conference->scale_h264_canvas_bandwidth) {
|
if (conference->scale_h264_canvas_bandwidth) {
|
||||||
if (strcasecmp(conference->scale_h264_canvas_bandwidth, "auto")) {
|
if (strcasecmp(conference->scale_h264_canvas_bandwidth, "auto")) {
|
||||||
@ -3095,14 +3129,14 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
|
|||||||
if (bw == -1) {
|
if (bw == -1) {
|
||||||
float fps = conference->video_fps.fps;
|
float fps = conference->video_fps.fps;
|
||||||
|
|
||||||
if (write_codecs[i]->fps_divisor) fps /= write_codecs[i]->fps_divisor;
|
if (canvas->write_codecs[i]->fps_divisor) fps /= canvas->write_codecs[i]->fps_divisor;
|
||||||
|
|
||||||
bw = switch_calc_bitrate(conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, conference->video_quality, fps);
|
bw = switch_calc_bitrate(conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, conference->video_quality, fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_core_codec_control(&write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH, SCCT_INT, &bw, SCCT_NONE, NULL, NULL, NULL);
|
switch_core_codec_control(&canvas->write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH, SCCT_INT, &bw, SCCT_NONE, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
switch_set_flag((&write_codecs[i]->frame), SFF_RAW_RTP);
|
switch_set_flag((&canvas->write_codecs[i]->frame), SFF_RAW_RTP);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3796,17 +3830,10 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (min_members && conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
if (min_members && conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
||||||
for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
for (i = 0; canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
||||||
write_codecs[i]->frame.img = write_img;
|
canvas->write_codecs[i]->frame.img = write_img;
|
||||||
conference_video_write_canvas_image_to_codec_group(conference, canvas, write_codecs[i], i,
|
conference_video_write_canvas_image_to_codec_group(conference, canvas, canvas->write_codecs[i], i,
|
||||||
timestamp, need_refresh, send_keyframe, need_reset);
|
timestamp, need_refresh, send_keyframe, need_reset);
|
||||||
|
|
||||||
if (canvas->video_write_bandwidth) {
|
|
||||||
switch_core_codec_control(&write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH,
|
|
||||||
SCCT_INT, &canvas->video_write_bandwidth, SCCT_NONE, NULL, NULL, NULL);
|
|
||||||
canvas->video_write_bandwidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3894,9 +3921,9 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAX_MUX_CODECS; i++) {
|
for (i = 0; i < MAX_MUX_CODECS; i++) {
|
||||||
if (write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec)) {
|
if (canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec)) {
|
||||||
switch_core_codec_destroy(&write_codecs[i]->codec);
|
switch_core_codec_destroy(&canvas->write_codecs[i]->codec);
|
||||||
switch_img_free(&(write_codecs[i]->scaled_img));
|
switch_img_free(&(canvas->write_codecs[i]->scaled_img));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3935,7 +3962,6 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
|
|||||||
conference_obj_t *conference = canvas->conference;
|
conference_obj_t *conference = canvas->conference;
|
||||||
conference_member_t *imember;
|
conference_member_t *imember;
|
||||||
switch_codec_t *check_codec = NULL;
|
switch_codec_t *check_codec = NULL;
|
||||||
codec_set_t *write_codecs[MAX_MUX_CODECS] = { 0 };
|
|
||||||
int buflen = SWITCH_RTP_MAX_BUF_LEN;
|
int buflen = SWITCH_RTP_MAX_BUF_LEN;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
switch_time_t last_key_time = 0;
|
switch_time_t last_key_time = 0;
|
||||||
@ -4046,31 +4072,63 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
|
|||||||
|
|
||||||
if (switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
|
if (switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
|
||||||
if (imember->video_codec_index < 0 && (check_codec = switch_core_session_get_video_write_codec(imember->session))) {
|
if (imember->video_codec_index < 0 && (check_codec = switch_core_session_get_video_write_codec(imember->session))) {
|
||||||
for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
for (i = 0; canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
||||||
if (check_codec->implementation->codec_id == write_codecs[i]->codec.implementation->codec_id) {
|
if (check_codec->implementation->codec_id == canvas->write_codecs[i]->codec.implementation->codec_id) {
|
||||||
imember->video_codec_index = i;
|
if ((zstr(imember->video_codec_group) && zstr(canvas->write_codecs[i]->video_codec_group)) ||
|
||||||
imember->video_codec_id = check_codec->implementation->codec_id;
|
(!strcmp(switch_str_nil(imember->video_codec_group), switch_str_nil(canvas->write_codecs[i]->video_codec_group)))) {
|
||||||
need_refresh = SWITCH_TRUE;
|
|
||||||
break;
|
imember->video_codec_index = i;
|
||||||
|
imember->video_codec_id = check_codec->implementation->codec_id;
|
||||||
|
need_refresh = SWITCH_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imember->video_codec_index < 0) {
|
if (imember->video_codec_index < 0) {
|
||||||
write_codecs[i] = switch_core_alloc(conference->pool, sizeof(codec_set_t));
|
canvas->write_codecs[i] = switch_core_alloc(conference->pool, sizeof(codec_set_t));
|
||||||
|
canvas->write_codecs_count = i+1;
|
||||||
if (switch_core_codec_copy(check_codec, &write_codecs[i]->codec,
|
|
||||||
|
if (switch_core_codec_copy(check_codec, &canvas->write_codecs[i]->codec,
|
||||||
&conference->video_codec_settings, conference->pool) == SWITCH_STATUS_SUCCESS) {
|
&conference->video_codec_settings, conference->pool) == SWITCH_STATUS_SUCCESS) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
|
||||||
"Setting up video write codec %s at slot %d\n", write_codecs[i]->codec.implementation->iananame, i);
|
"Setting up video write codec %s at slot %d group %s\n",
|
||||||
|
canvas->write_codecs[i]->codec.implementation->iananame, i,
|
||||||
|
imember->video_codec_group ? imember->video_codec_group : "_none_");
|
||||||
|
|
||||||
imember->video_codec_index = i;
|
imember->video_codec_index = i;
|
||||||
imember->video_codec_id = check_codec->implementation->codec_id;
|
imember->video_codec_id = check_codec->implementation->codec_id;
|
||||||
need_refresh = SWITCH_TRUE;
|
need_refresh = SWITCH_TRUE;
|
||||||
write_codecs[i]->frame.packet = switch_core_alloc(conference->pool, buflen);
|
if (imember->video_codec_group) {
|
||||||
write_codecs[i]->frame.data = ((uint8_t *)write_codecs[i]->frame.packet) + 12;
|
canvas->write_codecs[i]->video_codec_group = switch_core_strdup(conference->pool, imember->video_codec_group);
|
||||||
write_codecs[i]->frame.packetlen = buflen;
|
}
|
||||||
write_codecs[i]->frame.buflen = buflen - 12;
|
canvas->write_codecs[i]->frame.packet = switch_core_alloc(conference->pool, buflen);
|
||||||
switch_set_flag((&write_codecs[i]->frame), SFF_RAW_RTP);
|
canvas->write_codecs[i]->frame.data = ((uint8_t *)canvas->write_codecs[i]->frame.packet) + 12;
|
||||||
|
canvas->write_codecs[i]->frame.packetlen = buflen;
|
||||||
|
canvas->write_codecs[i]->frame.buflen = buflen - 12;
|
||||||
|
if (conference->scale_h264_canvas_width > 0 && conference->scale_h264_canvas_height > 0 && !strcmp(check_codec->implementation->iananame, "H264")) {
|
||||||
|
int32_t bw = -1;
|
||||||
|
|
||||||
|
canvas->write_codecs[i]->fps_divisor = conference->scale_h264_canvas_fps_divisor;
|
||||||
|
canvas->write_codecs[i]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, 16);
|
||||||
|
|
||||||
|
if (conference->scale_h264_canvas_bandwidth) {
|
||||||
|
if (strcasecmp(conference->scale_h264_canvas_bandwidth, "auto")) {
|
||||||
|
bw = switch_parse_bandwidth_string(conference->scale_h264_canvas_bandwidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bw == -1) {
|
||||||
|
float fps = conference->video_fps.fps;
|
||||||
|
|
||||||
|
if (canvas->write_codecs[i]->fps_divisor) fps /= canvas->write_codecs[i]->fps_divisor;
|
||||||
|
|
||||||
|
bw = switch_calc_bitrate(conference->scale_h264_canvas_width, conference->scale_h264_canvas_height, conference->video_quality, fps);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_core_codec_control(&canvas->write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH, SCCT_INT, &bw, SCCT_NONE, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
switch_set_flag((&canvas->write_codecs[i]->frame), SFF_RAW_RTP);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4154,15 +4212,9 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (min_members && conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
if (min_members && conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
|
||||||
for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
for (i = 0; canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {
|
||||||
write_codecs[i]->frame.img = write_img;
|
canvas->write_codecs[i]->frame.img = write_img;
|
||||||
conference_video_write_canvas_image_to_codec_group(conference, canvas, write_codecs[i], i, timestamp, need_refresh, send_keyframe, need_reset);
|
conference_video_write_canvas_image_to_codec_group(conference, canvas, canvas->write_codecs[i], i, timestamp, need_refresh, send_keyframe, need_reset);
|
||||||
|
|
||||||
if (canvas->video_write_bandwidth) {
|
|
||||||
switch_core_codec_control(&write_codecs[i]->codec, SCC_VIDEO_BANDWIDTH,
|
|
||||||
SCCT_INT, &canvas->video_write_bandwidth, SCCT_NONE, NULL, NULL, NULL);
|
|
||||||
canvas->video_write_bandwidth = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4241,8 +4293,8 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAX_MUX_CODECS; i++) {
|
for (i = 0; i < MAX_MUX_CODECS; i++) {
|
||||||
if (write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec)) {
|
if (canvas->write_codecs[i] && switch_core_codec_ready(&canvas->write_codecs[i]->codec)) {
|
||||||
switch_core_codec_destroy(&write_codecs[i]->codec);
|
switch_core_codec_destroy(&canvas->write_codecs[i]->codec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,6 +843,9 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob
|
|||||||
switch_core_hash_destroy(&conference->layout_group_hash);
|
switch_core_hash_destroy(&conference->layout_group_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch_mutex_lock(conference->flag_mutex);
|
||||||
|
switch_event_destroy(&conference->variables);
|
||||||
|
switch_mutex_unlock(conference->flag_mutex);
|
||||||
|
|
||||||
if (conference->pool) {
|
if (conference->pool) {
|
||||||
switch_memory_pool_t *pool = conference->pool;
|
switch_memory_pool_t *pool = conference->pool;
|
||||||
@ -2550,6 +2553,28 @@ conference_obj_t *conference_find(char *name, char *domain)
|
|||||||
return conference;
|
return conference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void conference_set_variable(conference_obj_t *conference, const char *var, const char *val)
|
||||||
|
{
|
||||||
|
switch_mutex_lock(conference->flag_mutex);
|
||||||
|
switch_event_add_header_string(conference->variables, SWITCH_STACK_BOTTOM, var, val);
|
||||||
|
switch_mutex_unlock(conference->flag_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *conference_get_variable(conference_obj_t *conference, const char *var)
|
||||||
|
{
|
||||||
|
const char *val;
|
||||||
|
|
||||||
|
switch_mutex_lock(conference->flag_mutex);
|
||||||
|
val = switch_event_get_header(conference->variables, var);
|
||||||
|
switch_mutex_unlock(conference->flag_mutex);
|
||||||
|
|
||||||
|
if (val) {
|
||||||
|
return switch_core_strdup(conference->pool, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* create a new conferene with a specific profile */
|
/* create a new conferene with a specific profile */
|
||||||
conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_core_session_t *session, switch_memory_pool_t *pool)
|
conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_core_session_t *session, switch_memory_pool_t *pool)
|
||||||
{
|
{
|
||||||
@ -3070,7 +3095,7 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
|
|||||||
conference->broadcast_chat_messages = broadcast_chat_messages;
|
conference->broadcast_chat_messages = broadcast_chat_messages;
|
||||||
conference->video_quality = conference_video_quality;
|
conference->video_quality = conference_video_quality;
|
||||||
conference->auto_kps_debounce = auto_kps_debounce;
|
conference->auto_kps_debounce = auto_kps_debounce;
|
||||||
|
switch_event_create_plain(&conference->variables, SWITCH_EVENT_CHANNEL_DATA);
|
||||||
conference->conference_video_mode = conference_video_mode;
|
conference->conference_video_mode = conference_video_mode;
|
||||||
|
|
||||||
conference->scale_h264_canvas_width = scale_h264_canvas_width;
|
conference->scale_h264_canvas_width = scale_h264_canvas_width;
|
||||||
@ -3567,7 +3592,7 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
|
|||||||
|
|
||||||
if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
|
if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
|
||||||
video_layout_t *vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group);
|
video_layout_t *vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group);
|
||||||
|
|
||||||
if (!vlayout) {
|
if (!vlayout) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot find layout\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot find layout\n");
|
||||||
conference->video_layout_name = conference->video_layout_group = NULL;
|
conference->video_layout_name = conference->video_layout_group = NULL;
|
||||||
@ -3598,6 +3623,28 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
|
|||||||
switch_mutex_unlock(conference->canvas_mutex);
|
switch_mutex_unlock(conference->canvas_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cfg.profile) {
|
||||||
|
for (xml_kvp = switch_xml_child(cfg.profile, "video-codec-group"); xml_kvp; xml_kvp = xml_kvp->next) {
|
||||||
|
char *name = (char *) switch_xml_attr_soft(xml_kvp, "name");
|
||||||
|
char *bw = (char *) switch_xml_attr_soft(xml_kvp, "bandwidth");
|
||||||
|
char *fps = (char *) switch_xml_attr_soft(xml_kvp, "fps-divisor");
|
||||||
|
char *res = (char *) switch_xml_attr_soft(xml_kvp, "res-divisor");
|
||||||
|
|
||||||
|
if (name && bw) {
|
||||||
|
const char *str = switch_core_sprintf(conference->pool, "%s%s%s%s%s", bw,
|
||||||
|
!zstr(res) ? ":" : "", res, !zstr(fps) ? ":" : "", fps);
|
||||||
|
|
||||||
|
const char *gname = switch_core_sprintf(conference->pool, "group-%s", name);
|
||||||
|
|
||||||
|
conference_set_variable(conference, gname, str);
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video codec group preset %s set to [%s]\n", gname, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
|
|
||||||
#define CONFFUNCAPISIZE (sizeof(conference_api_sub_commands)/sizeof(conference_api_sub_commands[0]))
|
#define CONFFUNCAPISIZE (sizeof(conference_api_sub_commands)/sizeof(conference_api_sub_commands[0]))
|
||||||
|
|
||||||
#define MAX_MUX_CODECS 10
|
#define MAX_MUX_CODECS 50
|
||||||
|
|
||||||
#define ALC_HRTF_SOFT 0x1992
|
#define ALC_HRTF_SOFT 0x1992
|
||||||
|
|
||||||
@ -521,6 +521,17 @@ typedef struct layout_group_s {
|
|||||||
video_layout_node_t *layouts;
|
video_layout_node_t *layouts;
|
||||||
} layout_group_t;
|
} layout_group_t;
|
||||||
|
|
||||||
|
typedef struct codec_set_s {
|
||||||
|
switch_codec_t codec;
|
||||||
|
switch_frame_t frame;
|
||||||
|
uint8_t *packet;
|
||||||
|
switch_image_t *scaled_img;
|
||||||
|
uint8_t fps_divisor;
|
||||||
|
uint32_t frame_count;
|
||||||
|
char *video_codec_group;
|
||||||
|
} codec_set_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct mcu_canvas_s {
|
typedef struct mcu_canvas_s {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
@ -549,13 +560,14 @@ typedef struct mcu_canvas_s {
|
|||||||
switch_thread_t *video_muxing_thread;
|
switch_thread_t *video_muxing_thread;
|
||||||
int video_timer_reset;
|
int video_timer_reset;
|
||||||
switch_queue_t *video_queue;
|
switch_queue_t *video_queue;
|
||||||
int32_t video_write_bandwidth;
|
|
||||||
int recording;
|
int recording;
|
||||||
switch_image_t *bgimg;
|
switch_image_t *bgimg;
|
||||||
switch_image_t *fgimg;
|
switch_image_t *fgimg;
|
||||||
switch_thread_rwlock_t *video_rwlock;
|
switch_thread_rwlock_t *video_rwlock;
|
||||||
int playing_video_file;
|
int playing_video_file;
|
||||||
int overlay_video_file;
|
int overlay_video_file;
|
||||||
|
codec_set_t *write_codecs[MAX_MUX_CODECS];
|
||||||
|
int write_codecs_count;
|
||||||
} mcu_canvas_t;
|
} mcu_canvas_t;
|
||||||
|
|
||||||
/* Record Node */
|
/* Record Node */
|
||||||
@ -620,6 +632,7 @@ typedef struct conference_obj {
|
|||||||
char *video_letterbox_bgcolor;
|
char *video_letterbox_bgcolor;
|
||||||
char *video_mute_banner;
|
char *video_mute_banner;
|
||||||
char *no_video_avatar;
|
char *no_video_avatar;
|
||||||
|
switch_event_t *variables;
|
||||||
conference_video_mode_t conference_video_mode;
|
conference_video_mode_t conference_video_mode;
|
||||||
int video_quality;
|
int video_quality;
|
||||||
int members_with_video;
|
int members_with_video;
|
||||||
@ -847,6 +860,7 @@ struct conference_member {
|
|||||||
char *video_mute_png;
|
char *video_mute_png;
|
||||||
char *video_reservation_id;
|
char *video_reservation_id;
|
||||||
char *video_role_id;
|
char *video_role_id;
|
||||||
|
char *video_codec_group;
|
||||||
switch_vid_params_t vid_params;
|
switch_vid_params_t vid_params;
|
||||||
uint32_t auto_kps_debounce_ticks;
|
uint32_t auto_kps_debounce_ticks;
|
||||||
uint32_t layer_loops;
|
uint32_t layer_loops;
|
||||||
@ -897,15 +911,6 @@ typedef struct api_command {
|
|||||||
char *psyntax;
|
char *psyntax;
|
||||||
} api_command_t;
|
} api_command_t;
|
||||||
|
|
||||||
typedef struct codec_set_s {
|
|
||||||
switch_codec_t codec;
|
|
||||||
switch_frame_t frame;
|
|
||||||
uint8_t *packet;
|
|
||||||
switch_image_t *scaled_img;
|
|
||||||
uint8_t fps_divisor;
|
|
||||||
uint32_t frame_count;
|
|
||||||
} codec_set_t;
|
|
||||||
|
|
||||||
typedef void (*conference_key_callback_t) (conference_member_t *, struct caller_control_actions *);
|
typedef void (*conference_key_callback_t) (conference_member_t *, struct caller_control_actions *);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -1200,6 +1205,7 @@ switch_status_t conference_api_sub_vid_role_id(conference_member_t *member, swit
|
|||||||
switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
||||||
switch_status_t conference_api_sub_get(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
switch_status_t conference_api_sub_get(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
||||||
switch_status_t conference_api_sub_vid_mute_img(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
switch_status_t conference_api_sub_vid_mute_img(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
||||||
|
switch_status_t conference_api_sub_vid_codec_group(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
||||||
switch_status_t conference_api_sub_vid_logo_img(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
switch_status_t conference_api_sub_vid_logo_img(conference_member_t *member, switch_stream_handle_t *stream, void *data);
|
||||||
switch_status_t conference_api_sub_vid_fps(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
switch_status_t conference_api_sub_vid_fps(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
||||||
switch_status_t conference_api_sub_canvas_fgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
switch_status_t conference_api_sub_canvas_fgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv);
|
||||||
@ -1262,7 +1268,8 @@ void conference_loop_exec_app(conference_member_t *member, caller_control_action
|
|||||||
void conference_loop_deaf_toggle(conference_member_t *member, caller_control_action_t *action);
|
void conference_loop_deaf_toggle(conference_member_t *member, caller_control_action_t *action);
|
||||||
void conference_loop_deaf_on(conference_member_t *member, caller_control_action_t *action);
|
void conference_loop_deaf_on(conference_member_t *member, caller_control_action_t *action);
|
||||||
void conference_loop_deaf_off(conference_member_t *member, caller_control_action_t *action);
|
void conference_loop_deaf_off(conference_member_t *member, caller_control_action_t *action);
|
||||||
|
void conference_set_variable(conference_obj_t *conference, const char *var, const char *val);
|
||||||
|
const char *conference_get_variable(conference_obj_t *conference, const char *var);
|
||||||
|
|
||||||
|
|
||||||
/* Global Structs */
|
/* Global Structs */
|
||||||
|
@ -41,7 +41,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime);
|
|||||||
SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime);
|
SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime);
|
||||||
|
|
||||||
#define EP_NAME "verto.rtc"
|
#define EP_NAME "verto.rtc"
|
||||||
#define WSS_STANDALONE 1
|
//#define WSS_STANDALONE 1
|
||||||
#include "ws.h"
|
#include "ws.h"
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
Loading…
x
Reference in New Issue
Block a user