2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2017, Seven Du <dujinfang@gmail.com>
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/
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
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
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.
26 * Seven Du <dujinfang@gmail.com>
27 * Anthony Minessale <anthm@freeswitch.org>
29 * mod_video_filter -- FS Video Codec / File Format using libav.org
35 switch_loadable_module_interface_t
*MODULE_INTERFACE
;
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
);
41 typedef struct chromakey_context_s
{
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
;
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
;
60 switch_chromakey_t
*ck
;
61 switch_core_video_filter_t video_filters
;
62 switch_queue_t
*child_queue
;
64 switch_media_bug_t
*child_bug
;
65 } chromakey_context_t
;
67 typedef struct chromakey_child_context_s
{
68 chromakey_context_t
*parent
;
70 } chromakey_child_context_t
;
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
;
80 static void init_context(chromakey_context_t
*context
)
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
);
88 static void uninit_context(chromakey_context_t
*context
)
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
);
97 if (context
->child_bug
) {
98 switch_core_media_bug_close(&context
->child_bug
, SWITCH_TRUE
);
99 context
->child_uuid
= NULL
;
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
));
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
));
112 switch_safe_free(context
->data
);
113 switch_safe_free(context
->patch_data
);
114 switch_chromakey_destroy(&context
->ck
);
117 static int flush_video_queue(switch_queue_t
*q
, int min
)
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
) {
131 return switch_queue_size(q
);
134 static switch_bool_t
chromakey_child_bug_callback(switch_media_bug_t
*bug
, void *user_data
, switch_abc_type_t type
)
136 chromakey_child_context_t
*child_context
= (chromakey_child_context_t
*)user_data
;
139 case SWITCH_ABC_TYPE_INIT
:
143 case SWITCH_ABC_TYPE_CLOSE
:
147 case SWITCH_ABC_TYPE_READ_VIDEO_PING
:
148 case SWITCH_ABC_TYPE_VIDEO_PATCH
:
150 switch_image_t
*img
= NULL
;
151 switch_frame_t
*frame
= switch_core_media_bug_get_video_ping_frame(bug
);
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
);
172 static void parse_params(chromakey_context_t
*context
, int start
, int argc
, char **argv
, const char **function
, switch_media_bug_flag_t
*flags
)
174 int n
= argc
- start
;
177 switch_mutex_lock(context
->command_mutex
);
180 switch_mutex_unlock(context
->command_mutex
);
186 if (n
> 0 && argv
[i
]) { // color
188 char *list
[CHROMAKEY_MAX_MASK
];
191 list_argc
= switch_split(argv
[i
], ':', list
);
193 switch_chromakey_clear_colors(context
->ck
);
195 for (j
= 0; j
< list_argc
; j
++) {
198 switch_rgb_color_t color
= { 0 };
200 if ((p
= strchr(list
[j
], '+'))) {
203 if (thresh
< 0) thresh
= 0;
206 if (*list
[j
] == '#') {
207 switch_color_set_rgb(&color
, list
[j
]);
208 switch_chromakey_add_color(context
->ck
, &color
, thresh
);
210 switch_chromakey_autocolor(context
->ck
, switch_chromakey_str2shade(context
->ck
, list
[j
]), thresh
);
217 if (n
> 1 && argv
[i
]) { // thresh
218 int thresh
= atoi(argv
[i
]);
221 switch_chromakey_set_default_threshold(context
->ck
, thresh
);
227 if (n
> 2 && argv
[i
]) {
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
;
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
));
240 if (context
->bgimg_orig
) {
241 switch_img_free(&context
->bgimg_orig
);
244 if (context
->bgimg
) {
245 switch_img_free(&context
->bgimg
);
248 if (context
->bgimg_scaled
) {
249 switch_img_free(&context
->bgimg_scaled
);
252 if (context
->fgimg_scaled
) {
253 switch_img_free(&context
->fgimg_scaled
);
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
;
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
;
270 switch_channel_wait_for_flag(channel
, CF_VIDEO_READY
, SWITCH_TRUE
, 10000, NULL
);
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
));
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
) {
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
));
284 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(bsession
), SWITCH_LOG_ERROR
, "Failure! %d\n", status
);
287 switch_core_session_rwunlock(bsession
);
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");
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");
301 switch_vid_params_t vp
= { 0 };
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
;
313 while (n
> 3 && argv
[i
]) {
315 if (!strncasecmp(argv
[i
], "filter:", 7)) {
316 char *filter
= argv
[i
] + 7;
317 switch_core_video_parse_filter_string(&context
->video_filters
, filter
);
319 if (context
->bgimg_orig
&& context
->video_filters
) {
320 switch_img_free(&context
->bgimg
);
321 switch_img_copy(context
->bgimg_orig
, &context
->bgimg
);
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
);
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
);
334 if (!strncasecmp(argv
[i
], "fgvid:", 6)) {
335 char *file
= argv
[i
] + 6;
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
));
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");
348 switch_vid_params_t vp
= { 0 };
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
;
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
);
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
);
372 if (!strcasecmp(argv
[i
], "patch")) {
373 *function
= "patch:video";
374 *flags
= SMBF_VIDEO_PATCH
;
381 if (context
->bgimg_orig
&& !context
->bgimg
) {
382 switch_img_copy(context
->bgimg_orig
, &context
->bgimg
);
385 switch_core_session_request_video_refresh(context
->session
);
388 switch_mutex_unlock(context
->command_mutex
);
394 static switch_status_t
video_thread_callback(switch_core_session_t
*session
, switch_frame_t
*frame
, void *user_data
)
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
;
402 if (!switch_channel_ready(channel
)) {
403 return SWITCH_STATUS_FALSE
;
407 return SWITCH_STATUS_SUCCESS
;
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
;
417 if (switch_mutex_trylock(context
->command_mutex
) != SWITCH_STATUS_SUCCESS
) {
418 switch_image_t
*last_img
= switch_chromakey_cache_image(context
->ck
);
421 switch_img_patch(frame
->img
, last_img
, 0, 0);
424 return SWITCH_STATUS_SUCCESS
;
427 bytes
= frame
->img
->d_w
* frame
->img
->d_h
* 4;
429 if (bytes
> context
->datalen
) {
430 context
->data
= realloc(context
->data
, bytes
);
431 context
->datalen
= bytes
;
434 switch_assert(context
->data
);
436 patch_data
= context
->data
;
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
;
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
);
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
);
453 switch_chromakey_process(context
->ck
, img
);
455 if (context
->video_filters
& SCV_FILTER_GRAY_FG
) {
456 switch_img_gray(img
, 0, 0, img
->d_w
, img
->d_h
);
459 if (context
->video_filters
& SCV_FILTER_SEPIA_FG
) {
460 switch_img_sepia(img
, 0, 0, img
->d_w
, img
->d_h
);
464 if (context
->bgimg
) {
465 switch_image_t
*tmp
= NULL
;
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
);
471 if (!context
->bgimg_scaled
) {
472 switch_img_scale(context
->bgimg
, &context
->bgimg_scaled
, frame
->img
->d_w
, frame
->img
->d_h
);
475 if (context
->imgbg
) {
476 switch_img_copy(img
, &tmp
);
479 switch_img_patch_rgb(img
, context
->bgimg_scaled
, 0, 0, SWITCH_TRUE
);
481 if (context
->imgbg
) {
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
);
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
);
492 switch_img_patch(img
, tmp
, 0, 0);
493 switch_img_free(&tmp
);
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
;
502 context
->vfh
.mm
.scale_w
= frame
->img
->d_w
;
503 context
->vfh
.mm
.scale_h
= frame
->img
->d_h
;
505 if (!zstr(context
->child_uuid
)) {
508 flush_video_queue(context
->child_queue
, 1);
510 if ((status
= switch_queue_trypop(context
->child_queue
, &pop
)) == SWITCH_STATUS_SUCCESS
&& pop
) {
511 file_frame
.img
= (switch_image_t
*) pop
;
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
);
522 status
= switch_core_file_read_video(&context
->vfh
, &file_frame
, SVR_FLUSH
);
523 switch_core_file_command(&context
->vfh
, SCFC_FLUSH_AUDIO
);
526 if (file_frame
.img
) {
527 switch_img_free(&context
->bgimg_scaled
);
528 use_img
= context
->bgimg_scaled
= file_frame
.img
;
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
);
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
);
539 use_img
= context
->bgimg_scaled
;
545 bytes
= use_img
->d_w
* use_img
->d_h
* 4;
547 if (bytes
> context
->patch_datalen
) {
548 context
->patch_data
= realloc(context
->patch_data
, bytes
);
549 context
->patch_datalen
= bytes
;
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
);
557 if (context
->imgbg
) {
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
);
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
);
567 switch_img_patch(i2
, img
, 0, 0);
568 switch_img_free(&img
);
570 patch_data
= context
->patch_data
;
573 if (status
!= SWITCH_STATUS_SUCCESS
&& status
!= SWITCH_STATUS_BREAK
) {
576 if (context
->vfh
.params
) {
577 const char *loopstr
= switch_event_get_header(context
->vfh
.params
, "loop");
578 if (switch_true(loopstr
)) {
580 switch_core_file_seek(&context
->vfh
, &pos
, 0, SEEK_SET
);
586 switch_core_file_close(&context
->vfh
);
592 switch_img_fill_noalpha(img
, 0, 0, img
->d_w
, img
->d_h
, &context
->bgcolor
);
595 if (context
->imgfg
) {
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
);
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
);
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
;
610 context
->fg_vfh
.mm
.scale_w
= frame
->img
->d_w
;
611 context
->fg_vfh
.mm
.scale_h
= frame
->img
->d_h
;
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
);
617 if (status
!= SWITCH_STATUS_SUCCESS
&& status
!= SWITCH_STATUS_BREAK
) {
620 if (context
->fg_vfh
.params
) {
621 const char *loopstr
= switch_event_get_header(context
->fg_vfh
.params
, "loop");
622 if (switch_true(loopstr
)) {
624 switch_core_file_seek(&context
->fg_vfh
, &pos
, 0, SEEK_SET
);
630 switch_core_file_close(&context
->fg_vfh
);
634 if (file_frame
.img
) {
635 switch_img_free(&context
->fgimg_scaled
);
636 use_img
= context
->fgimg_scaled
= file_frame
.img
;
638 use_img
= context
->fgimg_scaled
;
642 switch_img_patch(img
, use_img
, 0, 0);
648 switch_img_from_raw(frame
->img
, patch_data
, SWITCH_IMG_FMT_ARGB
, frame
->img
->d_w
, frame
->img
->d_h
);
650 switch_img_free(&img
);
652 switch_mutex_unlock(context
->command_mutex
);
654 return SWITCH_STATUS_SUCCESS
;
657 static switch_bool_t
chromakey_bug_callback(switch_media_bug_t
*bug
, void *user_data
, switch_abc_type_t type
)
659 chromakey_context_t
*context
= (chromakey_context_t
*)user_data
;
662 case SWITCH_ABC_TYPE_INIT
:
666 case SWITCH_ABC_TYPE_CLOSE
:
668 switch_thread_rwlock_unlock(MODULE_INTERFACE
->rwlock
);
669 uninit_context(context
);
672 case SWITCH_ABC_TYPE_READ_VIDEO_PING
:
673 case SWITCH_ABC_TYPE_VIDEO_PATCH
:
675 switch_frame_t
*frame
= switch_core_media_bug_get_video_ping_frame(bug
);
676 video_thread_callback(context
->session
, frame
, context
);
686 #define CHROMAKEY_APP_SYNTAX "<#mask_color> [threshold] [#bg_color|path/to/image.png]"
687 SWITCH_STANDARD_APP(chromakey_start_function
)
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 };
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
;
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
);
704 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session
), SWITCH_LOG_WARNING
, "Cannot run 2 chromakey at once on the same channel!\n");
709 switch_channel_wait_for_flag(channel
, CF_VIDEO_READY
, SWITCH_TRUE
, 10000, NULL
);
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
);
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
);
722 switch_thread_rwlock_rdlock(MODULE_INTERFACE
->rwlock
);
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
);
730 switch_channel_set_private(channel
, "_chromakey_bug_", bug
);
733 /* API Interface Function */
734 #define CHROMAKEY_API_SYNTAX "<uuid> [start|stop] " CHROMAKEY_APP_SYNTAX
735 SWITCH_STANDARD_API(chromakey_api_function
)
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
;
744 char *argv
[25] = { 0 };
747 switch_media_bug_flag_t flags
= SMBF_READ_VIDEO_PING
| SMBF_READ_VIDEO_PATCH
;
748 const char *function
= "chromakey";
754 if (!(mycmd
= strdup(cmd
))) {
758 if ((argc
= switch_separate_string(mycmd
, ' ', argv
, (sizeof(argv
) / sizeof(argv
[0])))) < 2) {
765 if (!(rsession
= switch_core_session_locate(uuid
))) {
766 stream
->write_function(stream
, "-ERR Cannot locate session!\n");
770 channel
= switch_core_session_get_channel(rsession
);
772 if ((bug
= (switch_media_bug_t
*) switch_channel_get_private(channel
, "_chromakey_bug_"))) {
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");
785 stream
->write_function(stream
, "-ERR Invalid action\n");
790 if (!zstr(action
) && strcasecmp(action
, "start")) {
794 context
= (chromakey_context_t
*) switch_core_session_alloc(rsession
, sizeof(*context
));
795 switch_assert(context
!= NULL
);
796 context
->session
= rsession
;
798 init_context(context
);
799 parse_params(context
, 2, argc
, argv
, &function
, &flags
);
801 switch_thread_rwlock_rdlock(MODULE_INTERFACE
->rwlock
);
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
);
809 switch_channel_set_private(channel
, "_chromakey_bug_", bug
);
810 stream
->write_function(stream
, "+OK Success\n");
815 stream
->write_function(stream
, "-USAGE: %s\n", CHROMAKEY_API_SYNTAX
);
819 switch_core_session_rwunlock(rsession
);
822 switch_safe_free(mycmd
);
823 return SWITCH_STATUS_SUCCESS
;
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
)
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 };
832 if (!switch_channel_ready(channel
)) {
833 return SWITCH_STATUS_FALSE
;
837 return SWITCH_STATUS_SUCCESS
;
840 if (switch_test_flag(&context
->vfh
, SWITCH_FILE_OPEN
)) {
841 switch_status_t status
= SWITCH_STATUS_FALSE
;
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
;
848 status
= switch_core_file_read_video(&context
->vfh
, &file_frame
, SVR_FLUSH
);
849 switch_core_file_command(&context
->vfh
, SCFC_FLUSH_AUDIO
);
851 if (status
!= SWITCH_STATUS_SUCCESS
&& status
!= SWITCH_STATUS_BREAK
) {
854 if (context
->vfh
.params
) {
855 const char *loopstr
= switch_event_get_header(context
->vfh
.params
, "loop");
856 if (switch_true(loopstr
)) {
859 if (switch_core_file_seek(&context
->vfh
, &pos
, 0, SEEK_SET
) == SWITCH_STATUS_SUCCESS
) close
= 0;
864 switch_core_file_close(&context
->vfh
);
868 if (file_frame
.img
) {
869 switch_img_free(&(context
->rp_img
));
870 context
->rp_img
= file_frame
.img
;
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
) {
878 switch_img_copy(context
->rp_img
, &frame
->img
);
882 return SWITCH_STATUS_SUCCESS
;
885 static switch_bool_t
video_replace_bug_callback(switch_media_bug_t
*bug
, void *user_data
, switch_abc_type_t type
)
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
;
892 case SWITCH_ABC_TYPE_INIT
:
896 case SWITCH_ABC_TYPE_CLOSE
:
898 switch_thread_rwlock_unlock(MODULE_INTERFACE
->rwlock
);
899 switch_img_free(&context
->rp_img
);
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
));
907 case SWITCH_ABC_TYPE_READ_VIDEO_PING
:
908 case SWITCH_ABC_TYPE_WRITE_VIDEO_PING
:
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
);
914 switch_channel_set_private(channel
, "_video_replace_bug_", NULL
);
926 SWITCH_STANDARD_APP(video_replace_start_function
)
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
;
936 char *argv
[2] = { 0 };
937 char *direction
= NULL
;
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
);
945 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session
), SWITCH_LOG_WARNING
, "alreday start!\n");
950 if (data
&& (lbuf
= switch_core_session_strdup(session
, data
))
951 && (argc
= switch_separate_string(lbuf
, ' ', argv
, (sizeof(argv
) / sizeof(argv
[0])))) > 0) {
961 if (!strcasecmp(direction
, "read")) {
962 flags
= SMBF_READ_VIDEO_PING
;
963 } else if (!strcasecmp(direction
, "write")) {
964 flags
= SMBF_WRITE_VIDEO_PING
;
966 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session
), SWITCH_LOG_ERROR
, "invalid replace direction!\n");
973 // switch_channel_wait_for_flag(channel, CF_VIDEO_READY, SWITCH_TRUE, 10000, NULL);
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
;
980 switch_thread_rwlock_rdlock(MODULE_INTERFACE
->rwlock
);
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
);
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
);
996 switch_channel_set_private(channel
, "_video_replace_bug_", bug
);
999 /* API Interface Function */
1000 #define VIDEO_REPLACE_API_SYNTAX "<uuid> <stop|start> [read|write] <file>"
1001 SWITCH_STANDARD_API(video_replace_api_function
)
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
;
1010 char *argv
[4] = { 0 };
1012 char *action
= NULL
;
1014 char *direction
= NULL
;
1015 switch_media_bug_flag_t flags
= 0;
1016 const char *function
= "video_replace";
1022 if (!(mycmd
= strdup(cmd
))) {
1026 if ((argc
= switch_separate_string(mycmd
, ' ', argv
, (sizeof(argv
) / sizeof(argv
[0])))) < 2) {
1033 if (!(rsession
= switch_core_session_locate(uuid
))) {
1034 stream
->write_function(stream
, "-ERR Cannot locate session!\n");
1038 channel
= switch_core_session_get_channel(rsession
);
1040 bug
= (switch_media_bug_t
*) switch_channel_get_private(channel
, "_video_replace_bug_");
1042 if (!strcasecmp(action
, "stop")) {
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");
1048 stream
->write_function(stream
, "-ERR not start\n");
1052 } else if (!strcasecmp(action
, "start")) {
1054 direction
= "write";
1057 direction
= argv
[2];
1061 if (zstr(direction
) || zstr(file
)) goto usage
;
1063 if (!strcasecmp(direction
, "read")) {
1064 flags
= SMBF_READ_VIDEO_PING
;
1065 } else if (!strcasecmp(direction
, "write")) {
1066 flags
= SMBF_WRITE_VIDEO_PING
;
1072 stream
->write_function(stream
, "-ERR alreday start\n");
1079 context
= (video_replace_context_t
*) switch_core_session_alloc(rsession
, sizeof(*context
));
1080 switch_assert(context
!= NULL
);
1081 context
->session
= rsession
;
1083 switch_thread_rwlock_rdlock(MODULE_INTERFACE
->rwlock
);
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
);
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
);
1099 switch_channel_set_private(channel
, "_video_replace_bug_", bug
);
1100 stream
->write_function(stream
, "+OK Success\n");
1105 stream
->write_function(stream
, "-USAGE: %s\n", VIDEO_REPLACE_API_SYNTAX
);
1109 switch_core_session_rwunlock(rsession
);
1112 switch_safe_free(mycmd
);
1113 return SWITCH_STATUS_SUCCESS
;
1117 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_video_filter_shutdown
)
1119 return SWITCH_STATUS_SUCCESS
;
1122 SWITCH_MODULE_LOAD_FUNCTION(mod_video_filter_load
)
1124 switch_application_interface_t
*app_interface
;
1125 switch_api_interface_t
*api_interface
;
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
;
1131 SWITCH_ADD_APP(app_interface
, "chromakey", "chromakey", "chromakey bug",
1132 chromakey_start_function
, CHROMAKEY_APP_SYNTAX
, SAF_NONE
);
1134 SWITCH_ADD_API(api_interface
, "chromakey", "chromakey", chromakey_api_function
, CHROMAKEY_API_SYNTAX
);
1136 SWITCH_ADD_APP(app_interface
, "video_replace", "video_replace", "video replace bug",
1137 video_replace_start_function
, "[read|write] <file> | stop", SAF_NONE
);
1139 SWITCH_ADD_API(api_interface
, "uuid_video_replace", "video_replace", video_replace_api_function
, VIDEO_REPLACE_API_SYNTAX
);
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");
1145 return SWITCH_STATUS_SUCCESS
;
1151 * indent-tabs-mode:t
1156 * vim:set softtabstop=4 shiftwidth=4 tabstop=4: