From da6b9e001cf41d935c07e029eff4641a4c5935bb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 30 Nov 2016 18:17:47 -0600 Subject: [PATCH] FS-9742: [mod_conference,mod_cv] Refactor canvas zoom code #resolve --- html5/verto/js/Makefile | 2 + html5/verto/js/src/jquery.verto.js | 20 + html5/verto/video_demo/js/verto-min.js | 7 +- src/include/switch_frame.h | 7 +- .../mod_conference/conference_api.c | 257 ++++++++++++ .../mod_conference/conference_event.c | 153 ++++++- .../mod_conference/conference_video.c | 394 +++++++++++++++--- .../mod_conference/mod_conference.c | 52 +-- .../mod_conference/mod_conference.h | 36 +- src/mod/applications/mod_cv/mod_cv.cpp | 37 +- src/mod/endpoints/mod_verto/mod_verto.c | 12 +- 11 files changed, 868 insertions(+), 109 deletions(-) diff --git a/html5/verto/js/Makefile b/html5/verto/js/Makefile index 05aa6617b0..ac2fcc9cd4 100644 --- a/html5/verto/js/Makefile +++ b/html5/verto/js/Makefile @@ -22,6 +22,8 @@ install-maxdemo: all verto-max.js install-video_demo: all cp verto-min.js ../video_demo/js + cp verto-min.js ../video_demo-live_canvas/js install-video_maxdemo: all verto-max.js cp verto-max.js ../video_demo/js/verto-min.js + cp verto-max.js ../video_demo-live_canvas/js/verto-min.js diff --git a/html5/verto/js/src/jquery.verto.js b/html5/verto/js/src/jquery.verto.js index 1c33b1392f..cff5bf1b41 100644 --- a/html5/verto/js/src/jquery.verto.js +++ b/html5/verto/js/src/jquery.verto.js @@ -1247,6 +1247,14 @@ } }); + verto.subscribe(conf.params.laData.infoChannel, { + handler: function(v, e) { + if (typeof(conf.params.infoCallback) === "function") { + conf.params.infoCallback(v,e); + } + } + }); + verto.subscribe(conf.params.laData.chatChannel, { handler: function(v, e) { if (typeof(conf.params.chatCallback) === "function") { @@ -1283,6 +1291,10 @@ if (conf.params.laData.chatChannel) { conf.verto.unsubscribe(conf.params.laData.chatChannel); } + + if (conf.params.laData.infoChannel) { + conf.verto.unsubscribe(conf.params.laData.infoChannel); + } }; function createMainModeratorMethods() { @@ -1684,6 +1696,14 @@ //$(".jsDataTable").width(confMan.params.hasVid ? "900px" : "800px"); + verto.subscribe(confMan.params.laData.infoChannel, { + handler: function(v, e) { + if (typeof(confMan.params.infoCallback) === "function") { + confMan.params.infoCallback(v,e); + } + } + }); + verto.subscribe(confMan.params.laData.chatChannel, { handler: function(v, e) { if (typeof(confMan.params.chatCallback) === "function") { diff --git a/html5/verto/video_demo/js/verto-min.js b/html5/verto/video_demo/js/verto-min.js index e7e1782a92..ca36b1f470 100644 --- a/html5/verto/video_demo/js/verto-min.js +++ b/html5/verto/video_demo/js/verto-min.js @@ -213,8 +213,9 @@ dt.fnClearTable();dt.fnAddData(genArray(obj));dt.fnAdjustColumnSizing();break;ca if(args.redraw>-1){dt.fnClearTable();dt.fnAddData(genArray(obj));}else{dt.fnAddData(genRow(args.data));} dt.fnAdjustColumnSizing();break;case"modify":if(!args.data){return;} dt.fnUpdate(genRow(args.data),index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(genArray(obj));break;case"hide":jq.hide();break;case"show":jq.show();break;}}catch(err){console.error("ERROR: "+err);iserr++;} -if(iserr){obj.errs++;if(obj.errs<3){obj.bootstrap(obj.user_obj);}}else{obj.errs=0;}};la.onChange(la,{action:"init"});};var CONFMAN_SERNO=1;$.verto.conf=function(verto,params){var conf=this;conf.params=$.extend({dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);conf.verto=verto;conf.serno=CONFMAN_SERNO++;createMainModeratorMethods();verto.subscribe(conf.params.laData.modChannel,{handler:function(v,e){if(conf.params.onBroadcast){conf.params.onBroadcast(verto,conf,e.data);}}});verto.subscribe(conf.params.laData.chatChannel,{handler:function(v,e){if(typeof(conf.params.chatCallback)==="function"){conf.params.chatCallback(v,e);}}});};$.verto.conf.prototype.modCommand=function(cmd,id,value){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.modChannel,"data":{"application":"conf-control","command":cmd,"id":id,"value":value}});};$.verto.conf.prototype.destroy=function(){var conf=this;conf.destroyed=true;conf.params.onBroadcast(conf.verto,conf,'destroy');if(conf.params.laData.modChannel){conf.verto.unsubscribe(conf.params.laData.modChannel);} -if(conf.params.laData.chatChannel){conf.verto.unsubscribe(conf.params.laData.chatChannel);}};function createMainModeratorMethods(){$.verto.conf.prototype.listVideoLayouts=function(){this.modCommand("list-videoLayouts",null,null);};$.verto.conf.prototype.play=function(file){this.modCommand("play",null,file);};$.verto.conf.prototype.stop=function(){this.modCommand("stop",null,"all");};$.verto.conf.prototype.deaf=function(memberID){this.modCommand("deaf",parseInt(memberID));};$.verto.conf.prototype.undeaf=function(memberID){this.modCommand("undeaf",parseInt(memberID));};$.verto.conf.prototype.record=function(file){this.modCommand("recording",null,["start",file]);};$.verto.conf.prototype.stopRecord=function(){this.modCommand("recording",null,["stop","all"]);};$.verto.conf.prototype.snapshot=function(file){if(!this.params.hasVid){throw'Conference has no video';} +if(iserr){obj.errs++;if(obj.errs<3){obj.bootstrap(obj.user_obj);}}else{obj.errs=0;}};la.onChange(la,{action:"init"});};var CONFMAN_SERNO=1;$.verto.conf=function(verto,params){var conf=this;conf.params=$.extend({dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);conf.verto=verto;conf.serno=CONFMAN_SERNO++;createMainModeratorMethods();verto.subscribe(conf.params.laData.modChannel,{handler:function(v,e){if(conf.params.onBroadcast){conf.params.onBroadcast(verto,conf,e.data);}}});verto.subscribe(conf.params.laData.infoChannel,{handler:function(v,e){if(typeof(conf.params.infoCallback)==="function"){conf.params.infoCallback(v,e);}}});verto.subscribe(conf.params.laData.chatChannel,{handler:function(v,e){if(typeof(conf.params.chatCallback)==="function"){conf.params.chatCallback(v,e);}}});};$.verto.conf.prototype.modCommand=function(cmd,id,value){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.modChannel,"data":{"application":"conf-control","command":cmd,"id":id,"value":value}});};$.verto.conf.prototype.destroy=function(){var conf=this;conf.destroyed=true;conf.params.onBroadcast(conf.verto,conf,'destroy');if(conf.params.laData.modChannel){conf.verto.unsubscribe(conf.params.laData.modChannel);} +if(conf.params.laData.chatChannel){conf.verto.unsubscribe(conf.params.laData.chatChannel);} +if(conf.params.laData.infoChannel){conf.verto.unsubscribe(conf.params.laData.infoChannel);}};function createMainModeratorMethods(){$.verto.conf.prototype.listVideoLayouts=function(){this.modCommand("list-videoLayouts",null,null);};$.verto.conf.prototype.play=function(file){this.modCommand("play",null,file);};$.verto.conf.prototype.stop=function(){this.modCommand("stop",null,"all");};$.verto.conf.prototype.deaf=function(memberID){this.modCommand("deaf",parseInt(memberID));};$.verto.conf.prototype.undeaf=function(memberID){this.modCommand("undeaf",parseInt(memberID));};$.verto.conf.prototype.record=function(file){this.modCommand("recording",null,["start",file]);};$.verto.conf.prototype.stopRecord=function(){this.modCommand("recording",null,["stop","all"]);};$.verto.conf.prototype.snapshot=function(file){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("vid-write-png",null,file);};$.verto.conf.prototype.setVideoLayout=function(layout,canvasID){if(!this.params.hasVid){throw'Conference has no video';} if(canvasID){this.modCommand("vid-layout",null,[layout,canvasID]);}else{this.modCommand("vid-layout",null,layout);}};$.verto.conf.prototype.kick=function(memberID){this.modCommand("kick",parseInt(memberID));};$.verto.conf.prototype.muteMic=function(memberID){this.modCommand("tmute",parseInt(memberID));};$.verto.conf.prototype.muteVideo=function(memberID){if(!this.params.hasVid){throw'Conference has no video';} this.modCommand("tvmute",parseInt(memberID));};$.verto.conf.prototype.presenter=function(memberID){if(!this.params.hasVid){throw'Conference has no video';} @@ -230,7 +231,7 @@ html+="
"+""+" jq.html(html);if(!jq.data("mouse")){$("#"+box_id).hide();} jq.mouseover(function(e){jq.data({"mouse":true});$("#"+box_id).show();});jq.mouseout(function(e){jq.data({"mouse":false});$("#"+box_id).hide();});$("#"+transfer_id).click(function(){var xten=prompt("Enter Extension");if(xten){confMan.modCommand("transfer",x,xten);}});$("#"+kick_id).click(function(){confMan.modCommand("kick",x);});$("#"+layer_set_id).click(function(){var cid=prompt("Please enter layer ID","");if(cid){confMan.modCommand("vid-layer",x,cid);}});$("#"+layer_next_id).click(function(){confMan.modCommand("vid-layer",x,"next");});$("#"+layer_prev_id).click(function(){confMan.modCommand("vid-layer",x,"prev");});$("#"+canvas_in_set_id).click(function(){var cid=prompt("Please enter canvas ID","");if(cid){confMan.modCommand("vid-canvas",x,cid);}});$("#"+canvas_out_set_id).click(function(){var cid=prompt("Please enter canvas ID","");if(cid){confMan.modCommand("vid-watching-canvas",x,cid);}});$("#"+canvas_in_next_id).click(function(){confMan.modCommand("vid-canvas",x,"next");});$("#"+canvas_in_prev_id).click(function(){confMan.modCommand("vid-canvas",x,"prev");});$("#"+canvas_out_next_id).click(function(){confMan.modCommand("vid-watching-canvas",x,"next");});$("#"+canvas_out_prev_id).click(function(){confMan.modCommand("vid-watching-canvas",x,"prev");});$("#"+tmute_id).click(function(){confMan.modCommand("tmute",x);});if(confMan.params.hasVid){$("#"+tvmute_id).click(function(){confMan.modCommand("tvmute",x);});$("#"+tvpresenter_id).click(function(){confMan.modCommand("vid-res-id",x,"presenter");});$("#"+tvfloor_id).click(function(){confMan.modCommand("vid-floor",x,"force");});$("#"+vbanner_id).click(function(){var text=prompt("Please enter text","");if(text){confMan.modCommand("vid-banner",x,escape(text));}});} $("#"+gainup_id).click(function(){confMan.modCommand("volume_in",x,"up");});$("#"+gaindn_id).click(function(){confMan.modCommand("volume_in",x,"down");});$("#"+volup_id).click(function(){confMan.modCommand("volume_out",x,"up");});$("#"+voldn_id).click(function(){confMan.modCommand("volume_out",x,"down");});return html;} -var atitle="";var awidth=0;verto.subscribe(confMan.params.laData.chatChannel,{handler:function(v,e){if(typeof(confMan.params.chatCallback)==="function"){confMan.params.chatCallback(v,e);}}});if(confMan.params.laData.role==="moderator"){atitle="Action";awidth=600;if(confMan.params.mainModID){genMainMod($(confMan.params.mainModID));$(confMan.params.displayID).html("Moderator Controls Ready

");}else{$(confMan.params.mainModID).html("");} +var atitle="";var awidth=0;verto.subscribe(confMan.params.laData.infoChannel,{handler:function(v,e){if(typeof(confMan.params.infoCallback)==="function"){confMan.params.infoCallback(v,e);}}});verto.subscribe(confMan.params.laData.chatChannel,{handler:function(v,e){if(typeof(confMan.params.chatCallback)==="function"){confMan.params.chatCallback(v,e);}}});if(confMan.params.laData.role==="moderator"){atitle="Action";awidth=600;if(confMan.params.mainModID){genMainMod($(confMan.params.mainModID));$(confMan.params.displayID).html("Moderator Controls Ready

");}else{$(confMan.params.mainModID).html("");} verto.subscribe(confMan.params.laData.modChannel,{handler:function(v,e){if(confMan.params.onBroadcast){confMan.params.onBroadcast(verto,confMan,e.data);} if(e.data["conf-command"]==="list-videoLayouts"){for(var j=0;j []"}, {"say", (void_fn_t) & conference_api_sub_say, CONF_API_SUB_ARGS_AS_ONE, "say", ""}, {"saymember", (void_fn_t) & conference_api_sub_saymember, CONF_API_SUB_ARGS_AS_ONE, "saymember", " "}, + {"cam", (void_fn_t) & conference_api_sub_cam, CONF_API_SUB_ARGS_SPLIT, "cam", ""}, {"stop", (void_fn_t) & conference_api_sub_stop, CONF_API_SUB_ARGS_SPLIT, "stop", "<[current|all|async|last]> []"}, {"dtmf", (void_fn_t) & conference_api_sub_dtmf, CONF_API_SUB_MEMBER_TARGET, "dtmf", "<[member_id|all|last|non_moderator]> "}, {"kick", (void_fn_t) & conference_api_sub_kick, CONF_API_SUB_MEMBER_TARGET, "kick", "<[member_id|all|last|non_moderator]> []"}, @@ -2009,6 +2010,262 @@ switch_status_t conference_api_sub_saymember(conference_obj_t *conference, switc return ret_status; } +switch_status_t conference_api_sub_cam(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) +{ + int x; + int canvas_id = -1; + int layer_id = -1; + int ok = 0; + mcu_canvas_t *canvas = NULL; + mcu_layer_t *layer = NULL; + + if (!conference->canvases[0]) { + stream->write_function(stream, "Conference is not in mixing mode\n"); + return SWITCH_STATUS_SUCCESS; + } + + if (argc > 4) { + canvas_id = atoi(argv[2]); + layer_id = atoi(argv[3]); + + if (canvas_id > -1 && layer_id > -1 && canvas_id < conference->canvas_count) { + switch_mutex_lock(conference->canvas_mutex); + canvas = conference->canvases[canvas_id]; + switch_mutex_lock(canvas->mutex); + if (layer_id < canvas->total_layers) { + layer = &canvas->layers[layer_id]; + ok = 1; + for (x = 4; x < argc; x++) { + char *p = strchr(argv[x], '='); + int val = -1, isfalse = 0; + char *str_arg = NULL; + + if (p) { + *p++ = '\0'; + if (!p) p = ""; + + if (!strcasecmp(argv[x], "zoom") || !strcasecmp(argv[x], "pan")) { + str_arg = p; + if (switch_false(p)) { + isfalse = 1; + } + } else { + if (switch_is_number(p)) { + val = atoi(p); + } else if (switch_true(p)) { + val = 1; + } else { + val = 0; + isfalse = 1; + } + } + } else if (!strcasecmp(argv[x], "reset")) { + str_arg = "true"; + } + + if (val < 0 && !str_arg) { + stream->write_function(stream, "-ERR invalid val for option [%s]\n", argv[x]); + continue; + } + + if (!strcasecmp(argv[x], "autozoom")) { + if ((layer->cam_opts.autozoom = val)) { + layer->cam_opts.manual_zoom = 0; + } + + } else if (!strcasecmp(argv[x], "autopan")) { + if ((layer->cam_opts.autopan = val)) { + layer->cam_opts.manual_pan = 0; + } + } else if (!strcasecmp(argv[x], "zoom_factor")) { + if (val > 0 && val < 5) { + layer->cam_opts.zoom_factor = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x]); + } + } else if (!strcasecmp(argv[x], "snap_factor")) { + if (val > 0 && val < layer->screen_w / 2) { + layer->cam_opts.snap_factor = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-%d\n", argv[x], layer->screen_w / 2); + } + } else if (!strcasecmp(argv[x], "zoom_move_factor")) { + if (val > 0 && val < layer->screen_w / 2) { + layer->cam_opts.zoom_move_factor = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x], layer->screen_w / 2); + } + } else if (!strcasecmp(argv[x], "pan_speed")) { + if (val > 0 && val < 100) { + layer->cam_opts.pan_speed = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "pan_accel_speed")) { + if (val > 0 && val < 100) { + layer->cam_opts.pan_accel_speed = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "pan_accel_min")) { + if (val > 0 && val < 100) { + layer->cam_opts.pan_accel_min = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "zoom_speed")) { + if (val > 0 && val < 100) { + layer->cam_opts.zoom_speed = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "zoom_accel_speed")) { + if (val > 0 && val < 100) { + layer->cam_opts.zoom_accel_speed = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "zoom_accel_min")) { + if (val > 0 && val < 100) { + layer->cam_opts.zoom_accel_min = val; + } else { + stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n"); + } + } else if (!strcasecmp(argv[x], "reset")) { + conference_video_reset_layer_cam(layer); + } else if (!strcasecmp(argv[x], "pan")) { + char *x_val = NULL, *y_val = NULL; + int x = -1, y = -1; + int on = 0; + + if (isfalse) { + layer->pan_geometry.x = 0; + layer->pan_geometry.y = 0; + layer->cam_opts.manual_pan = 0; + } else { + + if (str_arg) { + char *p = strchr(str_arg, ':'); + + if (p) { + *p++ = '\0'; + + if (*str_arg == 'x') { + x_val = p; + } else if (*str_arg == 'y') { + y_val = p; + } + } + } + + if (!x_val && !y_val) { + stream->write_function(stream, "-ERR invalid val for pan\n"); + } + + if (x_val) x = atoi(x_val); + if (y_val) y = atoi(y_val); + + if (x_val && strrchr(x_val, 'i')) { + int nx = (int)layer->pan_geometry.x + x; + + if (nx < 0) nx = 0; + if (nx + layer->pan_geometry.w > layer->img->d_w) nx = layer->img->d_w - layer->pan_geometry.w; + + layer->pan_geometry.x = nx; + on++; + } else if (x > -1) { + layer->pan_geometry.x = x; + on++; + } + + if (y_val && strrchr(y_val, 'i')) { + int ny = (int)layer->pan_geometry.y + y; + + if (ny < 0) ny = 0; + if (ny + layer->pan_geometry.h > layer->img->d_h) ny = layer->img->d_h - layer->pan_geometry.h; + + layer->pan_geometry.y = ny; + on++; + } else if (y > -1) { + layer->pan_geometry.y = y; + on++; + } + + + if (on) { + layer->cam_opts.manual_pan = 1; + layer->cam_opts.autopan = 0; + stream->write_function(stream, "+OK PAN %d,%d\n", layer->pan_geometry.x, layer->pan_geometry.y); + } + } + + + } else if (!strcasecmp(argv[x], "zoom")) { + if (str_arg && !isfalse) { + char *array[4] = {0}; + int iray[4] = {0}; + int ac; + + if ((ac = switch_split(str_arg, ':', array)) >= 3) { + int i; + + for (i = 0; i < ac; i++) { + int tmp = atoi(array[i]); + + if (tmp < 0) break; + iray[i] = tmp; + } + + if (i == ac) { + layer->cam_opts.manual_zoom = 1; + layer->cam_opts.autozoom = 0; + + layer->zoom_geometry.x = iray[0]; + layer->zoom_geometry.y = iray[1]; + layer->zoom_geometry.w = iray[2]; + if (iray[3]) { + layer->zoom_geometry.h = iray[3]; + } else { + layer->zoom_geometry.h = iray[2]; + } + + layer->crop_x = iray[0]; + layer->crop_y = iray[1]; + layer->crop_w = iray[2]; + layer->crop_h = iray[2]; + layer->pan_geometry = layer->zoom_geometry; + } else { + ok = 0; + } + } + } else { + layer->zoom_geometry.x = 0; + layer->zoom_geometry.y = 0; + layer->zoom_geometry.w = 0; + layer->zoom_geometry.h = 0; + layer->cam_opts.manual_zoom = 0; + } + } else { + stream->write_function(stream, "-ERR invalid option [%s]\n", argv[x]); + } + } + } + switch_mutex_unlock(canvas->mutex); + switch_mutex_unlock(conference->canvas_mutex); + + } + } + + if (ok) { + stream->write_function(stream, "+OK\n"); + } else { + stream->write_function(stream, "-ERR invalid args\n"); + } + + + return SWITCH_STATUS_SUCCESS; +} + switch_status_t conference_api_sub_stop(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) { uint8_t current = 0, all = 0, async = 0; diff --git a/src/mod/applications/mod_conference/conference_event.c b/src/mod/applications/mod_conference/conference_event.c index 79f487fae9..6325311c32 100644 --- a/src/mod/applications/mod_conference/conference_event.c +++ b/src/mod/applications/mod_conference/conference_event.c @@ -237,14 +237,22 @@ void conference_event_mod_channel_handler(const char *event_channel, cJSON *json const void *vvar; cJSON *array = cJSON_CreateArray(); conference_obj_t *conference = NULL; + int i; + if ((conference = conference_find(conference_name, NULL))) { switch_mutex_lock(conference_globals.setup_mutex); + + for (i = 0; i <= conference->canvas_count; i++) { + if (conference->canvases[i]) { + conference_event_adv_layout(conference, conference->canvases[i], conference->canvases[i]->vlayout); + } + } + if (conference->layout_hash) { for (hi = switch_core_hash_first(conference->layout_hash); hi; hi = switch_core_hash_next(&hi)) { video_layout_t *vlayout; cJSON *obj = cJSON_CreateObject(); cJSON *resarray = cJSON_CreateArray(); - int i; switch_core_hash_this(hi, &vvar, NULL, &val); vlayout = (video_layout_t *)val; @@ -292,6 +300,83 @@ void conference_event_mod_channel_handler(const char *event_channel, cJSON *json switch_thread_rwlock_unlock(conference->rwlock); } addobj = array; + } else if (!strcasecmp(action, "click-layer")) { + } else if (!strcasecmp(action, "shift-click-layer")) { + } else if (!strcasecmp(action, "reset-layer") || !strcasecmp(action, "layer-pan-x") || !strcasecmp(action, "layer-pan-y")) { + cJSON *v; + int layer_id = 0, canvas_id = 0, metric = 0, absolute = 0; + const char *i = "i", *xy = ""; + + if ((v = cJSON_GetObjectItem(data, "layerID"))) { + layer_id = v->valueint; + } + + if ((v = cJSON_GetObjectItem(data, "canvasID"))) { + canvas_id = v->valueint; + } + + if ((v = cJSON_GetObjectItem(data, "metric"))) { + metric = v->valueint; + } + + if ((v = cJSON_GetObjectItem(data, "absolute"))) { + if ((absolute = v->valueint)) { + i = ""; + } + } + + if (canvas_id > -1 && layer_id > -1) { + if (!strcasecmp(action, "layer-pan-x")) { + xy = "x"; + } else if (!strcasecmp(action, "layer-pan-y")) { + xy = "y"; + } + + if (!strcasecmp(action, "reset-layer")) { + exec = switch_mprintf("%s cam %d %d reset", conference_name, canvas_id, layer_id); + } else { + exec = switch_mprintf("%s cam %d %d pan=%s:%d%s", conference_name, canvas_id, layer_id, xy, metric, i); + } + } + + + } else if (!strcasecmp(action, "zoom-layer")) { + cJSON *v; + int layer_id = -1, canvas_id = -1, x = -1, y = -1, w = -1, h = -1; + + if ((v = cJSON_GetObjectItem(data, "layerID"))) { + layer_id = v->valueint; + } + + if ((v = cJSON_GetObjectItem(data, "canvasID"))) { + canvas_id = v->valueint; + } + + + if ((v = cJSON_GetObjectItem(data, "dimensions"))) { + cJSON *d; + + if ((d = cJSON_GetObjectItem(v, "w"))) { + w = d->valueint; + } + + if ((d = cJSON_GetObjectItem(v, "h"))) { + h = d->valueint; + } + + if ((d = cJSON_GetObjectItem(v, "x"))) { + x = d->valueint; + } + + if ((d = cJSON_GetObjectItem(v, "y"))) { + y = d->valueint; + } + } + + if (canvas_id > -1 && layer_id > -1 && x > -1 && y > -1 && w > -1 && h > -1) { + exec = switch_mprintf("%s cam %d %d zoom=%d:%d:%d:%d snap_factor=1 zoom_factor=1", conference_name, canvas_id, layer_id, x, y, w, h); + } + } if (exec) { @@ -486,6 +571,59 @@ void conference_event_la_command_handler(switch_live_array_t *la, const char *cm { } +void conference_event_adv_layout(conference_obj_t *conference, mcu_canvas_t *canvas, video_layout_t *vlayout) +{ + cJSON *msg, *data, *obj; + int i = 0; + + msg = cJSON_CreateObject(); + data = json_add_child_obj(msg, "eventData", NULL); + + cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(conference->info_event_channel)); + cJSON_AddItemToObject(data, "contentType", cJSON_CreateString("layout-info")); + + switch_thread_rwlock_rdlock(canvas->video_rwlock); + switch_mutex_lock(canvas->mutex); + + if ((obj = get_canvas_info(canvas))) { + cJSON *array = cJSON_CreateArray(); + + for (i = 0; i < vlayout->layers; i++) { + cJSON *layout = cJSON_CreateObject(); + int scale = vlayout->images[i].scale; + int hscale = vlayout->images[i].hscale ? vlayout->images[i].hscale : scale; + + cJSON_AddItemToObject(layout, "x", cJSON_CreateNumber(vlayout->images[i].x)); + cJSON_AddItemToObject(layout, "y", cJSON_CreateNumber(vlayout->images[i].y)); + cJSON_AddItemToObject(layout, "scale", cJSON_CreateNumber(vlayout->images[i].scale)); + cJSON_AddItemToObject(layout, "hscale", cJSON_CreateNumber(hscale)); + cJSON_AddItemToObject(layout, "scale", cJSON_CreateNumber(scale)); + cJSON_AddItemToObject(layout, "zoom", cJSON_CreateNumber(vlayout->images[i].zoom)); + cJSON_AddItemToObject(layout, "border", cJSON_CreateNumber(vlayout->images[i].border)); + cJSON_AddItemToObject(layout, "floor", cJSON_CreateNumber(vlayout->images[i].floor)); + cJSON_AddItemToObject(layout, "overlap", cJSON_CreateNumber(vlayout->images[i].overlap)); + cJSON_AddItemToObject(layout, "screenWidth", cJSON_CreateNumber((uint32_t)(canvas->img->d_w * scale / VIDEO_LAYOUT_SCALE))); + cJSON_AddItemToObject(layout, "screenHeight", cJSON_CreateNumber((uint32_t)(canvas->img->d_h * hscale / VIDEO_LAYOUT_SCALE))); + cJSON_AddItemToObject(layout, "xPOS", cJSON_CreateNumber((int)(canvas->img->d_w * vlayout->images[i].x / VIDEO_LAYOUT_SCALE))); + cJSON_AddItemToObject(layout, "yPOS", cJSON_CreateNumber((int)(canvas->img->d_h * vlayout->images[i].y / VIDEO_LAYOUT_SCALE))); + cJSON_AddItemToObject(layout, "resID", cJSON_CreateString(vlayout->images[i].res_id)); + cJSON_AddItemToObject(layout, "audioPOS", cJSON_CreateString(vlayout->images[i].audio_position)); + cJSON_AddItemToArray(array, layout); + } + + + cJSON_AddItemToObject(obj, "canvasLayouts", array); + + cJSON_AddItemToObject(obj, "scale", cJSON_CreateNumber(VIDEO_LAYOUT_SCALE)); + cJSON_AddItemToObject(data, "canvasInfo", obj); + } + + switch_mutex_unlock(canvas->mutex); + switch_thread_rwlock_unlock(canvas->video_rwlock); + + switch_event_channel_broadcast(conference->info_event_channel, &msg, "mod_conference", conference_globals.event_channel_id); + +} void conference_event_adv_la(conference_obj_t *conference, conference_member_t *member, switch_bool_t join) { @@ -501,6 +639,7 @@ void conference_event_adv_la(conference_obj_t *conference, conference_member_t * switch_event_t *variables; switch_event_header_t *hp; char idstr[128] = ""; + int i; snprintf(idstr, sizeof(idstr), "%d", member->id); msg = cJSON_CreateObject(); @@ -526,6 +665,7 @@ void conference_event_adv_la(conference_obj_t *conference, conference_member_t * } cJSON_AddItemToObject(data, "chatChannel", cJSON_CreateString(conference->chat_event_channel)); + cJSON_AddItemToObject(data, "infoChannel", cJSON_CreateString(conference->info_event_channel)); switch_core_get_variables(&variables); for (hp = variables->headers; hp; hp = hp->next) { @@ -538,12 +678,19 @@ void conference_event_adv_la(conference_obj_t *conference, conference_member_t * } switch_event_destroy(&variables); - switch_event_channel_broadcast(event_channel, &msg, "mod_conference", conference_globals.event_channel_id); - if (cookie) { switch_event_channel_permission_modify(cookie, conference->la_event_channel, join); switch_event_channel_permission_modify(cookie, conference->mod_event_channel, join); switch_event_channel_permission_modify(cookie, conference->chat_event_channel, join); + switch_event_channel_permission_modify(cookie, conference->info_event_channel, join); + } + + switch_event_channel_broadcast(event_channel, &msg, "mod_conference", conference_globals.event_channel_id); + + for (i = 0; i <= conference->canvas_count; i++) { + if (conference->canvases[i]) { + conference_event_adv_layout(conference, conference->canvases[i], conference->canvases[i]->vlayout); + } } } } diff --git a/src/mod/applications/mod_conference/conference_video.c b/src/mod/applications/mod_conference/conference_video.c index 8d0d0e98a0..6c8e744f22 100644 --- a/src/mod/applications/mod_conference/conference_video.c +++ b/src/mod/applications/mod_conference/conference_video.c @@ -349,6 +349,45 @@ void conference_video_clear_layer(mcu_layer_t *layer) } +static void set_default_cam_opts(mcu_layer_t *layer) +{ + //layer->cam_opts.autozoom = 1; + //layer->cam_opts.autopan = 1; + layer->cam_opts.manual_pan = 0; + layer->cam_opts.manual_zoom = 0; + layer->cam_opts.zoom_factor = 3; + layer->cam_opts.snap_factor = 25; + layer->cam_opts.zoom_move_factor = 125; + layer->cam_opts.pan_speed = 3; + layer->cam_opts.pan_accel_speed = 10; + layer->cam_opts.pan_accel_min = 50; + layer->cam_opts.zoom_speed = 3; + layer->cam_opts.zoom_accel_speed = 10; + layer->cam_opts.zoom_accel_min = 50; +} + + +void conference_video_reset_layer_cam(mcu_layer_t *layer) +{ + layer->crop_x = 0; + layer->crop_y = 0; + layer->crop_w = 0; + layer->crop_h = 0; + layer->last_w = 0; + layer->last_h = 0; + layer->img_count = 0; + + memset(&layer->bug_frame, 0, sizeof(layer->bug_frame)); + memset(&layer->auto_geometry, 0, sizeof(layer->auto_geometry)); + memset(&layer->pan_geometry, 0, sizeof(layer->pan_geometry)); + memset(&layer->zoom_geometry, 0, sizeof(layer->zoom_geometry)); + memset(&layer->last_geometry, 0, sizeof(layer->last_geometry)); + + set_default_cam_opts(layer); + + +} + void conference_video_reset_layer(mcu_layer_t *layer) { switch_img_free(&layer->banner_img); @@ -361,7 +400,7 @@ void conference_video_reset_layer(mcu_layer_t *layer) layer->is_avatar = 0; layer->need_patch = 0; - memset(&layer->bug_frame, 0, sizeof(layer->bug_frame)); + conference_video_reset_layer_cam(layer); if (layer->geometry.overlap) { layer->canvas->refresh = 1; @@ -375,36 +414,73 @@ void conference_video_reset_layer(mcu_layer_t *layer) switch_img_free(&layer->cur_img); } -static void set_pan(mcu_layer_t *layer, int crop_point, int max_width) +static void set_pan(int crop_point, int *target_point, int accel_speed, int accel_min, int speed) { - if (layer->crop_point <= 0 || layer->crop_point > max_width) { - layer->crop_point = crop_point; - } else if (crop_point > layer->crop_point) { - if (crop_point - layer->crop_point > 25) { - layer->crop_point += 5; + + if (crop_point > *target_point) { + if ((crop_point - *target_point) > accel_min) { + *target_point += accel_speed; } else { - layer->crop_point++; + *target_point += speed; } - if (crop_point < layer->crop_point) { - layer->crop_point = crop_point; + if (*target_point > crop_point) { + *target_point = crop_point; } - } else if (crop_point < layer->crop_point) { - if (layer->crop_point - crop_point > 25) { - layer->crop_point -= 5; + } else if (crop_point < *target_point) { + + if ((*target_point - crop_point) > accel_min) { + *target_point -= accel_speed; } else { - layer->crop_point--; + *target_point -= speed; } - if (crop_point > layer->crop_point) { - layer->crop_point = crop_point; + if (*target_point < crop_point) { + *target_point = crop_point; } } } +static void set_bounds(int *x, int *y, int img_w, int img_h, int crop_w, int crop_h) +{ + int crop_x = *x; + int crop_y = *y; + + if (crop_x < 0) { + crop_x = 0; + } + + if (crop_y < 0) { + crop_y = 0; + } + + if (crop_x + crop_w > img_w) { + crop_x = img_w - crop_w; + } + + if (crop_y + crop_h > img_h) { + crop_y = img_h - crop_h; + } + + if (crop_x < 0) { + crop_x = 0; + } + + if (crop_y < 0) { + crop_y = 0; + } + + *x = crop_x; + *y = crop_y; + + +} + + void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, switch_bool_t freeze) { switch_image_t *IMG, *img; + int img_changed = 0; switch_mutex_lock(layer->canvas->mutex); @@ -417,6 +493,47 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, switch_mutex_unlock(layer->canvas->mutex); return; } + //printf("RAW %dx%d\n", img->d_w, img->d_h); + + if (layer->img_count++ == 0 || layer->last_w != img->d_w || layer->last_h != img->d_h) { + double change_scale; + + if (img->d_w && layer->last_w) { + if (img->d_w < layer->last_w) { + change_scale = layer->last_w / img->d_w; + } else { + change_scale = img->d_w / layer->last_w; + } + + layer->crop_x = (int)(layer->crop_x * change_scale); + layer->crop_y = (int)(layer->crop_y * change_scale); + layer->crop_w = (int)(layer->crop_w * change_scale); + layer->crop_h = (int)(layer->crop_h * change_scale); + + layer->zoom_geometry.x = (int)(layer->zoom_geometry.x * change_scale); + layer->zoom_geometry.y = (int)(layer->zoom_geometry.y * change_scale); + layer->zoom_geometry.w = (int)(layer->zoom_geometry.w * change_scale); + layer->zoom_geometry.h = (int)(layer->zoom_geometry.h * change_scale); + + + layer->pan_geometry.x = (int)(layer->pan_geometry.x * change_scale); + layer->pan_geometry.y = (int)(layer->pan_geometry.y * change_scale); + layer->pan_geometry.w = (int)(layer->pan_geometry.w * change_scale); + layer->pan_geometry.h = (int)(layer->pan_geometry.h * change_scale); + + } + + memset(&layer->auto_geometry, 0, sizeof(layer->auto_geometry)); + //memset(&layer->zoom_geometry, 0, sizeof(layer->zoom_geometry)); + //memset(&layer->pan_geometry, 0, sizeof(layer->pan_geometry)); + memset(&layer->last_geometry, 0, sizeof(layer->last_geometry)); + + img_changed = 1; + } + + layer->last_w = img->d_w; + layer->last_h = img->d_h; + if (layer->bugged) { if (layer->member_id > -1 && layer->member && switch_thread_rwlock_tryrdlock(layer->member->rwlock) == SWITCH_STATUS_SUCCESS) { @@ -427,7 +544,39 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, switch_thread_rwlock_unlock(layer->member->rwlock); } + if ((!layer->manual_geometry.w || + (layer->last_geometry.x && abs(layer->manual_geometry.x - layer->last_geometry.x) > layer->cam_opts.zoom_move_factor) || + (layer->last_geometry.y && abs(layer->manual_geometry.y - layer->last_geometry.y) > layer->cam_opts.zoom_move_factor) || + (layer->last_geometry.w && abs(layer->manual_geometry.w - layer->last_geometry.w) > layer->cam_opts.zoom_move_factor / 2))) { + switch_event_t *event; + + if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "action", "movement-detection"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "member_id", "%d", layer->member_id); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "last_x", "%d", layer->manual_geometry.x); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "last_y", "%d", layer->manual_geometry.y); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "last_w", "%d", layer->manual_geometry.w); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "last_h", "%d", layer->manual_geometry.h); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "new_x", "%d", layer->bug_frame.geometry.x); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "new_y", "%d", layer->bug_frame.geometry.y); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "new_w", "%d", layer->bug_frame.geometry.w); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "new_h", "%d", layer->bug_frame.geometry.h); + + switch_event_fire(&event); + } + + layer->manual_geometry = layer->bug_frame.geometry; + } + layer->bugged = 0; + } else { + if (layer->bug_frame.geometry.w) { + memset(&layer->bug_frame, 0, sizeof(layer->bug_frame)); + } + layer->cam_opts.autozoom = 0; + layer->cam_opts.autopan = 0; } if (layer->clear) { @@ -446,7 +595,7 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, int x_pos = layer->x_pos; int y_pos = layer->y_pos; switch_size_t img_addr = 0; - + switch_frame_geometry_t *use_geometry = &layer->auto_geometry; img_w = layer->screen_w = (uint32_t)(IMG->d_w * layer->geometry.scale / VIDEO_LAYOUT_SCALE); img_h = layer->screen_h = (uint32_t)(IMG->d_h * layer->geometry.hscale / VIDEO_LAYOUT_SCALE); @@ -456,76 +605,184 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, img_addr = (switch_size_t)img; - if (layer->last_img_addr != img_addr && layer->geometry.zoom) { - uint32_t new_w = 0, new_h = 0; - int crop_point = 0; + + if (layer->last_img_addr != img_addr && (layer->geometry.zoom || layer->cam_opts.autozoom || + layer->cam_opts.autopan || layer->cam_opts.manual_pan || layer->cam_opts.manual_zoom)) { + double scale = 1; + int crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0, zoom_w = 0, zoom_h = 0; + int can_pan = 0; + int can_zoom = 0; + int did_zoom = 0; if (screen_aspect < img_aspect) { - if (img->d_h != layer->screen_h) { scale = (double)layer->screen_h / img->d_h; } - - new_w = (uint32_t)((double)layer->screen_w / scale); - new_h = (uint32_t)((double)layer->screen_h / scale); - - if (layer->bug_frame.geometry.w) { - //new_w = layer->bug_frame.geometry.w * 2; - //new_h = new_w / screen_aspect; - crop_point = switch_round_to_step(layer->bug_frame.geometry.x - (new_w / 2), 25); - } else { - crop_point = (img->d_w - new_w) / 2; - } - - if (crop_point < 1) { - crop_point = 1; - } else if (crop_point > img->d_w - new_w) { - crop_point = img->d_w - new_w; - } - - set_pan(layer, crop_point, img->d_w - new_w); - - if (layer->crop_point > 0) { - switch_img_set_rect(img, layer->crop_point, 0, new_w, new_h); - img_aspect = (double) img->d_w / img->d_h; - } - } else if (screen_aspect > img_aspect) { - if (img->d_w != layer->screen_w) { scale = (double)layer->screen_w / img->d_w; } + } + if (scale == 1) { + crop_w = img->d_w; + crop_h = img->d_h; + } else { + crop_w = (uint32_t)((double)layer->screen_w / scale); + crop_h = (uint32_t)((double)layer->screen_h / scale); + } - new_w = (uint32_t)((double)layer->screen_w / scale); - new_h = (uint32_t)((double)layer->screen_h / scale); + //if (layer->bug_frame.geometry.X > 90) { + // memset(&layer->auto_geometry, 0, sizeof(layer->auto_geometry)); + //} + + if (layer->cam_opts.autopan) { + can_pan = layer->bug_frame.geometry.w && (layer->geometry.zoom || layer->cam_opts.manual_zoom); + } else { + can_pan = layer->cam_opts.manual_pan && (layer->geometry.zoom || layer->cam_opts.manual_zoom); + } - if (layer->bug_frame.geometry.w) { - crop_point = layer->bug_frame.geometry.y - (new_h / 2); + if (layer->cam_opts.autozoom) { + can_zoom = layer->bug_frame.geometry.w; + } else { + can_zoom = layer->cam_opts.manual_zoom && layer->zoom_geometry.w; + } + + //printf("CHECK %d %d,%d %d,%d %d/%d\n", layer->auto_geometry.w, + // layer->last_geometry.x, layer->last_geometry.y, + // layer->auto_geometry.x, layer->auto_geometry.y, + // abs(layer->auto_geometry.x - layer->last_geometry.x), + // abs(layer->auto_geometry.y - layer->last_geometry.y)); + + if ((layer->cam_opts.autozoom || layer->cam_opts.autopan) && + (!layer->auto_geometry.w || + (layer->last_geometry.x && abs(layer->auto_geometry.x - layer->last_geometry.x) > layer->cam_opts.zoom_move_factor) || + (layer->last_geometry.y && abs(layer->auto_geometry.y - layer->last_geometry.y) > layer->cam_opts.zoom_move_factor) || + (layer->last_geometry.w && abs(layer->auto_geometry.w - layer->last_geometry.w) > layer->cam_opts.zoom_move_factor / 2))) { + + layer->auto_geometry = layer->bug_frame.geometry; + } + + if (can_zoom) { + + if (layer->cam_opts.autozoom) { + use_geometry = &layer->auto_geometry; } else { - crop_point = (img->d_h - new_h) / 2; + use_geometry = &layer->zoom_geometry; } - if (crop_point < 1) { - crop_point = 1; - } else if (crop_point > img->d_h - new_h) { - crop_point = img->d_h - new_h; - } + zoom_w = use_geometry->w * layer->cam_opts.zoom_factor; + zoom_h = zoom_w / screen_aspect; + + if (zoom_w < crop_w && zoom_h < crop_h) { + int c_x = use_geometry->x; + int c_y = use_geometry->y; + + crop_w = zoom_w; + crop_h = zoom_h; + + //crop_w = switch_round_to_step(crop_w, layer->cam_opts.snap_factor); + //crop_h = switch_round_to_step(crop_h, layer->cam_opts.snap_factor); - set_pan(layer, crop_point, img->d_h - new_h); + if (layer->cam_opts.autozoom) { + did_zoom = 1; + } + - if (crop_point > 0) { - switch_img_set_rect(img, 0, crop_point, (unsigned int)(layer->screen_w/scale), (unsigned int)(layer->screen_h/scale)); - img_aspect = (double) img->d_w / img->d_h; + if (layer->cam_opts.autozoom) { + c_x = switch_round_to_step(c_x, layer->cam_opts.snap_factor); + c_y = switch_round_to_step(c_y, layer->cam_opts.snap_factor); + + crop_x = c_x - (crop_w / 2); + crop_y = c_y - (crop_h / 2); + } else { + crop_x = c_x; + crop_y = c_y; + } + + set_bounds(&crop_x, &crop_y, img->d_w, img->d_h, crop_w, crop_h); + + //printf("ZOOM %d,%d %d,%d %dx%d\n", crop_x, crop_y, c_x, c_y, zoom_w, zoom_h); } } + + + if (!did_zoom) { + + if (layer->cam_opts.autopan) { + use_geometry = &layer->auto_geometry; + } else { + use_geometry = &layer->pan_geometry; + } + + if (can_pan) { + if (layer->cam_opts.autopan) { + crop_x = use_geometry->x - (crop_w / 2); + } else { + crop_x = use_geometry->x; + } + } else if (screen_aspect > img_aspect) { + crop_x = img->d_w / 4; + } + + if (can_pan) { + if (layer->cam_opts.autopan) { + crop_y = use_geometry->y - (crop_h / 2); + } else { + crop_y = use_geometry->y; + } + } else if (screen_aspect < img_aspect) { + crop_y = img->d_h / 4; + } + + crop_x = switch_round_to_step(crop_x, layer->cam_opts.snap_factor); + crop_y = switch_round_to_step(crop_y, layer->cam_opts.snap_factor); + } + + //printf("BOUNDS B4 %d,%d %dx%d %dx%d\n", crop_x, crop_y, img->d_w, img->d_h, crop_w, crop_h); + set_bounds(&crop_x, &crop_y, img->d_w, img->d_h, crop_w, crop_h); + //printf("BOUNDS AF %d,%d %dx%d %dx%d\n", crop_x, crop_y, img->d_w, img->d_h, crop_w, crop_h); + + if (img_changed) { + layer->crop_x = crop_x; + layer->crop_y = crop_y; + layer->crop_w = crop_w; + layer->crop_h = crop_h; + } + + //printf("B4 %d,%d %d,%d\n", crop_x, crop_y, layer->crop_x, layer->crop_y); + + + set_pan(crop_x, &layer->crop_x, layer->cam_opts.pan_accel_speed, layer->cam_opts.pan_accel_min, layer->cam_opts.pan_speed); + set_pan(crop_y, &layer->crop_y, layer->cam_opts.pan_accel_speed, layer->cam_opts.pan_accel_min, layer->cam_opts.pan_speed); + + //printf("AF %d,%d\n", layer->crop_x, layer->crop_y); + + + //printf("B4 %dx%d %dx%d\n", crop_w, crop_h, layer->crop_w, layer->crop_h); + set_pan(crop_w, &layer->crop_w, layer->cam_opts.zoom_accel_speed, layer->cam_opts.zoom_accel_min, layer->cam_opts.zoom_speed); + layer->crop_h = layer->crop_w / screen_aspect; + + set_bounds(&layer->crop_x, &layer->crop_y, img->d_w, img->d_h, layer->crop_w, layer->crop_h); + + assert(layer->crop_w > 0); + + //printf("RECT %d,%d %dx%d (%dx%d) [%dx%d] [%dx%d]\n", layer->crop_x, layer->crop_y, layer->crop_w, layer->crop_h, layer->crop_w + layer->crop_x, layer->crop_h + layer->crop_y, img->d_w, img->d_h, layer->screen_w, layer->screen_h); + + switch_img_set_rect(img, layer->crop_x, layer->crop_y, layer->crop_w, layer->crop_h); + switch_assert(img->d_w == layer->crop_w); + + img_aspect = (double) img->d_w / img->d_h; + } + layer->last_geometry = layer->bug_frame.geometry; + if (freeze) { switch_img_free(&layer->img); } - + if (screen_aspect > img_aspect) { img_w = (uint32_t)ceil((double)img_aspect * layer->screen_h); x_pos += (layer->screen_w - img_w) / 2; @@ -565,9 +822,12 @@ void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, img_w -= (layer->geometry.border * 2); img_h -= (layer->geometry.border * 2); + //printf("SCALE %d,%d %dx%d\n", x_pos, y_pos, img_w, img_h); + switch_img_scale(img, &layer->img, img_w, img_h); if (layer->img) { + //switch_img_copy(img, &layer->img); switch_img_patch(IMG, layer->img, x_pos + layer->geometry.border, y_pos + layer->geometry.border); } @@ -734,6 +994,8 @@ void conference_video_detach_video_layer(conference_member_t *member) switch_img_txt_handle_destroy(&layer->txthandle); } + member->cam_opts = layer->cam_opts; + conference_video_reset_layer(layer); layer->member_id = 0; layer->member = NULL; @@ -1121,6 +1383,9 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva for (i = 0; i < vlayout->layers; i++) { mcu_layer_t *layer = &canvas->layers[i]; + + conference_video_reset_layer(layer); + layer->geometry.x = vlayout->images[i].x; layer->geometry.y = vlayout->images[i].y; layer->geometry.hscale = vlayout->images[i].scale; @@ -1137,7 +1402,6 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva layer->idx = i; layer->refresh = 1; - layer->screen_w = (uint32_t)(canvas->img->d_w * layer->geometry.scale / VIDEO_LAYOUT_SCALE); layer->screen_h = (uint32_t)(canvas->img->d_h * layer->geometry.hscale / VIDEO_LAYOUT_SCALE); @@ -1147,6 +1411,8 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva layer->x_pos = (int)(canvas->img->d_w * layer->geometry.x / VIDEO_LAYOUT_SCALE); layer->y_pos = (int)(canvas->img->d_h * layer->geometry.y / VIDEO_LAYOUT_SCALE); + set_default_cam_opts(layer); + if (layer->geometry.floor) { canvas->layout_floor_id = i; @@ -1199,6 +1465,8 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva switch_mutex_unlock(canvas->mutex); switch_thread_rwlock_unlock(canvas->video_rwlock); + conference_event_adv_layout(conference, canvas, vlayout); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Canvas position %d applied layout %s\n", canvas->canvas_id + 1, vlayout->name); } diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index ab7bd44754..37b9ed7f60 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -3447,6 +3447,34 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co conference->super_canvas_label_layers = video_super_canvas_label_layers; conference->super_canvas_show_all_layers = video_super_canvas_show_all_layers; + + if (conference_utils_test_flag(conference, CFLAG_LIVEARRAY_SYNC)) { + char *p; + + if (strchr(conference->name, '@')) { + conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s", conference->name); + conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s", conference->name); + conference->info_event_channel = switch_core_sprintf(conference->pool, "conference-info.%s", conference->name); + conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s", conference->name); + } else { + conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s@%s", conference->name, conference->domain); + conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s@%s", conference->name, conference->domain); + conference->info_event_channel = switch_core_sprintf(conference->pool, "conference-info.%s@%s", conference->name, conference->domain); + conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s@%s", conference->name, conference->domain); + } + + conference->la_name = switch_core_strdup(conference->pool, conference->name); + if ((p = strchr(conference->la_name, '@'))) { + *p = '\0'; + } + + switch_live_array_create(conference->la_event_channel, conference->la_name, conference_globals.event_channel_id, &conference->la); + switch_live_array_set_user_data(conference->la, conference); + switch_live_array_set_command_handler(conference->la, conference_event_la_command_handler); + } + + + if (video_canvas_count < 1) video_canvas_count = 1; if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) && video_canvas_count > 1) { @@ -3499,30 +3527,6 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "conference-create"); switch_event_fire(&event); - if (conference_utils_test_flag(conference, CFLAG_LIVEARRAY_SYNC)) { - char *p; - - if (strchr(conference->name, '@')) { - conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s", conference->name); - conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s", conference->name); - conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s", conference->name); - } else { - conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s@%s", conference->name, conference->domain); - conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s@%s", conference->name, conference->domain); - conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s@%s", conference->name, conference->domain); - } - - conference->la_name = switch_core_strdup(conference->pool, conference->name); - if ((p = strchr(conference->la_name, '@'))) { - *p = '\0'; - } - - switch_live_array_create(conference->la_event_channel, conference->la_name, conference_globals.event_channel_id, &conference->la); - switch_live_array_set_user_data(conference->la, conference); - switch_live_array_set_command_handler(conference->la, conference_event_la_command_handler); - } - - end: switch_mutex_unlock(conference_globals.hash_mutex); diff --git a/src/mod/applications/mod_conference/mod_conference.h b/src/mod/applications/mod_conference/mod_conference.h index 101cbd93bb..0a3fb8db10 100644 --- a/src/mod/applications/mod_conference/mod_conference.h +++ b/src/mod/applications/mod_conference/mod_conference.h @@ -429,6 +429,23 @@ typedef struct mcu_layer_def_s { mcu_layer_geometry_t layers[MCU_MAX_LAYERS]; } mcu_layer_def_t; + +typedef struct mcu_layer_cam_opts_s { + int manual_pan; + int manual_zoom; + int autozoom; + int autopan; + int zoom_factor; + int snap_factor; + int zoom_move_factor; + int pan_speed; + int pan_accel_speed; + int pan_accel_min; + int zoom_speed; + int zoom_accel_speed; + int zoom_accel_min; +} mcu_layer_cam_opts_t; + struct mcu_canvas_s; typedef struct mcu_layer_s { @@ -447,7 +464,13 @@ typedef struct mcu_layer_s { int refresh; int clear; int is_avatar; - int crop_point; + int crop_x; + int crop_y; + int crop_w; + int crop_h; + int last_w; + int last_h; + uint32_t img_count; switch_size_t last_img_addr; switch_image_t *img; switch_image_t *cur_img; @@ -463,6 +486,12 @@ typedef struct mcu_layer_s { int need_patch; conference_member_t *member; switch_frame_t bug_frame; + switch_frame_geometry_t last_geometry; + switch_frame_geometry_t auto_geometry; + switch_frame_geometry_t zoom_geometry; + switch_frame_geometry_t pan_geometry; + switch_frame_geometry_t manual_geometry; + mcu_layer_cam_opts_t cam_opts; } mcu_layer_t; typedef struct video_layout_s { @@ -540,6 +569,7 @@ typedef struct conference_obj { char *la_event_channel; char *chat_event_channel; char *mod_event_channel; + char *info_event_channel; char *desc; char *timer_name; char *tts_engine; @@ -809,6 +839,7 @@ struct conference_member { char *text_framedata; uint32_t text_framesize; + mcu_layer_cam_opts_t cam_opts; }; @@ -967,6 +998,7 @@ void conference_event_send_rfc(conference_obj_t *conference); void conference_member_update_status_field(conference_member_t *member); void conference_event_la_command_handler(switch_live_array_t *la, const char *cmd, const char *sessid, cJSON *jla, void *user_data); void conference_event_adv_la(conference_obj_t *conference, conference_member_t *member, switch_bool_t join); +void conference_event_adv_layout(conference_obj_t *conference, mcu_canvas_t *canvas, video_layout_t *vlayout); switch_status_t conference_video_init_canvas(conference_obj_t *conference, video_layout_t *vlayout, mcu_canvas_t **canvasP); switch_status_t conference_video_attach_canvas(conference_obj_t *conference, mcu_canvas_t *canvas, int super); void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canvas_t *canvas, video_layout_t *vlayout); @@ -980,6 +1012,7 @@ void conference_video_set_canvas_letterbox_bgcolor(mcu_canvas_t *canvas, char *c void conference_video_set_canvas_bgcolor(mcu_canvas_t *canvas, char *color); void conference_video_scale_and_patch(mcu_layer_t *layer, switch_image_t *ximg, switch_bool_t freeze); void conference_video_reset_layer(mcu_layer_t *layer); +void conference_video_reset_layer_cam(mcu_layer_t *layer); void conference_video_clear_layer(mcu_layer_t *layer); void conference_video_reset_image(switch_image_t *img, switch_rgb_color_t *color); void conference_video_parse_layouts(conference_obj_t *conference, int WIDTH, int HEIGHT); @@ -1111,6 +1144,7 @@ switch_status_t conference_api_sub_check_record(conference_obj_t *conference, sw switch_status_t conference_api_sub_check_record(conference_obj_t *conference, switch_stream_handle_t *stream, int arc, char **argv); switch_status_t conference_api_sub_volume_in(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_file_seek(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); +switch_status_t conference_api_sub_cam(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_stop(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); switch_status_t conference_api_sub_hup(conference_member_t *member, switch_stream_handle_t *stream, void *data); switch_status_t conference_api_sub_pauserec(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv); diff --git a/src/mod/applications/mod_cv/mod_cv.cpp b/src/mod/applications/mod_cv/mod_cv.cpp index c3a0376b3b..dfedd930de 100644 --- a/src/mod/applications/mod_cv/mod_cv.cpp +++ b/src/mod/applications/mod_cv/mod_cv.cpp @@ -134,6 +134,10 @@ typedef struct cv_context_s { char *png_prefix; int tick_speed; int confidence_level; + int max_search_w; + int max_search_h; + int neighbors; + double search_scale; } cv_context_t; @@ -484,6 +488,12 @@ static void init_context(cv_context_t *context) context->cascade_path = switch_core_get_variable_pdup("cv_default_cascade", context->pool); context->nested_cascade_path = switch_core_get_variable_pdup("cv_default_nested_cascade", context->pool); context->confidence_level = 20; + context->max_search_w = 20; + context->max_search_h = 20; + context->neighbors = 2; + context->search_scale = 1.1; + + for (int i = 0; i < MAX_OVERLAY; i++) { context->overlay[i] = (struct overlay *) switch_core_alloc(context->pool, sizeof(struct overlay)); @@ -588,12 +598,12 @@ void detectAndDraw(cv_context_t *context) equalizeHist( smallImg, smallImg ); context->cascade->detectMultiScale( smallImg, detectedObjs, - 1.1, 2, 0 + context->search_scale, context->neighbors, 0 |CV_HAAR_FIND_BIGGEST_OBJECT |CV_HAAR_DO_ROUGH_SEARCH |CV_HAAR_SCALE_IMAGE , - Size(20, 20) ); + Size(context->max_search_w, context->max_search_h) ); parse_stats(&context->detected, detectedObjs.size(), context->skip); @@ -616,7 +626,7 @@ void detectAndDraw(cv_context_t *context) double aspect_ratio = (double)r->width/r->height; - if (context->shape_idx >= MAX_SHAPES) { + if (context->shape_idx >= 1) {//MAX_SHAPES) { break; } @@ -625,6 +635,7 @@ void detectAndDraw(cv_context_t *context) center.x = switch_round_to_step(cvRound((r->x + r->width*0.5)*scale), 20); center.y = switch_round_to_step(cvRound((r->y + r->height*0.5)*scale), 20); radius = switch_round_to_step(cvRound((r->width + r->height)*0.25*scale), 20); + if (context->debug) { circle( img, center, radius, color, 3, 8, 0 ); @@ -679,6 +690,7 @@ void detectAndDraw(cv_context_t *context) // Draw rectangle reflecting confidence const int object_neighbors = nestedObjects.size(); + //printf("WTF %d\n", object_neighbors); //cout << "Detected " << object_neighbors << " object neighbors" << endl; const int rect_height = cvRound((float)img.rows * object_neighbors / max_neighbors); CvScalar col = CV_RGB((float)255 * object_neighbors / max_neighbors, 0, 0); @@ -861,9 +873,11 @@ static switch_status_t video_thread_callback(switch_core_session_t *session, swi frame->geometry.y = context->shape[0].cy; frame->geometry.w = context->shape[0].w; frame->geometry.h = context->shape[0].h; - frame->geometry.m = 1; + frame->geometry.M++; + frame->geometry.X = 0; } else { - frame->geometry.m = 0; + frame->geometry.M = 0; + frame->geometry.X++; } if (context->overlay_count && (abs || (context->detect_event && context->shape[0].cx))) { @@ -991,7 +1005,7 @@ static void parse_params(cv_context_t *context, int start, int argc, char **argv *val++ = '\0'; } - if (name && val) { + if (name && !zstr(val)) { if (!strcasecmp(name, "xo")) { context->overlay[png_idx]->xo = atof(val); } else if (!strcasecmp(name, "nick")) { @@ -1029,6 +1043,17 @@ static void parse_params(cv_context_t *context, int start, int argc, char **argv context->skip = atoi(val); } else if (!strcasecmp(name, "debug")) { context->debug = atoi(val); + } else if (!strcasecmp(name, "neighbors")) { + context->neighbors = atoi(val); + } else if (!strcasecmp(name, "max_search_w")) { + context->max_search_w = atoi(val); + } else if (!strcasecmp(name, "max_search_h")) { + context->max_search_h = atoi(val); + } else if (!strcasecmp(name, "search_scale")) { + double tmp = atof(val); + if (tmp > 1) { + context->search_scale = tmp; + } } else if (!strcasecmp(name, "confidence")) { context->confidence_level = atoi(val); } else if (!strcasecmp(name, "cascade")) { diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c index 0f53f64ed7..87ca225014 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.c +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -3898,7 +3898,6 @@ static switch_bool_t verto__unsubscribe_func(const char *method, cJSON *params, static switch_bool_t verto__broadcast_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) { char *json_text = NULL; - switch_bool_t r = SWITCH_FALSE; const char *event_channel = cJSON_GetObjectCstr(params, "eventChannel"); cJSON *jevent, *broadcast; const char *display = NULL; @@ -3941,11 +3940,12 @@ static switch_bool_t verto__broadcast_func(const char *method, cJSON *params, js if (mcast_socket_send(&jsock->profile->mcast_pub, json_text, strlen(json_text) + 1) <= 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "multicast socket send error! %s\n", strerror(errno)); - r = SWITCH_FALSE; - cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Send failure!")); + //r = SWITCH_FALSE; + //cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Send failure!")); } else { - r = SWITCH_TRUE; - cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Sent")); + //r = SWITCH_TRUE; + //cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Sent")); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MCAST Data Sent\n"); } free(json_text); json_text = NULL; @@ -3956,7 +3956,7 @@ static switch_bool_t verto__broadcast_func(const char *method, cJSON *params, js end: - return r; + return SWITCH_TRUE; } static switch_bool_t login_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)