]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-7513: refactor mcu code, put everyone on a canvas and send out using a dedicated...
authorSeven Du <dujinfang@gmail.com>
Wed, 28 Jan 2015 08:43:08 +0000 (16:43 +0800)
committerMichael Jerris <mike@jerris.com>
Thu, 28 May 2015 17:46:55 +0000 (12:46 -0500)
src/mod/applications/mod_conference/mod_conference.c

index e3113983bce8826ff20b58d3f404bb21d75cf404..96203b5caf3040802cf1db8b0ecca6c1e6f9e868 100644 (file)
@@ -89,7 +89,10 @@ static int EC = 0;
 #define SCORE_IIR_SPEAKING_MAX 300
 /* the threshold below which you cede the floor to someone loud (see above value). */
 #define SCORE_IIR_SPEAKING_MIN 100
-
+/* the FPS of the conference canvas */
+#define FPS 30
+/* max supported layers in one mcu */
+#define MCU_MAX_LAYERS 16
 
 #define test_eflag(conference, flag) ((conference)->eflags & flag)
 
@@ -333,6 +336,28 @@ struct vid_helper {
        int up;
 };
 
+typedef struct mcu_bgcolor_s {
+       uint8_t y;
+       uint8_t u;
+       uint8_t v;
+} mcu_bgcolor_t;
+
+typedef struct mcu_layer_s {
+       int x;
+       int y;
+       int w;
+       int h;
+       switch_image_t *img;
+} mcu_layer_t;
+
+typedef struct mcu_canvas_s {
+       int width;
+       int height;
+       mcu_bgcolor_t bgcolor;
+       switch_image_t *img;
+       mcu_layer_t layers[MCU_MAX_LAYERS];
+} mcu_canvas_t;
+
 struct conference_obj;
 
 /* Record Node */
@@ -344,8 +369,6 @@ typedef struct conference_record {
        struct conference_record *next;
 } conference_record_t;
 
-struct video_mixer;
-
 /* Conference Object */
 typedef struct conference_obj {
        char *name;
@@ -460,7 +483,7 @@ typedef struct conference_obj {
        switch_file_handle_t *record_fh;
        int video_recording;
        switch_thread_t *video_muxing_thread;
-       struct video_mixer *video_mixer;
+       mcu_canvas_t canvas;
 } conference_obj_t;
 
 /* Relationship with another member */
@@ -537,6 +560,7 @@ struct conference_member {
        al_handle_t *al;
        int last_speech_channels;
        int video_layer_id;
+       switch_image_t *img;
 };
 
 typedef enum {
@@ -556,28 +580,6 @@ typedef struct api_command {
        char *psyntax;
 } api_command_t;
 
-
-#define FPS 15 / 2
-#define WIDTH 352
-#define HEIGHT 288
-#define SIZE WIDTH * HEIGHT
-#define MAX_LAYERS 64
-
-typedef struct mcu_layer_s {
-       int id;
-       char *name;
-       char *description;
-       int w;
-       int h;
-       int x;
-       int y;
-       int location_id;
-       const char *channel_uuid;
-       switch_mutex_t *mutex;
-       switch_memory_pool_t *pool;
-       switch_image_t *scaled_img;
-} mcu_layer_t;
-
 typedef struct bgcolor_yuv_s
 {
        uint8_t y;
@@ -585,25 +587,6 @@ typedef struct bgcolor_yuv_s
        uint8_t v;
 } bgcolor_yuv_t;
 
-typedef struct video_mixer {
-       int max_layers;
-       int total_layers;
-       int width;
-       int height;
-       int current_layers;
-       char *name;
-       bgcolor_yuv_t bgcolor;
-       switch_image_t *img;
-       mcu_layer_t layers[MAX_LAYERS];
-       switch_memory_pool_t *pool;
-       switch_mutex_t *mutex;
-       switch_mutex_t *cond_mutex;
-       switch_mutex_t *cond2_mutex;
-       unsigned char *packet;
-       switch_size_t packetlen;
-       switch_thread_cond_t *cond;
-} video_mixer_t;
-
 /* Function Prototypes */
 static int setup_media(conference_member_t *member, conference_obj_t *conference);
 static uint32_t next_member_id(void);
@@ -672,7 +655,6 @@ static switch_status_t conference_add_event_member_data(conference_member_t *mem
 static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data);
 static switch_status_t conf_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data);
 static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data);
-static int video_mixer_wake(video_mixer_t *video_mixer);
 
 #define lock_member(_member) switch_mutex_lock(_member->write_mutex); switch_mutex_lock(_member->read_mutex)
 #define unlock_member(_member) switch_mutex_unlock(_member->read_mutex); switch_mutex_unlock(_member->write_mutex)
@@ -2811,64 +2793,6 @@ static switch_status_t conference_file_close(conference_obj_t *conference, confe
        return switch_core_file_close(&node->fh);
 }
 
-void reset_mixer_node(video_mixer_t *video_mixer, mcu_layer_t *mcu_layer_node)
-{
-       int i, j, k;
-       int H = video_mixer->height;
-       int W = video_mixer->width;
-       int h = mcu_layer_node->h;
-       int w = mcu_layer_node->w;
-       int x = mcu_layer_node->x;
-       int y = mcu_layer_node->y;
-       uint8_t *LS[3] = { 0 };
-       uint8_t *ls[3] = { 0 };
-
-       LS[0] = video_mixer->img->planes[0];
-       LS[1] = video_mixer->img->planes[1];
-       LS[2] = video_mixer->img->planes[2];
-
-       for (i = y; i < (y + h) && i < H; i++) {
-               for (j = x; j < (x + w) && j < W; j++) {
-                       LS[0][i * W + j] = video_mixer->bgcolor.y;
-               }
-       }
-
-       for (i = y; i < (y + h) && i < H; i+=4) {
-               for (j = x; j < (x + w) && j < W; j+=4) {
-                       for (k = 1; k <= 2; k++) {
-                               uint8_t tag;
-                               if (k == 1){
-                                       tag = video_mixer->bgcolor.u;
-                               } else {
-                                       tag = video_mixer->bgcolor.v;
-                               }
-                               LS[k][i/4 * W + j/2]                 = tag;
-                               LS[k][i/4 * W + j/2 + 1]             = tag;
-                               LS[k][(i+2)/4 * W + j/2 + W / 2]     = tag;
-                               LS[k][(i+2)/4 * W + j/2 + W / 2 + 1] = tag;
-                       }
-               }
-       }
-
-       if (mcu_layer_node->scaled_img != NULL) {
-               ls[0] = mcu_layer_node->scaled_img->planes[0];
-               ls[1] = mcu_layer_node->scaled_img->planes[1];
-               ls[2] = mcu_layer_node->scaled_img->planes[2];
-
-               for (i = 0; i < h; i++) {
-                       for (j = 0; j < w; j++) {
-                               ls[0][i * w + j] = video_mixer->bgcolor.y;
-                       }
-               }
-
-               for (i = 0; i < w * h / 4 ; i++) {
-                       ls[1][i] = video_mixer->bgcolor.u;
-                       ls[2][i] = video_mixer->bgcolor.v;
-               }
-       }
-}
-
-
 /* Gain exclusive access and remove the member from the list */
 static switch_status_t conference_del_member(conference_obj_t *conference, conference_member_t *member)
 {
@@ -2892,14 +2816,6 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
 
        lock_member(member);
 
-       if (member->video_layer_id > -1 && member->conference->video_mixer) {
-               switch_mutex_lock(conference->video_mixer->mutex);
-               conference->video_mixer->current_layers--;
-               conference->video_mixer->layers[member->video_layer_id].channel_uuid = NULL;
-               reset_mixer_node(conference->video_mixer, &conference->video_mixer->layers[member->video_layer_id]);
-               switch_mutex_unlock(conference->video_mixer->mutex);
-       }
-
        member_del_relationship(member, 0);
 
        conference_cdr_del(member);
@@ -3128,16 +3044,12 @@ switch_status_t video_thread_callback(switch_core_session_t *session, switch_fra
        }
 
        if (switch_test_flag(member->conference, CFLAG_VIDEO_MUXING) && frame->img) {
-               switch_image_t *img_copy = NULL;
-
-               switch_img_copy(frame->img, &img_copy);
-               switch_queue_push(member->video_queue, img_copy);
+               switch_img_copy(frame->img, &member->img);
+               switch_thread_rwlock_unlock(member->conference->rwlock);
                unlock_member(member);
-               video_mixer_wake(member->conference->video_mixer);
                return SWITCH_STATUS_SUCCESS;
        }
 
-
        for (rel = member->relationships; rel; rel = rel->next) {
                conference_member_t *imember;
                if (!(rel->flags & RFLAG_CAN_SEND_VIDEO)) continue;
@@ -3187,43 +3099,21 @@ static void conference_command_handler(switch_live_array_t *la, const char *cmd,
 {
 }
 
-void init_video_buff(video_mixer_t *video_mixer)
+void reset_image(switch_image_t *img, bgcolor_yuv_t *color)
 {
-       uint8_t *buff = video_mixer->img->img_data;
-       uint8_t y = video_mixer->bgcolor.y;
-       uint8_t u = video_mixer->bgcolor.u;
-       uint8_t v = video_mixer->bgcolor.v;
-       int width = video_mixer->width;
-       int height = video_mixer->height;
-
-       memset(buff, y, width * height);
-       buff = buff + width * height;
-
-       memset(buff, u, width * height / 4);
-       buff = buff + width * height / 4;
-
-       memset(buff, v, width * height / 4);
-}
-
-
-static void destroy_video_mixer(video_mixer_t **video_mixer)
-{
-       switch_memory_pool_t *pool;
+       int i;
 
-       if (!video_mixer || !*video_mixer) {
-               return;
+       for (i = 0; i < img->h; i++) {
+               memset(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * i, color->y, img->d_w);
        }
 
-       if ((*video_mixer)->img) switch_img_free(&(*video_mixer)->img);
-
-       pool = (*video_mixer)->pool;
-       switch_core_destroy_memory_pool(&pool);
-       
-       *video_mixer = NULL;
+       for (i = 0; i < img->h / 2; i++) {
+               memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * i, color->u, img->d_w);
+               memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_U] * i, color->v, img->d_w);
+       }
 }
 
-
-void analy_bgcolor(bgcolor_yuv_t *bgcolor, char *bgcolor_str)
+void set_bgcolor(bgcolor_yuv_t *bgcolor, char *bgcolor_str)
 {
        uint8_t y = 134;
        uint8_t u = 128;
@@ -3249,190 +3139,8 @@ void analy_bgcolor(bgcolor_yuv_t *bgcolor, char *bgcolor_str)
        bgcolor->v = v;
 }
 
-
-static int video_mixer_wake(video_mixer_t *video_mixer)
-{
-       switch_status_t status;
-       int tries = 0;
-
- top:
-       
-       status = switch_mutex_trylock(video_mixer->cond_mutex);
-       if (status == SWITCH_STATUS_SUCCESS) {
-               switch_thread_cond_signal(video_mixer->cond);
-               switch_mutex_unlock(video_mixer->cond_mutex);
-               return 1;
-       } else {
-               if (switch_mutex_trylock(video_mixer->cond2_mutex) == SWITCH_STATUS_SUCCESS) {
-                       switch_mutex_unlock(video_mixer->cond2_mutex);
-               } else {
-                       if (++tries < 10) {
-                               switch_cond_next();
-                               goto top;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-#define MIX_CONF "video_layouts.conf"
-static video_mixer_t *create_video_mixer(const char *layout_name)
-{
-       video_mixer_t *video_mixer;
-       int i, j;
-       int width = 352;
-       int height = 288;
-       int rows = 2; // to do, read from profile
-       int cols = 2;
-       int id = 0;
-       //char *bgcolor = NULL;
-       bgcolor_yuv_t bgcolor;
-       char *layers_name = NULL;
-       switch_xml_t cfg, xml, x_layers, x_group, x_layer;
-       switch_xml_t x_profiles, x_profile, x_param = NULL;
-       switch_memory_pool_t *pool;
-
-       switch_core_new_memory_pool(&pool);
-
-
-       if (!(xml = switch_xml_open_cfg(MIX_CONF, &cfg, NULL))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Opening of video layouts failed, using builtin default profile values\n");
-       } else if ((x_profiles = switch_xml_child(cfg, "profiles"))) {
-               if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", layout_name))) {
-                       for (x_param = switch_xml_child(x_profile, "param"); x_param; x_param = x_param->next) {
-                               char *var = (char *)switch_xml_attr_soft(x_param, "name");
-                               char *val = (char *)switch_xml_attr_soft(x_param, "value");
-
-                               if (!strcmp(var, "width")) {
-                                       width = atoi(val);
-                               } else if (!strcmp(var, "height")) {
-                                       height = atoi(val);
-                               } else if (!strcmp(var, "rows")) {
-                                       rows = atoi(val);
-                               } else if (!strcmp(var, "cols")) {
-                                       cols = atoi(val);
-                               } else if (!strcmp(var, "bgcolor")) {
-                                       analy_bgcolor(&bgcolor, val);
-                               } else if (!strcmp(var, "layers")) {
-                                       layers_name = val;
-                               }
-                       }
-               }
-       }
-
-       video_mixer = (video_mixer_t *)switch_core_alloc(pool, sizeof(video_mixer_t));
-
-       switch_core_new_memory_pool(&(video_mixer->pool));
-       video_mixer->pool = pool;
-       video_mixer->name = switch_core_strdup(video_mixer->pool, layout_name);
-       video_mixer->bgcolor = bgcolor;
-       video_mixer->max_layers = MAX_LAYERS;
-       video_mixer->total_layers = 0;
-       video_mixer->current_layers = 0;
-       video_mixer->height = height * rows;
-       video_mixer->width = width * cols;
-       video_mixer->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, video_mixer->width, video_mixer->height, 0);
-       video_mixer->packet = switch_core_alloc(video_mixer->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
-       video_mixer->packetlen = SWITCH_RECOMMENDED_BUFFER_SIZE;
-       switch_thread_cond_create(&video_mixer->cond, video_mixer->pool);
-       switch_mutex_init(&video_mixer->cond_mutex, SWITCH_MUTEX_NESTED, video_mixer->pool);
-       switch_mutex_init(&video_mixer->cond2_mutex, SWITCH_MUTEX_NESTED, video_mixer->pool);
-
-       init_video_buff(video_mixer);
-       switch_mutex_init(&video_mixer->mutex, SWITCH_MUTEX_NESTED, video_mixer->pool);
-
-       for (i = 0; i < rows; i++) {
-               for (j = 0; j < cols; j++) {
-                       mcu_layer_t *layer = NULL;
-                       layer = &video_mixer->layers[video_mixer->total_layers];
-
-                       layer->w = (int)width;//(int)(width / rows);
-                       layer->h = (int)height;//(int)(height / cols);
-                       layer->x = j * layer->w;
-                       layer->y = i * layer->h;
-
-                       if ((cols % 2) && (j > 0)) layer->x += j;
-                       if ((rows % 2) && (i > 0)) layer->y += i;
-                       layer->location_id = ++id;
-                       video_mixer->total_layers++;
-               }
-       }
-
-       if (NULL != layers_name) {
-
-               if (!(xml = switch_xml_open_cfg(MIX_CONF, &cfg, NULL))) {
-                       goto skip;
-               }
-
-               if ((x_layers = switch_xml_child(cfg, "layers"))) {
-                       x_group = switch_xml_find_child(x_layers, "group", "name", layers_name);
-
-                       if (x_group) {
-                               for (x_layer = switch_xml_child(x_group, "layer"); x_layer; x_layer = x_layer->next) {
-                                       char *x = (char *)switch_xml_attr_soft(x_layer, "x");
-                                       char *y = (char *)switch_xml_attr_soft(x_layer, "y");
-                                       char *w = (char *)switch_xml_attr_soft(x_layer, "w");
-                                       char *h = (char *)switch_xml_attr_soft(x_layer, "h");
-                                       mcu_layer_t *layer = NULL;
-                                       layer = &video_mixer->layers[video_mixer->total_layers];
-
-                                       if (!x || !y || !w || !h) continue;
-
-                                       layer->w = atoi(w);
-                                       layer->h = atoi(h);
-                                       layer->x = atoi(x);
-                                       layer->y = atoi(y);
-
-                                       layer->location_id = ++id;
-                                       video_mixer->total_layers++;
-                               }
-                       }
-               }
-       }
-
-       if (video_mixer->total_layers == 0) {
-               destroy_video_mixer(&video_mixer);
-               return video_mixer;
-       }
-
-skip:
-
-       return video_mixer;
-}
-
 // simple implementation to patch a small img to a big IMG at position x,y
-void patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
-{
-       int i, j, k;
-       int W = IMG->d_w;
-       int H = IMG->d_h;
-       int w = img->d_w;
-       int h = img->d_h;
-
-       switch_assert(img->fmt = SWITCH_IMG_FMT_I420);
-       switch_assert(IMG->fmt = SWITCH_IMG_FMT_I420);
-
-       for (i = y; i < (y + h) && i < H; i++) {
-               for (j = x; j < (x + w) && j < W; j++) {
-                       IMG->planes[0][i * IMG->stride[0] + j] = img->planes[0][(i - y) * img->stride[0] + (j - x)];
-               }
-       }
-
-       for (i = y; i < (y + h) && i < H; i+=4) {
-               for (j = x; j < (x + w) && j < W; j+=4) {
-                       for (k = 1; k <= 2; k++) {
-                               IMG->planes[k][i/2 * IMG->stride[k] + j/2]         = img->planes[k][(i-y)/2 * img->stride[k] + (j-x)/2];
-                               IMG->planes[k][i/2 * IMG->stride[k] + j/2 + 1]     = img->planes[k][(i-y)/2 * img->stride[k] + (j-x)/2 + 1];
-                               IMG->planes[k][(i+2)/2 * IMG->stride[k] + j/2]     = img->planes[k][(i+2-y)/2 * img->stride[k] + (j-x)/2];
-                               IMG->planes[k][(i+2)/2 * IMG->stride[k] + j/2 + 1] = img->planes[k][(i+2-y)/2 * img->stride[k] + (j-x)/2 + 1];
-                       }
-               }
-       }
-}
-
-#if 0
-void patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
+void patch_image(switch_image_t *IMG, switch_image_t *img, int x, int y)
 {
        int i, j, k;
        int W = IMG->d_w;
@@ -3440,6 +3148,9 @@ void patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
        int w = img->d_w;
        int h = img->d_h;
 
+       switch_assert(img->fmt = SWITCH_IMG_FMT_I420);
+       switch_assert(IMG->fmt = SWITCH_IMG_FMT_I420);
+
        for (i = y; i < (y + h) && i < H; i++) {
                for (j = x; j < (x + w) && j < W; j++) {
                        IMG->planes[0][i * IMG->stride[0] + j] = img->planes[0][(i - y) * img->stride[0] + (j - x)];
@@ -3457,15 +3168,16 @@ void patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
                }
        }
 }
-#endif
 
-static void scale_and_patch(video_mixer_t *video_mixer, switch_image_t *img, mcu_layer_t *layer)
+static void scale_and_patch(switch_image_t *IMG, switch_image_t *img, mcu_layer_t *layer)
 {
        int width = img->d_w;
        int height = img->d_h;
        int ret;
        FilterModeEnum filtermode;
-       
+
+       switch_assert(layer->img);
+
        if (width != layer->w || height != layer->h) {
                filtermode = kFilterBox;
                /*int I420Scale(const uint8* src_y, int src_stride_y,
@@ -3483,50 +3195,78 @@ static void scale_and_patch(video_mixer_t *video_mixer, switch_image_t *img, mcu
                                                img->planes[1], img->stride[1],
                                                img->planes[2], img->stride[2],
                                                width, height,
-                                               layer->scaled_img->planes[0], layer->scaled_img->stride[0],
-                                               layer->scaled_img->planes[1], layer->scaled_img->stride[1],
-                                               layer->scaled_img->planes[2], layer->scaled_img->stride[2],
+                                               layer->img->planes[0], layer->img->stride[0],
+                                               layer->img->planes[1], layer->img->stride[1],
+                                               layer->img->planes[2], layer->img->stride[2],
                                                layer->w, layer->h,
                                                filtermode);
                
                if (ret != 0) {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ret: %d\n", ret);
                } else {
-                       switch_mutex_lock(video_mixer->mutex);
-                       printf("SCALE and PATCH at %dx%d to %dx%d at %dx%d\n", width, height, layer->w, layer->h, layer->x, layer->y);
-                       patch(video_mixer->img, layer->scaled_img, layer->x, layer->y);
-                       switch_mutex_unlock(video_mixer->mutex);
+                       printf("SCALE and PATCH at %dx%d to %dx%d\n", width, height, layer->x, layer->y);
+                       patch_image(IMG, layer->img, layer->x, layer->y);
                }
        } else {
-               switch_mutex_lock(video_mixer->mutex);
                printf("PATCH at %d %d\n", layer->x, layer->y);
-               patch(video_mixer->img, img, layer->x, layer->y);
-               switch_mutex_unlock(video_mixer->mutex);
+               patch_image(IMG, img, layer->x, layer->y);
        }
 }
 
+void reset_canvas(mcu_canvas_t *canvas, char *color)
+{
+       bgcolor_yuv_t bgcolor;
+
+       set_bgcolor(&bgcolor, color);
+       reset_image(canvas->img, &bgcolor);
+}
+
+static void init_canvas(mcu_canvas_t *canvas)
+{
+       int i;
+       canvas->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, 1920, 1080, 0);
+       switch_assert(canvas->img);
+
+       reset_canvas(canvas, "#0000FF");
+
+       for (i = 0; i < 4; i++) {
+               int w = 800;
+               int h = 600;
+               canvas->layers[i].x = 0 + i * 300;
+               canvas->layers[i].y = 0 + i * 300;
+               canvas->layers[i].w = w;
+               canvas->layers[i].h = h;
+               canvas->layers[i].img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, w, h, 1);
+               switch_assert(canvas->layers[i].img);
+       }
+}
+
+static void destroy_canvas(mcu_canvas_t *canvas) {
+       int i;
+
+       if (canvas->img) {
+               switch_img_free(&canvas->img);
+       }
+
+       for (i = 0; i < MCU_MAX_LAYERS; i++) {
+               switch_img_free(&canvas->layers[i].img);
+       }
+}
 
 static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thread, void *obj)
 {
        conference_obj_t *conference = (conference_obj_t *) obj;
        conference_member_t *imember;
        switch_frame_t write_frame = { 0 };
-       
-       if (!(conference->video_mixer = create_video_mixer(conference->video_layout_name))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot open video layout %s\n", conference->video_layout_name);
-               switch_yield(1000000);
-               return NULL;
-       }
+       uint8_t packet[SWITCH_RECOMMENDED_BUFFER_SIZE];
+
+       init_canvas(&conference->canvas);
 
-       switch_mutex_lock(conference->video_mixer->cond_mutex); 
-       
        while (globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT) && switch_test_flag(conference, CFLAG_VIDEO_MUXING)) {
-               int remaining = 0;
 
                switch_mutex_lock(conference->member_mutex);
                for (imember = conference->members; imember; imember = imember->next) {
                        switch_channel_t *ichannel = switch_core_session_get_channel(imember->session);
-                       void *pop;
 
                        printf("MEMBER %d\n", imember->id);
 
@@ -3535,46 +3275,11 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread
                                continue;
                        }
 
-                       if (switch_queue_trypop(imember->video_queue, &pop) == SWITCH_STATUS_SUCCESS) {
-                               switch_image_t *img = (switch_image_t *)pop;                            
-                               mcu_layer_t *layer = NULL;
-                               int i;
-
-                               remaining += switch_queue_size(imember->video_queue);
-                               
-                               if (imember->video_layer_id > -1) {
-                                       layer = &conference->video_mixer->layers[imember->video_layer_id];
-                               }
-
-                               if (!layer) {
-                                       /* find an empty layer */
-                                       switch_mutex_lock(conference->video_mixer->mutex);
-                                       for (i = 0; i < conference->video_mixer->total_layers; i++) {
-                                               layer = &conference->video_mixer->layers[i];
-                                               if (!layer->channel_uuid) {
-                                                       conference->video_mixer->layers[i].channel_uuid = switch_core_session_get_uuid(imember->session);
-                                                       conference->video_mixer->current_layers++;
-                                                       imember->video_layer_id = i;
-                                                       break;
-                                               }
-                                       }
-                                       switch_mutex_unlock(conference->video_mixer->mutex);
-                               }
-                               
-                               if (layer) {
-                                       printf("yay pop img layer %d w:%d h:%d x:%d y:%d\n", imember->video_layer_id, layer->w, layer->h, layer->x, layer->y);
+                       if (imember->img) {
+                               int which_layer = imember->id % 4; //random layer
+                               mcu_canvas_t *canvas = &conference->canvas;
 
-                                       if (!layer->scaled_img) {
-                                               layer->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, layer->w, layer->h, 0);
-                                               switch_assert(layer->scaled_img);
-                                       }
-                                       
-                                       scale_and_patch(conference->video_mixer, img, layer);
-                               } else {
-                                       printf("fuck no layer\n");
-                               }
-
-                               switch_img_free(&img);
+                               scale_and_patch(conference->canvas.img, imember->img, &canvas->layers[which_layer]);
                        }
 
                        if (imember->session) {
@@ -3582,7 +3287,6 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread
                        }
                }
 
-
                for (imember = conference->members; imember; imember = imember->next) {
                        switch_channel_t *ichannel = switch_core_session_get_channel(imember->session);
 
@@ -3593,33 +3297,29 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread
 
                        printf("WRITE TO %d\n", imember->id);
                        switch_set_flag(&write_frame, SFF_RAW_RTP);
-                       write_frame.img = conference->video_mixer->img;
-                       write_frame.packet = conference->video_mixer->packet;
-                       write_frame.data = conference->video_mixer->packet + 12;
-                       write_frame.datalen = conference->video_mixer->packetlen - 12;
+                       write_frame.img = conference->canvas.img;
+                       write_frame.packet = packet;
+                       write_frame.data = packet + 12;
+                       write_frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE - 12;
                        write_frame.buflen = write_frame.datalen;
-                       write_frame.packetlen = conference->video_mixer->packetlen;
+                       write_frame.packetlen = SWITCH_RECOMMENDED_BUFFER_SIZE;
 
                        switch_core_session_write_video_frame(imember->session, &write_frame, SWITCH_IO_FLAG_NONE, 0);                  
                        
-                       switch_core_session_rwunlock(imember->session);
-                       
+                       if (imember->session) {
+                               switch_core_session_rwunlock(imember->session);
+                       }
                }
 
                switch_mutex_unlock(conference->member_mutex);
-               
-               if (!remaining) {
-                       printf("SLEEP\n");
-                       switch_mutex_lock(conference->video_mixer->cond2_mutex);
-                       switch_thread_cond_wait(conference->video_mixer->cond, conference->video_mixer->cond_mutex);
-                       switch_mutex_unlock(conference->video_mixer->cond2_mutex);
-               }
-               
+
+               printf("SLEEP\n");
+               switch_yield(1000000/FPS);
        }
 
        printf("END MUX THREAD ???????\n");
 
-       destroy_video_mixer(&conference->video_mixer);
+       destroy_canvas(&conference->canvas);
 
        return NULL;
 }