SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_video_filter_shutdown);
SWITCH_MODULE_DEFINITION(mod_video_filter, mod_video_filter_load, mod_video_filter_shutdown, NULL);
+#define MAX_MASK 25
+
typedef struct chromakey_context_s {
int threshold;
switch_image_t *bgimg;
switch_image_t *bgimg_scaled;
+ switch_file_handle_t vfh;
switch_rgb_color_t bgcolor;
- switch_rgb_color_t mask;
+ switch_rgb_color_t mask[MAX_MASK];
+ int thresholds[MAX_MASK];
+ int mask_len;
switch_core_session_t *session;
+ switch_mutex_t *command_mutex;
} chromakey_context_t;
static void init_context(chromakey_context_t *context)
{
switch_color_set_rgb(&context->bgcolor, "#000000");
- switch_color_set_rgb(&context->mask, "#FFFFFF");
context->threshold = 300;
+ switch_mutex_init(&context->command_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(context->session));
}
static void uninit_context(chromakey_context_t *context)
{
switch_img_free(&context->bgimg);
switch_img_free(&context->bgimg_scaled);
+ if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
+ switch_core_file_close(&context->vfh);
+ memset(&context->vfh, 0, sizeof(context->vfh));
+ }
}
static void parse_params(chromakey_context_t *context, int start, int argc, char **argv, const char **function, switch_media_bug_flag_t *flags)
int n = argc - start;
int i = start;
+ switch_mutex_lock(context->command_mutex);
+
if (n > 0 && argv[i]) { // color
- switch_color_set_rgb(&context->mask, argv[i]);
+ int j = 0;
+ char *list[MAX_MASK];
+ int list_argc;
+
+ list_argc = switch_split(argv[i], ':', list);
+
+ context->mask_len = 0;
+ memset(context->thresholds, 0, sizeof(context->thresholds[0]) * MAX_MASK);
+
+ for (j = 0; j < list_argc; j++) {
+ char *p;
+ int thresh = 0;
+
+ if ((p = strchr(list[j], '+'))) {
+ *p++ = '\0';
+ thresh = atoi(p);
+ if (thresh < 0) thresh = 0;
+ }
+
+ switch_color_set_rgb(&context->mask[j], list[j]);
+ if (thresh) {
+ context->thresholds[j] = thresh;
+ }
+ context->mask_len++;
+ }
}
i++;
if (n > 1 && argv[i]) { // thresh
int thresh = atoi(argv[i]);
+ int j;
+
+ if (thresh > 0) {
+ context->threshold = thresh;
- if (thresh > 0) context->threshold = thresh;
+ for (j = 0; j < context->mask_len; j++) {
+ if (!context->thresholds[j]) context->thresholds[j] = context->threshold;
+ }
+ }
}
i++;
if (n > 2 && argv[i]) {
+
+ if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
+ switch_core_file_close(&context->vfh);
+ memset(&context->vfh, 0, sizeof(context->vfh));
+ }
+
if (context->bgimg) {
switch_img_free(&context->bgimg);
}
+
if (context->bgimg_scaled) {
switch_img_free(&context->bgimg_scaled);
}
+
if (argv[i][0] == '#') { // bgcolor
switch_color_set_rgb(&context->bgcolor, argv[i]);
+ } else if (switch_stristr(".png", argv[i])) {
+ if (!(context->bgimg = switch_img_read_png(argv[i], SWITCH_IMG_FMT_I420))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening png\n");
+ }
} else {
- context->bgimg = switch_img_read_png(argv[i], SWITCH_IMG_FMT_I420);
+
+ if (switch_core_file_open(&context->vfh, argv[i], 1, 8000,
+ SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO,
+ switch_core_session_get_pool(context->session)) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening video file\n");
+ } else {
+ switch_vid_params_t vp = { 0 };
+
+ switch_core_media_get_vid_params(context->session, &vp);
+ context->vfh.mm.scale_w = vp.width;
+ context->vfh.mm.scale_h = vp.height;
+ context->vfh.mm.fps = vp.fps;
+ }
}
}
}
i++;
+
+ switch_mutex_unlock(context->command_mutex);
+
+ switch_core_session_request_video_refresh(context->session);
+ switch_core_media_gen_key_frame(context->session);
+
}
static switch_status_t video_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
return SWITCH_STATUS_SUCCESS;
}
+ switch_mutex_lock(context->command_mutex);
+
data = malloc(frame->img->d_w * frame->img->d_h * 4);
switch_assert(data);
switch_img_to_raw(frame->img, data, frame->img->d_w * 4, SWITCH_IMG_FMT_ARGB);
img = switch_img_wrap(NULL, SWITCH_IMG_FMT_ARGB, frame->img->d_w, frame->img->d_h, 1, data);
switch_assert(img);
- switch_img_chromakey(img, &context->mask, context->threshold);
+ switch_img_chromakey_multi(img, context->mask, context->thresholds, context->mask_len);
if (context->bgimg) {
if (context->bgimg_scaled && (context->bgimg_scaled->d_w != frame->img->d_w || context->bgimg_scaled->d_h != frame->img->d_h)) {
}
switch_img_patch(frame->img, context->bgimg_scaled, 0, 0);
+ } else if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
+ switch_image_t *use_img = NULL;
+ switch_frame_t file_frame = { 0 };
+ switch_status_t status;
+
+ context->vfh.mm.scale_w = frame->img->d_w;
+ context->vfh.mm.scale_h = frame->img->d_h;
+
+ status = switch_core_file_read_video(&context->vfh, &file_frame, SVR_FLUSH);
+ switch_core_file_command(&context->vfh, SCFC_FLUSH_AUDIO);
+
+ if (file_frame.img) {
+ switch_img_free(&context->bgimg_scaled);
+ use_img = context->bgimg_scaled = file_frame.img;
+ } else {
+ use_img = context->bgimg_scaled;
+ }
+
+ if (use_img) {
+ switch_img_patch(frame->img, use_img, 0, 0);
+ }
+
+ if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+ int close = 1;
+
+ if (context->vfh.params) {
+ const char *loopstr = switch_event_get_header(context->vfh.params, "loop");
+ if (switch_true(loopstr)) {
+ uint32_t pos = 0;
+ switch_core_file_seek(&context->vfh, &pos, 0, SEEK_SET);
+ close = 0;
+ }
+ }
+
+ if (close) {
+ switch_core_file_close(&context->vfh);
+ }
+ }
+
+
} else {
switch_img_fill(frame->img, 0, 0, img->d_w, img->d_h, &context->bgcolor);
}
switch_img_free(&img);
free(data);
+ switch_mutex_unlock(context->command_mutex);
+
return SWITCH_STATUS_SUCCESS;
}
*/
static inline int switch_color_distance(switch_rgb_color_t *c1, switch_rgb_color_t *c2);
+/*!\brief compute distance between a color and a list of colors
+*
+* \param[in] c1 RGB color1
+* \param[in] clist RGB color list
+* \param[in] count number of colors in list
+* \param[in] threshold hint of target threshold to stop processing list
+*/
+static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, int *thresholds);
+
/*!\brief Draw a pixel on an image
*
* \param[in] img Image descriptor
#endif
}
+SWITCH_DECLARE(void) switch_img_chromakey_multi(switch_image_t *img, switch_rgb_color_t *mask, int *thresholds, int count)
+{
+ uint8_t *pixel, *last_pixel = NULL;
+ int last_hits = 0;
+ switch_assert(img);
+
+ if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
+
+ pixel = img->planes[SWITCH_PLANE_PACKED];
+
+ for (; pixel < (img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h * 4); pixel += 4) {
+ switch_rgb_color_t *color = (switch_rgb_color_t *)pixel;
+ int hits = 0;
+
+ if (last_pixel && (*(uint32_t *)pixel & 0xFFFFFF) == (*(uint32_t *)last_pixel & 0xFFFFFF)) {
+ hits = last_hits;
+ } else {
+ hits = switch_color_distance_multi(color, mask, count, thresholds);
+ }
+
+ last_hits = hits;
+ last_pixel = pixel;
+
+ if (hits) {
+ *pixel = 0;
+ }
+ }
+
+ return;
+}
+
SWITCH_DECLARE(void) switch_img_chromakey(switch_image_t *img, switch_rgb_color_t *mask, int threshold)
{
- uint8_t *pixel;
+ uint8_t *pixel, *last_pixel = NULL;
+ int last_threshold = 0;
switch_assert(img);
if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
for (; pixel < (img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h * 4); pixel += 4) {
switch_rgb_color_t *color = (switch_rgb_color_t *)pixel;
- int distance = switch_color_distance(color, mask);
+ int threshold = 0;
- if (distance <= threshold) {
+ if (last_pixel && (*(uint32_t *)pixel & 0xFFFFFF) == (*(uint32_t *)last_pixel & 0xFFFFFF)) {
+ threshold = last_threshold;
+ } else {
+ threshold = switch_color_distance(color, mask);
+ }
+
+ last_threshold = threshold;
+ last_pixel = pixel;
+
+ if (threshold) {
*pixel = 0;
}
}
return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
+static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, int *thresholds)
+{
+ int x = 0, hits = 0;
+
+ for (x = 0; x < count; x++) {
+ int distance = switch_color_distance(c1, &clist[x]);
+
+ if (distance < thresholds[x]) {
+ hits++;
+ }
+ }
+
+ return hits;
+}
+
+
+
#define CLAMP(val) MAX(0, MIN(val, 255))
#ifdef SWITCH_HAVE_YUV