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)