diff --git a/Makefile.am b/Makefile.am index 8f93c80317..6513aa055e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,9 +153,9 @@ libfreeswitch_spandsp_la_SOURCES = libs/spandsp/src/plc.c libs/spandsp/src/alloc libfreeswitch_spandsp_la_CFLAGS = -Ilibs/spandsp/src $(CORE_CFLAGS) $(AM_CFLAGS) CORE_LIBS+=libfreeswitch_spandsp.la lib_LTLIBRARIES = libfreeswitch.la -libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(FREETYPE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(openssl_CFLAGS) $(AM_CFLAGS) +libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(LIBPNG_CFLAGS) $(FREETYPE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(openssl_CFLAGS) $(AM_CFLAGS) libfreeswitch_la_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) $(PLATFORM_CORE_LDFLAGS) -no-undefined -libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_LIBS) $(FREETYPE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(openssl_LIBS) $(VPX_LIBS) $(PLATFORM_CORE_LIBS) +libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_LIBS) $(LIBPNG_LIBS) $(FREETYPE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(openssl_LIBS) $(VPX_LIBS) $(PLATFORM_CORE_LIBS) libfreeswitch_la_DEPENDENCIES = $(BUILT_SOURCES) if HAVE_ODBC diff --git a/configure.ac b/configure.ac index 965fc4f8cf..07dce7915b 100644 --- a/configure.ac +++ b/configure.ac @@ -755,6 +755,11 @@ if test "x$have_libz" = "xyes" ; then APR_ADDTO([PLATFORM_CORE_LIBS], [-lz]) fi +AC_CHECK_LIB(yuv, I420Scale, have_libyuv=yes, AC_MSG_ERROR([no usable libyuv; please install libyuv devel package or equivalent])) +if test "x$have_libyuv" = "xyes" ; then +APR_ADDTO([PLATFORM_CORE_LIBS], [-lyuv]) +fi + AC_CHECK_LIB(apr-1, apr_pool_mutex_set, use_system_apr=yes, use_system_apr=no) AM_CONDITIONAL([SYSTEM_APR],[test "${use_system_apr}" = "yes"]) AC_CHECK_LIB(aprutil-1, apr_queue_pop_timeout, use_system_aprutil=yes, use_system_aprutil=no) @@ -1192,6 +1197,7 @@ module_enabled() { grep -v -e "\#" -e "^\$" modules.conf | sed -e "s|^.*/||" | grep "^${1}\$" >/dev/null } +PKG_CHECK_MODULES([LIBPNG], [libpng12 >= 1.2.49]) PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 2.4.9]) PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.6.20]) PKG_CHECK_MODULES([CURL], [libcurl >= 7.19]) diff --git a/src/include/switch_core_video.h b/src/include/switch_core_video.h index 807f48faa3..84b0eaca90 100644 --- a/src/include/switch_core_video.h +++ b/src/include/switch_core_video.h @@ -191,6 +191,11 @@ SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_render(switch_img_txt_hand int x, int y, const char *text, const char *font_family, const char *font_color, const char *bgcolor, uint16_t font_size, double angle); + +SWITCH_DECLARE(void) switch_img_patch_hole(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_image_rect_t *rect); +SWITCH_DECLARE(switch_image_t *) switch_img_read_png(char* file_name); +SWITCH_DECLARE(void) switch_img_write_png(switch_image_t *img, char* file_name); + /** @} */ SWITCH_END_EXTERN_C diff --git a/src/mod/applications/mod_conference/Makefile.am b/src/mod/applications/mod_conference/Makefile.am index 6ad90f379d..36c0886c3b 100644 --- a/src/mod/applications/mod_conference/Makefile.am +++ b/src/mod/applications/mod_conference/Makefile.am @@ -5,7 +5,7 @@ mod_LTLIBRARIES = mod_conference.la mod_conference_la_SOURCES = mod_conference.c mod_conference_la_CFLAGS = $(AM_CFLAGS) -I. mod_conference_la_LIBADD = $(switch_builddir)/libfreeswitch.la -mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared -lyuv -lfreetype +mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared if HAVE_OPENAL mod_conference_la_LDFLAGS += -lopenal -lm diff --git a/src/switch_core_video.c b/src/switch_core_video.c index 128003d058..d6a206af49 100644 --- a/src/switch_core_video.c +++ b/src/switch_core_video.c @@ -31,7 +31,7 @@ #include #include - +#include SWITCH_DECLARE(switch_image_t *)switch_img_alloc(switch_image_t *img, switch_img_fmt_t fmt, @@ -545,6 +545,394 @@ SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_render(switch_img_txt_hand } +/* WARNING: + patch a big IMG with a rect hole, note this function is WIP ...... + It ONLY works when the hole is INSIDE the big IMG and the place the small img will patch to, + more sanity checks need to be decided +*/ +SWITCH_DECLARE(void) switch_img_patch_hole(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_image_rect_t *rect) +{ + int i, len; + + switch_assert(img->fmt == SWITCH_IMG_FMT_I420); + switch_assert(IMG->fmt == SWITCH_IMG_FMT_I420); + + len = MIN(img->d_w, IMG->d_w - x); + if (len <= 0) return; + + for (i = y; i < (y + img->d_h) && i < IMG->d_h; i++) { + if (rect && i >= rect->y && i < (rect->y + rect->h)) { + int size = rect->x > x ? rect->x - x : 0; + memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + x, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y), size); + size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w)); + memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + rect->x + rect->w, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y) + rect->w + (rect->x - x), size); + } else { + memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + x, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y), len); + } + } + + len /= 2; + + for (i = y; i < (y + img->d_h) && i < IMG->d_h; i += 2) { + if (rect && i > rect->y && i < (rect->y + rect->h)) { + int size = rect->x > x ? rect->x - x : 0; + + size /= 2; + memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * i / 2 + x / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i - y) / 2, size); + memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * i / 2 + x / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i - y) / 2, size); + size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w)) / 2; + memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * i / 2 + (rect->x + rect->w) / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i - y) / 2 + (rect->w + (rect->x - x)) / 2, size); + memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * i / 2 + (rect->x + rect->w) / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i - y) / 2 + (rect->w + (rect->x - x)) / 2, size); + } else { + memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * i / 2 + x / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i - y) / 2, len); + memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * i / 2 + x / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i - y) / 2, len); + } + } +} + +#define SWITCH_IMG_MAX_WIDTH 1920 * 2 +#define SWITCH_IMG_MAX_HEIGHT 1080 * 2 + +// WIP png functions, need furthur tweak/check to make sure it works on all png files and errors are properly detected and reported +// #define PNG_DEBUG 3 +#define PNG_SKIP_SETJMP_CHECK +#include + +// ref: most are out-dated, man libpng :) +// http://zarb.org/~gc/html/libpng.html +// http://www.libpng.org/pub/png/book/toc.html +// http://www.vias.org/pngguide/chapter01_03_02.html +// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html +// ftp://ftp.oreilly.com/examples/9781565920583/CDROM/SOFTWARE/SOURCE/LIBPNG/EXAMPLE.C + +SWITCH_DECLARE(switch_image_t *) switch_img_read_png(char* file_name) +{ + png_byte header[8]; // 8 is the maximum size that can be checked + png_bytep *row_pointers = NULL; + int y; + + int width, height; + png_byte color_type; + png_byte bit_depth; + + png_structp png_ptr; + png_infop info_ptr; + //int number_of_passes; + int row_bytes; + png_color_8p sig_bit; + // png_color_16 my_background = { 0 }; //{index,r, g, b, grey} + png_color_16 my_background = {0, 99, 99, 99, 0}; + + png_byte *buffer = NULL; + switch_image_t *img = NULL; + + /* open file and test for it being a png */ + FILE *fp = fopen(file_name, "rb"); + if (!fp) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for reading", file_name); + goto end; + } + + fread(header, 1, 8, fp); + if (png_sig_cmp(header, 0, 8)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s is not recognized as a PNG file", file_name); + goto end; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_read_struct failed"); + goto end; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed"); + goto end; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io"); + goto end; + } + + png_init_io(png_ptr, fp); + + png_set_sig_bytes(png_ptr, 8); + png_read_info(png_ptr, info_ptr); + + width = png_get_image_width(png_ptr, info_ptr); + height = png_get_image_height(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + //number_of_passes = png_set_interlace_handling(png_ptr); + + /* set up the transformations you want. Note that these are + all optional. Only call them if you want them */ + + /* expand paletted colors into true rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_expand(png_ptr); + } + + /* expand grayscale images to the full 8 bits */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + png_set_expand(png_ptr); + } + + /* expand images with transparency to full alpha channels */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_expand(png_ptr); + } + + /* Set the background color to draw transparent and alpha + images over */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { + // png_get_bKGD(png_ptr, info_ptr, &my_background); + // png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + } else { + png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + } + + /* tell libpng to handle the gamma conversion for you */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { + // png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma); + } else { + // png_set_gamma(png_ptr, screen_gamma, 0.45); + } + + /* tell libpng to strip 16 bit depth files down to 8 bits */ + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } + +#if 0 + /* dither rgb files down to 8 bit palettes & reduce palettes + to the number of colors available on your screen */ + if (0 && color_type & PNG_COLOR_MASK_COLOR) { + if (png_get_valid(png_ptr, info_ptr, & PNG_INFO_PLTE)) { + png_set_dither(png_ptr, info_ptr->palette, + info_ptr->num_palette, max_screen_colors, + info_ptr->histogram); + } else { + png_color std_color_cube[MAX_SCREEN_COLORS] = + {/* ... colors ... */}; + + png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS, + MAX_SCREEN_COLORS, NULL); + } + } +#endif + + /* invert monocrome files */ + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) { + // png_set_invert(png_ptr); + } + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + + /* shift the pixels down to their true bit depth */ + // if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT) && (bit_depth > (*sig_bit).red)) { + // png_set_shift(png_ptr, sig_bit); + // } + + /* pack pixels into bytes */ + if (bit_depth < 8) { + png_set_packing(png_ptr); + } + + /* flip the rgb pixels to bgr */ + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + // png_set_bgr(png_ptr); + } + + /* swap bytes of 16 bit files to least significant bit first */ + if (bit_depth == 16) { + png_set_swap(png_ptr); + } + + if (color_type & PNG_COLOR_MASK_ALPHA) { + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!!!!\n"); + goto end; + } + + png_set_strip_alpha(png_ptr); + } + + png_read_update_info(png_ptr, info_ptr); + + if (width > SWITCH_IMG_MAX_WIDTH || height > SWITCH_IMG_MAX_HEIGHT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", width, height); + } + + row_bytes = png_get_rowbytes(png_ptr, info_ptr); + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth); + + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + switch_assert(row_pointers); + + buffer = (png_byte *)malloc(row_bytes * height); + switch_assert(buffer); + + for (y = 0; y< height; y++) { + row_pointers[y] = buffer + row_bytes * y; + } + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + + img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1); + switch_assert(img); + + /* read file */ + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during read_image"); + goto end; + } + + png_read_image(png_ptr, row_pointers); + + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGBA) { + // should never get here since we already use png_set_strip_alpha() ? + switch_assert(1 == 2); + + switch_assert(row_bytes >= width * 4); + + for(y = 1; y < height; y++) { + memcpy(buffer + y * width * 4, row_pointers[y], width * 4); + } + + // ABGRToI420(buffer, width * 4, + RGBAToI420(buffer, width * 4, + img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y], + img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U], + img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V], + width, height); + } else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) { + switch_assert(row_bytes >= width * 3); + + for(y = 1; y < height; y++) { + memcpy(buffer + y * width * 3, row_pointers[y], width * 3); + } + RAWToI420(buffer, width * 3, + img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y], + img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U], + img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V], + width, height); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported color type: %d\n", png_get_color_type(png_ptr, info_ptr)); + } + +end: + switch_safe_free(buffer); + switch_safe_free(row_pointers); + if (fp) fclose(fp); + if (info_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + return img; +} + +SWITCH_DECLARE(void) switch_img_write_png(switch_image_t *img, char* file_name) +{ + int width, height; + png_byte color_type; + png_byte bit_depth; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers = NULL; + int row_bytes; + int y; + png_byte *buffer = NULL; + FILE *fp = NULL; + + width = img->d_w; + height = img->d_h; + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; + + fp = fopen(file_name, "wb"); + if (!fp) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for writing", file_name); + goto end; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_write_struct failed"); + goto end; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed"); + goto end; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io"); + goto end; + } + + png_init_io(png_ptr, fp); + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing header"); + goto end; + } + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + row_bytes = png_get_rowbytes(png_ptr, info_ptr); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth); + + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + switch_assert(row_pointers); + + buffer = (png_byte *)malloc(row_bytes * height); + switch_assert(buffer); + + for (y = 0; y < height; y++) { + row_pointers[y] = buffer + row_bytes * y; + } + + I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y], + img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U], + img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V], + buffer, width * 3, + width, height); + + for(y = height - 1; y > 0; y--) { + // todo, check overlaps + memcpy(row_pointers[y], buffer + row_bytes * y, width * 3); + } + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes"); + goto end; + } + + png_write_image(png_ptr, row_pointers); + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during end of write"); + goto end; + } + + png_write_end(png_ptr, NULL); + +end: + + switch_safe_free(buffer); + switch_safe_free(row_pointers); + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); +} /* For Emacs: