]> git.ipfire.org Git - thirdparty/freeswitch.git/blob - src/mod/applications/mod_video_filter/mod_video_filter.c
[mod_video_filter] Disable waiting video ready which blocks the channel 10 seconds...
[thirdparty/freeswitch.git] / src / mod / applications / mod_video_filter / mod_video_filter.c
1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2017, Seven Du <dujinfang@gmail.com>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Seven Du <dujinfang@gmail.com>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Seven Du <dujinfang@gmail.com>
27 * Anthony Minessale <anthm@freeswitch.org>
28 *
29 * mod_video_filter -- FS Video Codec / File Format using libav.org
30 *
31 */
32
33 #include <switch.h>
34
35 switch_loadable_module_interface_t *MODULE_INTERFACE;
36
37 SWITCH_MODULE_LOAD_FUNCTION(mod_video_filter_load);
38 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_video_filter_shutdown);
39 SWITCH_MODULE_DEFINITION(mod_video_filter, mod_video_filter_load, mod_video_filter_shutdown, NULL);
40
41 typedef struct chromakey_context_s {
42 int threshold;
43 switch_image_t *bgimg;
44 switch_image_t *bgimg_orig;
45 switch_image_t *bgimg_scaled;
46 switch_image_t *fgimg_scaled;
47 switch_image_t *imgfg;
48 switch_image_t *imgbg;
49 void *data;
50 void *patch_data;
51 switch_size_t datalen;
52 switch_size_t patch_datalen;
53 switch_file_handle_t vfh;
54 switch_file_handle_t fg_vfh;
55 switch_rgb_color_t bgcolor;
56 switch_core_session_t *session;
57 switch_mutex_t *command_mutex;
58 int patch;
59 int mod;
60 switch_chromakey_t *ck;
61 switch_core_video_filter_t video_filters;
62 switch_queue_t *child_queue;
63 char *child_uuid;
64 switch_media_bug_t *child_bug;
65 } chromakey_context_t;
66
67 typedef struct chromakey_child_context_s {
68 chromakey_context_t *parent;
69 char *master_uuid;
70 } chromakey_child_context_t;
71
72
73 typedef struct video_replace_context_s {
74 switch_image_t *rp_img;
75 switch_file_handle_t vfh;
76 switch_core_session_t *session;
77 } video_replace_context_t;
78
79
80 static void init_context(chromakey_context_t *context)
81 {
82 switch_color_set_rgb(&context->bgcolor, "#000000");
83 context->threshold = 300;
84 switch_mutex_init(&context->command_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(context->session));
85 switch_chromakey_create(&context->ck);
86 }
87
88 static void uninit_context(chromakey_context_t *context)
89 {
90 switch_img_free(&context->bgimg);
91 switch_img_free(&context->bgimg_orig);
92 switch_img_free(&context->bgimg_scaled);
93 switch_img_free(&context->fgimg_scaled);
94 switch_img_free(&context->imgbg);
95 switch_img_free(&context->imgfg);
96
97 if (context->child_bug) {
98 switch_core_media_bug_close(&context->child_bug, SWITCH_TRUE);
99 context->child_uuid = NULL;
100 }
101
102 if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
103 switch_core_file_close(&context->vfh);
104 memset(&context->vfh, 0, sizeof(context->vfh));
105 }
106
107 if (switch_test_flag(&context->fg_vfh, SWITCH_FILE_OPEN)) {
108 switch_core_file_close(&context->fg_vfh);
109 memset(&context->vfh, 0, sizeof(context->fg_vfh));
110 }
111
112 switch_safe_free(context->data);
113 switch_safe_free(context->patch_data);
114 switch_chromakey_destroy(&context->ck);
115 }
116
117 static int flush_video_queue(switch_queue_t *q, int min)
118 {
119 void *pop;
120
121 if (switch_queue_size(q) > min) {
122 while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) {
123 switch_image_t *img = (switch_image_t *) pop;
124 switch_img_free(&img);
125 if (min && switch_queue_size(q) <= min) {
126 break;
127 }
128 }
129 }
130
131 return switch_queue_size(q);
132 }
133
134 static switch_bool_t chromakey_child_bug_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
135 {
136 chromakey_child_context_t *child_context = (chromakey_child_context_t *)user_data;
137
138 switch (type) {
139 case SWITCH_ABC_TYPE_INIT:
140 {
141 }
142 break;
143 case SWITCH_ABC_TYPE_CLOSE:
144 {
145 }
146 break;
147 case SWITCH_ABC_TYPE_READ_VIDEO_PING:
148 case SWITCH_ABC_TYPE_VIDEO_PATCH:
149 {
150 switch_image_t *img = NULL;
151 switch_frame_t *frame = switch_core_media_bug_get_video_ping_frame(bug);
152
153 if (frame && frame->img) {
154 switch_img_copy(frame->img, &img);
155 if (switch_queue_trypush(child_context->parent->child_queue, img) != SWITCH_STATUS_SUCCESS) {
156 switch_img_free(&img);
157 }
158 img = NULL;
159 }
160 }
161 break;
162 default:
163 break;
164 }
165
166 return SWITCH_TRUE;
167 }
168
169
170
171
172 static void parse_params(chromakey_context_t *context, int start, int argc, char **argv, const char **function, switch_media_bug_flag_t *flags)
173 {
174 int n = argc - start;
175 int i = start;
176
177 switch_mutex_lock(context->command_mutex);
178
179 if (!context->ck) {
180 switch_mutex_unlock(context->command_mutex);
181 return;
182 }
183
184 context->patch = 0;
185
186 if (n > 0 && argv[i]) { // color
187 int j = 0;
188 char *list[CHROMAKEY_MAX_MASK];
189 int list_argc;
190
191 list_argc = switch_split(argv[i], ':', list);
192
193 switch_chromakey_clear_colors(context->ck);
194
195 for (j = 0; j < list_argc; j++) {
196 char *p;
197 int thresh = 0;
198 switch_rgb_color_t color = { 0 };
199
200 if ((p = strchr(list[j], '+'))) {
201 *p++ = '\0';
202 thresh = atoi(p);
203 if (thresh < 0) thresh = 0;
204 }
205
206 if (*list[j] == '#') {
207 switch_color_set_rgb(&color, list[j]);
208 switch_chromakey_add_color(context->ck, &color, thresh);
209 } else {
210 switch_chromakey_autocolor(context->ck, switch_chromakey_str2shade(context->ck, list[j]), thresh);
211 }
212 }
213 }
214
215 i++;
216
217 if (n > 1 && argv[i]) { // thresh
218 int thresh = atoi(argv[i]);
219
220 if (thresh > 0) {
221 switch_chromakey_set_default_threshold(context->ck, thresh);
222 }
223 }
224
225 i++;
226
227 if (n > 2 && argv[i]) {
228
229 if (context->child_bug) {
230 printf("WTF CLOSE IT\n");
231 switch_core_media_bug_close(&context->child_bug, SWITCH_TRUE);
232 context->child_uuid = NULL;
233 }
234
235 if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
236 switch_core_file_close(&context->vfh);
237 memset(&context->vfh, 0, sizeof(context->vfh));
238 }
239
240 if (context->bgimg_orig) {
241 switch_img_free(&context->bgimg_orig);
242 }
243
244 if (context->bgimg) {
245 switch_img_free(&context->bgimg);
246 }
247
248 if (context->bgimg_scaled) {
249 switch_img_free(&context->bgimg_scaled);
250 }
251
252 if (context->fgimg_scaled) {
253 switch_img_free(&context->fgimg_scaled);
254 }
255
256
257 if (argv[i][0] == '#') { // bgcolor
258 switch_color_set_rgb(&context->bgcolor, argv[i]);
259 } else if (!strncasecmp(argv[i], "uuid:", 5)) {
260 char *uuid = argv[i] + 5;
261 switch_core_session_t *bsession;
262 switch_status_t status;
263
264
265 if (!zstr(uuid) && (bsession = switch_core_session_locate(uuid))) {
266 switch_channel_t *channel = switch_core_session_get_channel(bsession);
267 chromakey_child_context_t *child_context;
268 switch_media_bug_flag_t flags = SMBF_READ_VIDEO_PING|SMBF_READ_VIDEO_PATCH;
269
270 switch_channel_wait_for_flag(channel, CF_VIDEO_READY, SWITCH_TRUE, 10000, NULL);
271
272 child_context = (chromakey_child_context_t *) switch_core_session_alloc(bsession, sizeof(*child_context));
273 child_context->master_uuid = switch_core_session_strdup(bsession, switch_core_session_get_uuid(context->session));
274
275
276 if ((status = switch_core_media_bug_add(bsession, "chromakey_child", NULL,
277 chromakey_child_bug_callback, child_context, 0, flags,
278 &context->child_bug)) == SWITCH_STATUS_SUCCESS) {
279
280 switch_queue_create(&context->child_queue, 200, switch_core_session_get_pool(context->session));
281 child_context->parent = context;
282 context->child_uuid = switch_core_session_strdup(context->session, switch_core_session_get_uuid(bsession));
283 } else {
284 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(bsession), SWITCH_LOG_ERROR, "Failure! %d\n", status);
285 }
286
287 switch_core_session_rwunlock(bsession);
288 }
289
290 } else if (switch_stristr(".png", argv[i])) {
291 if (!(context->bgimg_orig = switch_img_read_png(argv[i], SWITCH_IMG_FMT_ARGB))) {
292 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening png\n");
293 }
294 } else {
295
296 if (switch_core_file_open(&context->vfh, argv[i], 1, 8000,
297 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO,
298 switch_core_session_get_pool(context->session)) != SWITCH_STATUS_SUCCESS) {
299 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening video file\n");
300 } else {
301 switch_vid_params_t vp = { 0 };
302
303 switch_core_media_get_vid_params(context->session, &vp);
304 context->vfh.mm.scale_w = vp.width;
305 context->vfh.mm.scale_h = vp.height;
306 context->vfh.mm.fps = vp.fps;
307 }
308 }
309 }
310
311
312
313 while (n > 3 && argv[i]) {
314
315 if (!strncasecmp(argv[i], "filter:", 7)) {
316 char *filter = argv[i] + 7;
317 switch_core_video_parse_filter_string(&context->video_filters, filter);
318
319 if (context->bgimg_orig && context->video_filters) {
320 switch_img_free(&context->bgimg);
321 switch_img_copy(context->bgimg_orig, &context->bgimg);
322
323 if (context->video_filters & SCV_FILTER_SEPIA_BG) {
324 switch_img_sepia(context->bgimg, 0, 0, context->bgimg->d_w, context->bgimg->d_h);
325 }
326
327 if (context->video_filters & SCV_FILTER_GRAY_BG) {
328 switch_img_sepia(context->bgimg, 0, 0, context->bgimg->d_w, context->bgimg->d_h);
329 }
330 }
331 }
332
333
334 if (!strncasecmp(argv[i], "fgvid:", 6)) {
335 char *file = argv[i] + 6;
336
337 if (switch_test_flag(&context->fg_vfh, SWITCH_FILE_OPEN)) {
338 switch_core_file_close(&context->fg_vfh);
339 memset(&context->fg_vfh, 0, sizeof(context->fg_vfh));
340 }
341
342 if (!zstr(file)) {
343 if (switch_core_file_open(&context->fg_vfh, file, 1, 8000,
344 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO,
345 switch_core_session_get_pool(context->session)) != SWITCH_STATUS_SUCCESS) {
346 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening video file\n");
347 } else {
348 switch_vid_params_t vp = { 0 };
349
350 switch_core_media_get_vid_params(context->session, &vp);
351 context->fg_vfh.mm.scale_w = vp.width;
352 context->fg_vfh.mm.scale_h = vp.height;
353 context->fg_vfh.mm.fps = vp.fps;
354 }
355 }
356 }
357
358 if (!strncasecmp(argv[i], "fg:", 3)) {
359 switch_img_free(&context->imgfg);
360 if (!zstr(argv[i]+3)) {
361 context->imgfg = switch_img_read_png(argv[i]+3, SWITCH_IMG_FMT_ARGB);
362 }
363 }
364
365 if (!strncasecmp(argv[i], "bg:", 3)) {
366 switch_img_free(&context->imgbg);
367 if (!zstr(argv[i]+3)) {
368 context->imgbg = switch_img_read_png(argv[i]+3, SWITCH_IMG_FMT_ARGB);
369 }
370 }
371
372 if (!strcasecmp(argv[i], "patch")) {
373 *function = "patch:video";
374 *flags = SMBF_VIDEO_PATCH;
375 context->patch++;
376 }
377
378 i++;
379 }
380
381 if (context->bgimg_orig && !context->bgimg) {
382 switch_img_copy(context->bgimg_orig, &context->bgimg);
383 }
384
385 switch_core_session_request_video_refresh(context->session);
386 context->mod++;
387
388 switch_mutex_unlock(context->command_mutex);
389
390
391
392 }
393
394 static switch_status_t video_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
395 {
396 chromakey_context_t *context = (chromakey_context_t *)user_data;
397 switch_channel_t *channel = switch_core_session_get_channel(session);
398 switch_image_t *img = NULL;
399 switch_size_t bytes;
400 void *patch_data;
401
402 if (!switch_channel_ready(channel)) {
403 return SWITCH_STATUS_FALSE;
404 }
405
406 if (!frame->img) {
407 return SWITCH_STATUS_SUCCESS;
408 }
409
410 if (!context->patch && context->mod && !switch_test_flag(frame, SFF_IS_KEYFRAME)) {
411 switch_core_session_request_video_refresh(context->session);
412 return SWITCH_STATUS_SUCCESS;
413 }
414
415 context->mod = 0;
416
417 if (switch_mutex_trylock(context->command_mutex) != SWITCH_STATUS_SUCCESS) {
418 switch_image_t *last_img = switch_chromakey_cache_image(context->ck);
419
420 if (last_img) {
421 switch_img_patch(frame->img, last_img, 0, 0);
422 }
423
424 return SWITCH_STATUS_SUCCESS;
425 }
426
427 bytes = frame->img->d_w * frame->img->d_h * 4;
428
429 if (bytes > context->datalen) {
430 context->data = realloc(context->data, bytes);
431 context->datalen = bytes;
432 }
433
434 switch_assert(context->data);
435
436 patch_data = context->data;
437
438 if (context->video_filters & SCV_FILTER_8BIT_FG) {
439 switch_image_t *tmp = NULL;
440 int w = frame->img->d_w, h = frame->img->d_h;
441
442 switch_img_scale(frame->img, &tmp, w/8 ,h/8);
443 switch_img_scale(tmp, &frame->img, w,h);
444 switch_img_8bit(frame->img);
445 }
446
447
448 switch_img_to_raw(frame->img, context->data, frame->img->d_w * 4, SWITCH_IMG_FMT_ARGB);
449 img = switch_img_wrap(NULL, SWITCH_IMG_FMT_ARGB, frame->img->d_w, frame->img->d_h, 1, context->data);
450
451
452 switch_assert(img);
453 switch_chromakey_process(context->ck, img);
454
455 if (context->video_filters & SCV_FILTER_GRAY_FG) {
456 switch_img_gray(img, 0, 0, img->d_w, img->d_h);
457 }
458
459 if (context->video_filters & SCV_FILTER_SEPIA_FG) {
460 switch_img_sepia(img, 0, 0, img->d_w, img->d_h);
461 }
462
463
464 if (context->bgimg) {
465 switch_image_t *tmp = NULL;
466
467 if (context->bgimg_scaled && (context->bgimg_scaled->d_w != frame->img->d_w || context->bgimg_scaled->d_h != frame->img->d_h)) {
468 switch_img_free(&context->bgimg_scaled);
469 }
470
471 if (!context->bgimg_scaled) {
472 switch_img_scale(context->bgimg, &context->bgimg_scaled, frame->img->d_w, frame->img->d_h);
473 }
474
475 if (context->imgbg) {
476 switch_img_copy(img, &tmp);
477 }
478
479 switch_img_patch_rgb(img, context->bgimg_scaled, 0, 0, SWITCH_TRUE);
480
481 if (context->imgbg) {
482 int x = 0, y = 0;
483
484 if (context->imgbg->d_w != frame->img->d_w && context->imgbg->d_h != frame->img->d_h) {
485 switch_img_fit(&context->imgbg, frame->img->d_w, frame->img->d_h, SWITCH_FIT_SIZE);
486 }
487
488 switch_img_find_position(POS_CENTER_BOT, frame->img->d_w, frame->img->d_h, context->imgbg->d_w, context->imgbg->d_h, &x, &y);
489 switch_img_patch(img, context->imgbg, x, y);
490
491 if (tmp) {
492 switch_img_patch(img, tmp, 0, 0);
493 switch_img_free(&tmp);
494 }
495 }
496
497 } else if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN) || !zstr(context->child_uuid)) {
498 switch_image_t *use_img = NULL;
499 switch_frame_t file_frame = { 0 };
500 switch_status_t status;
501
502 context->vfh.mm.scale_w = frame->img->d_w;
503 context->vfh.mm.scale_h = frame->img->d_h;
504
505 if (!zstr(context->child_uuid)) {
506 void *pop = NULL;
507
508 flush_video_queue(context->child_queue, 1);
509
510 if ((status = switch_queue_trypop(context->child_queue, &pop)) == SWITCH_STATUS_SUCCESS && pop) {
511 file_frame.img = (switch_image_t *) pop;
512
513 if (file_frame.img->d_w != context->vfh.mm.scale_w || file_frame.img->d_h != context->vfh.mm.scale_h) {
514 switch_img_fit(&file_frame.img, context->vfh.mm.scale_w, context->vfh.mm.scale_h, SWITCH_FIT_SIZE_AND_SCALE);
515 if (file_frame.img->d_w != context->vfh.mm.scale_w || file_frame.img->d_h != context->vfh.mm.scale_h) {
516 switch_img_free(&file_frame.img);
517 }
518 }
519 }
520
521 } else {
522 status = switch_core_file_read_video(&context->vfh, &file_frame, SVR_FLUSH);
523 switch_core_file_command(&context->vfh, SCFC_FLUSH_AUDIO);
524 }
525
526 if (file_frame.img) {
527 switch_img_free(&context->bgimg_scaled);
528 use_img = context->bgimg_scaled = file_frame.img;
529
530 if (context->video_filters & SCV_FILTER_SEPIA_BG) {
531 switch_img_sepia(use_img, 0, 0, use_img->d_w, use_img->d_h);
532 }
533
534 if (context->video_filters & SCV_FILTER_GRAY_BG) {
535 switch_img_sepia(use_img, 0, 0, use_img->d_w, use_img->d_h);
536 }
537
538 } else {
539 use_img = context->bgimg_scaled;
540 }
541
542 if (use_img) {
543 switch_image_t *i2;
544
545 bytes = use_img->d_w * use_img->d_h * 4;
546
547 if (bytes > context->patch_datalen) {
548 context->patch_data = realloc(context->patch_data, bytes);
549 context->patch_datalen = bytes;
550 }
551
552 switch_img_to_raw(use_img, context->patch_data, use_img->d_w * 4, SWITCH_IMG_FMT_ARGB);
553 i2 = switch_img_wrap(NULL, SWITCH_IMG_FMT_ARGB, use_img->d_w, use_img->d_h, 1, context->patch_data);
554
555
556
557 if (context->imgbg) {
558 int x = 0, y = 0;
559
560 if (context->imgbg->d_w != frame->img->d_w && context->imgbg->d_h != frame->img->d_h) {
561 switch_img_fit(&context->imgbg, frame->img->d_w, frame->img->d_h, SWITCH_FIT_SIZE);
562 }
563 switch_img_find_position(POS_CENTER_BOT, frame->img->d_w, frame->img->d_h, context->imgbg->d_w, context->imgbg->d_h, &x, &y);
564 switch_img_patch(i2, context->imgbg, x, y);
565 }
566
567 switch_img_patch(i2, img, 0, 0);
568 switch_img_free(&img);
569 img = i2;
570 patch_data = context->patch_data;
571 }
572
573 if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
574 int close = 1;
575
576 if (context->vfh.params) {
577 const char *loopstr = switch_event_get_header(context->vfh.params, "loop");
578 if (switch_true(loopstr)) {
579 uint32_t pos = 0;
580 switch_core_file_seek(&context->vfh, &pos, 0, SEEK_SET);
581 close = 0;
582 }
583 }
584
585 if (close) {
586 switch_core_file_close(&context->vfh);
587 }
588 }
589
590
591 } else {
592 switch_img_fill_noalpha(img, 0, 0, img->d_w, img->d_h, &context->bgcolor);
593 }
594
595 if (context->imgfg) {
596 int x = 0, y = 0;
597
598 if (context->imgfg->d_w != frame->img->d_w && context->imgfg->d_h != frame->img->d_h) {
599 switch_img_fit(&context->imgfg, frame->img->d_w, frame->img->d_h, SWITCH_FIT_SIZE);
600 }
601 switch_img_find_position(POS_CENTER_BOT, frame->img->d_w, frame->img->d_h, context->imgfg->d_w, context->imgfg->d_h, &x, &y);
602 switch_img_patch(img, context->imgfg, x, y);
603 }
604
605 if (switch_test_flag(&context->fg_vfh, SWITCH_FILE_OPEN)) {
606 switch_frame_t file_frame = { 0 };
607 switch_status_t status;
608 switch_image_t *use_img = NULL;
609
610 context->fg_vfh.mm.scale_w = frame->img->d_w;
611 context->fg_vfh.mm.scale_h = frame->img->d_h;
612
613 status = switch_core_file_read_video(&context->fg_vfh, &file_frame, SVR_FLUSH);
614 switch_core_file_command(&context->fg_vfh, SCFC_FLUSH_AUDIO);
615
616
617 if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
618 int close = 1;
619
620 if (context->fg_vfh.params) {
621 const char *loopstr = switch_event_get_header(context->fg_vfh.params, "loop");
622 if (switch_true(loopstr)) {
623 uint32_t pos = 0;
624 switch_core_file_seek(&context->fg_vfh, &pos, 0, SEEK_SET);
625 close = 0;
626 }
627 }
628
629 if (close) {
630 switch_core_file_close(&context->fg_vfh);
631 }
632 }
633
634 if (file_frame.img) {
635 switch_img_free(&context->fgimg_scaled);
636 use_img = context->fgimg_scaled = file_frame.img;
637 } else {
638 use_img = context->fgimg_scaled;
639 }
640
641 if (use_img) {
642 switch_img_patch(img, use_img, 0, 0);
643 }
644
645 }
646
647
648 switch_img_from_raw(frame->img, patch_data, SWITCH_IMG_FMT_ARGB, frame->img->d_w, frame->img->d_h);
649
650 switch_img_free(&img);
651
652 switch_mutex_unlock(context->command_mutex);
653
654 return SWITCH_STATUS_SUCCESS;
655 }
656
657 static switch_bool_t chromakey_bug_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
658 {
659 chromakey_context_t *context = (chromakey_context_t *)user_data;
660
661 switch (type) {
662 case SWITCH_ABC_TYPE_INIT:
663 {
664 }
665 break;
666 case SWITCH_ABC_TYPE_CLOSE:
667 {
668 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
669 uninit_context(context);
670 }
671 break;
672 case SWITCH_ABC_TYPE_READ_VIDEO_PING:
673 case SWITCH_ABC_TYPE_VIDEO_PATCH:
674 {
675 switch_frame_t *frame = switch_core_media_bug_get_video_ping_frame(bug);
676 video_thread_callback(context->session, frame, context);
677 }
678 break;
679 default:
680 break;
681 }
682
683 return SWITCH_TRUE;
684 }
685
686 #define CHROMAKEY_APP_SYNTAX "<#mask_color> [threshold] [#bg_color|path/to/image.png]"
687 SWITCH_STANDARD_APP(chromakey_start_function)
688 {
689 switch_media_bug_t *bug;
690 switch_status_t status;
691 switch_channel_t *channel = switch_core_session_get_channel(session);
692 char *argv[4] = { 0 };
693 int argc;
694 char *lbuf;
695 switch_media_bug_flag_t flags = SMBF_READ_VIDEO_PING|SMBF_READ_VIDEO_PATCH;
696 const char *function = "chromakey";
697 chromakey_context_t *context;
698
699 if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_chromakey_bug_"))) {
700 if (!zstr(data) && !strcasecmp(data, "stop")) {
701 switch_channel_set_private(channel, "_chromakey_bug_", NULL);
702 switch_core_media_bug_remove(session, &bug);
703 } else {
704 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 chromakey at once on the same channel!\n");
705 }
706 return;
707 }
708
709 switch_channel_wait_for_flag(channel, CF_VIDEO_READY, SWITCH_TRUE, 10000, NULL);
710
711 context = (chromakey_context_t *) switch_core_session_alloc(session, sizeof(*context));
712 switch_assert(context != NULL);
713 memset(context, 0, sizeof(*context));
714 context->session = session;
715 init_context(context);
716
717 if (data && (lbuf = switch_core_session_strdup(session, data))
718 && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
719 parse_params(context, 1, argc, argv, &function, &flags);
720 }
721
722 switch_thread_rwlock_rdlock(MODULE_INTERFACE->rwlock);
723
724 if ((status = switch_core_media_bug_add(session, function, NULL, chromakey_bug_callback, context, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
725 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure!\n");
726 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
727 return;
728 }
729
730 switch_channel_set_private(channel, "_chromakey_bug_", bug);
731 }
732
733 /* API Interface Function */
734 #define CHROMAKEY_API_SYNTAX "<uuid> [start|stop] " CHROMAKEY_APP_SYNTAX
735 SWITCH_STANDARD_API(chromakey_api_function)
736 {
737 switch_core_session_t *rsession = NULL;
738 switch_channel_t *channel = NULL;
739 switch_media_bug_t *bug;
740 switch_status_t status;
741 chromakey_context_t *context;
742 char *mycmd = NULL;
743 int argc = 0;
744 char *argv[25] = { 0 };
745 char *uuid = NULL;
746 char *action = NULL;
747 switch_media_bug_flag_t flags = SMBF_READ_VIDEO_PING | SMBF_READ_VIDEO_PATCH;
748 const char *function = "chromakey";
749
750 if (zstr(cmd)) {
751 goto usage;
752 }
753
754 if (!(mycmd = strdup(cmd))) {
755 goto usage;
756 }
757
758 if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) {
759 goto usage;
760 }
761
762 uuid = argv[0];
763 action = argv[1];
764
765 if (!(rsession = switch_core_session_locate(uuid))) {
766 stream->write_function(stream, "-ERR Cannot locate session!\n");
767 goto done;
768 }
769
770 channel = switch_core_session_get_channel(rsession);
771
772 if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_chromakey_bug_"))) {
773 if (!zstr(action)) {
774 if (!strcasecmp(action, "stop")) {
775 switch_channel_set_private(channel, "_chromakey_bug_", NULL);
776 switch_core_media_bug_remove(rsession, &bug);
777 stream->write_function(stream, "+OK Success\n");
778 } else if (!strcasecmp(action, "start")) {
779 context = (chromakey_context_t *) switch_core_media_bug_get_user_data(bug);
780 switch_assert(context);
781 parse_params(context, 2, argc, argv, &function, &flags);
782 stream->write_function(stream, "+OK Success\n");
783 }
784 } else {
785 stream->write_function(stream, "-ERR Invalid action\n");
786 }
787 goto done;
788 }
789
790 if (!zstr(action) && strcasecmp(action, "start")) {
791 goto usage;
792 }
793
794 context = (chromakey_context_t *) switch_core_session_alloc(rsession, sizeof(*context));
795 switch_assert(context != NULL);
796 context->session = rsession;
797
798 init_context(context);
799 parse_params(context, 2, argc, argv, &function, &flags);
800
801 switch_thread_rwlock_rdlock(MODULE_INTERFACE->rwlock);
802
803 if ((status = switch_core_media_bug_add(rsession, function, NULL,
804 chromakey_bug_callback, context, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
805 stream->write_function(stream, "-ERR Failure!\n");
806 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
807 goto done;
808 } else {
809 switch_channel_set_private(channel, "_chromakey_bug_", bug);
810 stream->write_function(stream, "+OK Success\n");
811 goto done;
812 }
813
814 usage:
815 stream->write_function(stream, "-USAGE: %s\n", CHROMAKEY_API_SYNTAX);
816
817 done:
818 if (rsession) {
819 switch_core_session_rwunlock(rsession);
820 }
821
822 switch_safe_free(mycmd);
823 return SWITCH_STATUS_SUCCESS;
824 }
825
826 static switch_status_t video_replace_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data, switch_abc_type_t type)
827 {
828 video_replace_context_t *context = (video_replace_context_t *)user_data;
829 switch_channel_t *channel = switch_core_session_get_channel(session);
830 switch_frame_t file_frame = { 0 };
831
832 if (!switch_channel_ready(channel)) {
833 return SWITCH_STATUS_FALSE;
834 }
835
836 if (!frame->img) {
837 return SWITCH_STATUS_SUCCESS;
838 }
839
840 if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
841 switch_status_t status = SWITCH_STATUS_FALSE;
842
843 if (type == SWITCH_ABC_TYPE_READ_VIDEO_PING || (context->vfh.params && switch_true(switch_event_get_header(context->vfh.params, "scale")))) {
844 context->vfh.mm.scale_w = frame->img->d_w;
845 context->vfh.mm.scale_h = frame->img->d_h;
846 }
847
848 status = switch_core_file_read_video(&context->vfh, &file_frame, SVR_FLUSH);
849 switch_core_file_command(&context->vfh, SCFC_FLUSH_AUDIO);
850
851 if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
852 int close = 1;
853
854 if (context->vfh.params) {
855 const char *loopstr = switch_event_get_header(context->vfh.params, "loop");
856 if (switch_true(loopstr)) {
857 uint32_t pos = 0;
858
859 if (switch_core_file_seek(&context->vfh, &pos, 0, SEEK_SET) == SWITCH_STATUS_SUCCESS) close = 0;
860 }
861 }
862
863 if (close) {
864 switch_core_file_close(&context->vfh);
865 }
866 }
867
868 if (file_frame.img) {
869 switch_img_free(&(context->rp_img));
870 context->rp_img = file_frame.img;
871 }
872
873 if (context->rp_img) {
874 if (context->rp_img->d_w != frame->img->d_w || context->rp_img->d_h != frame->img->d_h ) {
875 frame->img = NULL;
876 }
877
878 switch_img_copy(context->rp_img, &frame->img);
879 }
880 }
881
882 return SWITCH_STATUS_SUCCESS;
883 }
884
885 static switch_bool_t video_replace_bug_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
886 {
887 switch_core_session_t *session = switch_core_media_bug_get_session(bug);
888 switch_channel_t *channel = switch_core_session_get_channel(session);
889 video_replace_context_t *context = (video_replace_context_t *)user_data;
890
891 switch (type) {
892 case SWITCH_ABC_TYPE_INIT:
893 {
894 }
895 break;
896 case SWITCH_ABC_TYPE_CLOSE:
897 {
898 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
899 switch_img_free(&context->rp_img);
900
901 if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
902 switch_core_file_close(&context->vfh);
903 memset(&context->vfh, 0, sizeof(context->vfh));
904 }
905 }
906 break;
907 case SWITCH_ABC_TYPE_READ_VIDEO_PING:
908 case SWITCH_ABC_TYPE_WRITE_VIDEO_PING:
909 {
910 if (switch_test_flag(&context->vfh, SWITCH_FILE_OPEN)) {
911 switch_frame_t *frame = switch_core_media_bug_get_video_ping_frame(bug);
912 video_replace_thread_callback(context->session, frame, context, type);
913 } else {
914 switch_channel_set_private(channel, "_video_replace_bug_", NULL);
915 return SWITCH_FALSE;
916 }
917 }
918 break;
919 default:
920 break;
921 }
922
923 return SWITCH_TRUE;
924 }
925
926 SWITCH_STANDARD_APP(video_replace_start_function)
927 {
928 switch_media_bug_t *bug;
929 switch_status_t status;
930 switch_channel_t *channel = switch_core_session_get_channel(session);
931 switch_media_bug_flag_t flags = 0;
932 const char *function = "video_replace";
933 video_replace_context_t *context;
934 char *lbuf;
935 int argc = 0;
936 char *argv[2] = { 0 };
937 char *direction = NULL;
938 char *file = NULL;
939
940 if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_video_replace_bug_"))) {
941 if (!zstr(data) && !strcasecmp(data, "stop")) {
942 switch_channel_set_private(channel, "_video_replace_bug_", NULL);
943 switch_core_media_bug_remove(session, &bug);
944 } else {
945 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "alreday start!\n");
946 }
947 return;
948 }
949
950 if (data && (lbuf = switch_core_session_strdup(session, data))
951 && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) > 0) {
952
953 if (argc > 1) {
954 direction = argv[0];
955 file = argv[1];
956 } else {
957 direction = "write";
958 file = lbuf;
959 }
960
961 if (!strcasecmp(direction, "read")) {
962 flags = SMBF_READ_VIDEO_PING;
963 } else if (!strcasecmp(direction, "write")) {
964 flags = SMBF_WRITE_VIDEO_PING;
965 } else {
966 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "invalid replace direction!\n");
967 return;
968 }
969 } else {
970 return;
971 }
972
973 // switch_channel_wait_for_flag(channel, CF_VIDEO_READY, SWITCH_TRUE, 10000, NULL);
974
975 context = (video_replace_context_t *) switch_core_session_alloc(session, sizeof(*context));
976 switch_assert(context != NULL);
977 memset(context, 0, sizeof(*context));
978 context->session = session;
979
980 switch_thread_rwlock_rdlock(MODULE_INTERFACE->rwlock);
981
982 if (switch_core_file_open(&context->vfh, file, 1, 8000,
983 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO,
984 switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
985 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening video file\n");
986 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
987 return;
988 }
989
990 if ((status = switch_core_media_bug_add(session, function, NULL, video_replace_bug_callback, context, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
991 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure!\n");
992 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
993 return;
994 }
995
996 switch_channel_set_private(channel, "_video_replace_bug_", bug);
997 }
998
999 /* API Interface Function */
1000 #define VIDEO_REPLACE_API_SYNTAX "<uuid> <stop|start> [read|write] <file>"
1001 SWITCH_STANDARD_API(video_replace_api_function)
1002 {
1003 switch_core_session_t *rsession = NULL;
1004 switch_channel_t *channel = NULL;
1005 switch_media_bug_t *bug;
1006 switch_status_t status;
1007 video_replace_context_t *context;
1008 char *mycmd = NULL;
1009 int argc = 0;
1010 char *argv[4] = { 0 };
1011 char *uuid = NULL;
1012 char *action = NULL;
1013 char *file = NULL;
1014 char *direction = NULL;
1015 switch_media_bug_flag_t flags = 0;
1016 const char *function = "video_replace";
1017
1018 if (zstr(cmd)) {
1019 goto usage;
1020 }
1021
1022 if (!(mycmd = strdup(cmd))) {
1023 goto usage;
1024 }
1025
1026 if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) {
1027 goto usage;
1028 }
1029
1030 uuid = argv[0];
1031 action = argv[1];
1032
1033 if (!(rsession = switch_core_session_locate(uuid))) {
1034 stream->write_function(stream, "-ERR Cannot locate session!\n");
1035 goto done;
1036 }
1037
1038 channel = switch_core_session_get_channel(rsession);
1039
1040 bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_video_replace_bug_");
1041
1042 if (!strcasecmp(action, "stop")) {
1043 if (bug) {
1044 switch_channel_set_private(channel, "_video_replace_bug_", NULL);
1045 switch_core_media_bug_remove(rsession, &bug);
1046 stream->write_function(stream, "+OK Success\n");
1047 } else {
1048 stream->write_function(stream, "-ERR not start\n");
1049 }
1050
1051 goto done;
1052 } else if (!strcasecmp(action, "start")) {
1053 if (argc == 3) {
1054 direction = "write";
1055 file = argv[2];
1056 } else {
1057 direction = argv[2];
1058 file = argv[3];
1059 }
1060
1061 if (zstr(direction) || zstr(file)) goto usage;
1062
1063 if (!strcasecmp(direction, "read")) {
1064 flags = SMBF_READ_VIDEO_PING;
1065 } else if (!strcasecmp(direction, "write")) {
1066 flags = SMBF_WRITE_VIDEO_PING;
1067 } else {
1068 goto usage;
1069 }
1070
1071 if (bug) {
1072 stream->write_function(stream, "-ERR alreday start\n");
1073 goto done;
1074 }
1075 } else {
1076 goto usage;
1077 }
1078
1079 context = (video_replace_context_t *) switch_core_session_alloc(rsession, sizeof(*context));
1080 switch_assert(context != NULL);
1081 context->session = rsession;
1082
1083 switch_thread_rwlock_rdlock(MODULE_INTERFACE->rwlock);
1084
1085 if (switch_core_file_open(&context->vfh, file, 1, 8000,
1086 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO,
1087 switch_core_session_get_pool(rsession)) != SWITCH_STATUS_SUCCESS) {
1088 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening video file\n");
1089 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
1090 goto done;
1091 }
1092
1093 if ((status = switch_core_media_bug_add(rsession, function, NULL,
1094 video_replace_bug_callback, context, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
1095 stream->write_function(stream, "-ERR Failure!\n");
1096 switch_thread_rwlock_unlock(MODULE_INTERFACE->rwlock);
1097 goto done;
1098 } else {
1099 switch_channel_set_private(channel, "_video_replace_bug_", bug);
1100 stream->write_function(stream, "+OK Success\n");
1101 goto done;
1102 }
1103
1104 usage:
1105 stream->write_function(stream, "-USAGE: %s\n", VIDEO_REPLACE_API_SYNTAX);
1106
1107 done:
1108 if (rsession) {
1109 switch_core_session_rwunlock(rsession);
1110 }
1111
1112 switch_safe_free(mycmd);
1113 return SWITCH_STATUS_SUCCESS;
1114 }
1115
1116
1117 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_video_filter_shutdown)
1118 {
1119 return SWITCH_STATUS_SUCCESS;
1120 }
1121
1122 SWITCH_MODULE_LOAD_FUNCTION(mod_video_filter_load)
1123 {
1124 switch_application_interface_t *app_interface;
1125 switch_api_interface_t *api_interface;
1126
1127 /* connect my internal structure to the blank pointer passed to me */
1128 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
1129 MODULE_INTERFACE = *module_interface;
1130
1131 SWITCH_ADD_APP(app_interface, "chromakey", "chromakey", "chromakey bug",
1132 chromakey_start_function, CHROMAKEY_APP_SYNTAX, SAF_NONE);
1133
1134 SWITCH_ADD_API(api_interface, "chromakey", "chromakey", chromakey_api_function, CHROMAKEY_API_SYNTAX);
1135
1136 SWITCH_ADD_APP(app_interface, "video_replace", "video_replace", "video replace bug",
1137 video_replace_start_function, "[read|write] <file> | stop", SAF_NONE);
1138
1139 SWITCH_ADD_API(api_interface, "uuid_video_replace", "video_replace", video_replace_api_function, VIDEO_REPLACE_API_SYNTAX);
1140
1141 switch_console_set_complete("add chromakey ::console::list_uuid ::[start:stop");
1142 switch_console_set_complete("add uuid_video_replace ::console::list_uuid start ::[read:write");
1143 switch_console_set_complete("add uuid_video_replace ::console::list_uuid stop");
1144
1145 return SWITCH_STATUS_SUCCESS;
1146 }
1147
1148 /* For Emacs:
1149 * Local Variables:
1150 * mode:c
1151 * indent-tabs-mode:t
1152 * tab-width:4
1153 * c-basic-offset:4
1154 * End:
1155 * For VIM:
1156 * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
1157 */