diff --git a/src/include/switch_core_video.h b/src/include/switch_core_video.h index 8998d0bac5..e152922675 100644 --- a/src/include/switch_core_video.h +++ b/src/include/switch_core_video.h @@ -393,7 +393,8 @@ SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char *file_name, swit SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char *file_name); SWITCH_DECLARE(switch_status_t) switch_png_open(switch_png_t **pngP, const char *file_name); SWITCH_DECLARE(void) switch_png_free(switch_png_t **pngP); - +SWITCH_DECLARE(switch_status_t) switch_img_data_url_png(switch_image_t *img, char **urlP); + /*!\brief put a small img over a big IMG at position x,y, with alpha transparency * * Both IMG and img must be non-NULL diff --git a/src/switch_core_video.c b/src/switch_core_video.c index ba9d18e9e7..54851cf8aa 100644 --- a/src/switch_core_video.c +++ b/src/switch_core_video.c @@ -578,6 +578,7 @@ SWITCH_DECLARE(void) switch_img_copy(switch_image_t *img, switch_image_t **new_i if (img->fmt != SWITCH_IMG_FMT_I420 && img->fmt != SWITCH_IMG_FMT_ARGB) return; if (*new_img) { + new_fmt = (*new_img)->fmt; if ((*new_img)->fmt != SWITCH_IMG_FMT_I420 && (*new_img)->fmt != SWITCH_IMG_FMT_ARGB) return; if (img->d_w != (*new_img)->d_w || img->d_h != (*new_img)->d_h ) { new_fmt = (*new_img)->fmt; @@ -2731,6 +2732,149 @@ end: #endif +static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + switch_buffer_t *data_buffer = (switch_buffer_t *)png_get_io_ptr(png_ptr); + switch_buffer_write(data_buffer, data, length); +} + +static void my_png_flush(png_structp png_ptr) +{ +} + +SWITCH_DECLARE(switch_status_t) switch_img_data_url_png(switch_image_t *img, char **urlP) +{ + 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; + switch_status_t status = SWITCH_STATUS_FALSE; + switch_buffer_t *data_buffer = NULL; + unsigned char *head; + + width = img->d_w; + height = img->d_h; + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; + + if (img->fmt == SWITCH_IMG_FMT_ARGB) { + color_type = PNG_COLOR_TYPE_RGBA; + } + + + 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; + } + + switch_buffer_create_dynamic(&data_buffer, 1024, 1024, 0); + png_set_write_fn(png_ptr, data_buffer, my_png_write_data, my_png_flush); + //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_DEBUG, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth); + + buffer = (png_byte *)malloc(row_bytes * height); + switch_assert(buffer); + + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + switch_assert(row_pointers); + + for (y = 0; y < height; y++) { + row_pointers[y] = buffer + row_bytes * y; + } + + if (img->fmt == SWITCH_IMG_FMT_I420) { + 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); + } else if (img->fmt == SWITCH_IMG_FMT_ARGB) { + ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], + buffer, row_bytes, width, height); + } + + if (setjmp(png_jmpbuf(png_ptr))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes"); + goto end; + } + + //png_set_rows(png_ptr, info_ptr, row_pointers); + //png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + 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, info_ptr); + + if ((head = (unsigned char *) switch_buffer_get_head_pointer(data_buffer))) { + switch_size_t olen = 0, blen = 0; + unsigned char *out = NULL; + const char *header = "data:image/png;base64,"; + + blen = switch_buffer_len(data_buffer); + olen = blen * 4; + + if (olen > strlen(header) + 1) { + switch_zmalloc(out, olen); + + switch_snprintf((char *)out, strlen(header) + 1, header); + switch_b64_encode(head, blen, out + strlen(header), olen - strlen(header)); + *urlP = (char *)out; + } else { + status = SWITCH_STATUS_MEMERR; + goto end; + } + } + + status = SWITCH_STATUS_SUCCESS; + +end: + + if (status != SWITCH_STATUS_SUCCESS) { + *urlP = NULL; + } + + switch_buffer_destroy(&data_buffer); + switch_safe_free(buffer); + switch_safe_free(row_pointers); + png_destroy_write_struct(&png_ptr, &info_ptr); + + return status; +} #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED /* available from libpng 1.6.0 */ @@ -2777,6 +2921,7 @@ SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* #else + SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name) { int width, height; @@ -2796,6 +2941,10 @@ SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; + if (img->fmt == SWITCH_IMG_FMT_ARGB) { + color_type = PNG_COLOR_TYPE_RGBA; + } + 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); @@ -2845,15 +2994,15 @@ SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* 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 (img->fmt == SWITCH_IMG_FMT_I420) { + 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); + } else if (img->fmt == SWITCH_IMG_FMT_ARGB) { + ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], + buffer, row_bytes, width, height); } if (setjmp(png_jmpbuf(png_ptr))) { diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index f0d3a06474..dff3fc3662 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -2,6 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam bin_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx \ switch_ivr_play_say +bin_PROGRAMS+= switch_core_video AM_LDFLAGS = -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS) AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) AM_CFLAGS = $(SWITCH_AM_CPPFLAGS) diff --git a/tests/unit/switch_core_video.c b/tests/unit/switch_core_video.c new file mode 100644 index 0000000000..cdd03cbac1 --- /dev/null +++ b/tests/unit/switch_core_video.c @@ -0,0 +1,90 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2018, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * Seven Du + * + * + * switch_core_video.c -- tests core_video + * + */ +#include +#include + +#include + +FST_CORE_BEGIN("./conf") +{ + FST_SUITE_BEGIN(switch_ivr_originate) + { + FST_SETUP_BEGIN() + { + } + FST_SETUP_END() + + FST_TEARDOWN_BEGIN() + { + } + FST_TEARDOWN_END() + + FST_TEST_BEGIN(data_url_test) + { + switch_image_t *img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, 120, 60, 1); + switch_image_t *argb_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, 120, 60, 1); + switch_rgb_color_t color = { 0 }; + color.r = 255; + // color.g = 255; + // color.b = 255; + char *data_url = NULL; + + switch_img_fill(img, 0, 0, img->d_w, img->d_h, &color); + switch_img_add_text(img->planes[0], img->d_w, 10, 10, "-1234567890"); + switch_img_write_png(img, "test-rgb.png"); + + switch_img_data_url_png(img, &data_url); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "I420: %s\n", data_url); + free(data_url); + data_url = NULL; + + switch_img_copy(img, &argb_img); + + { + uint8_t *p = argb_img->planes[0]; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3)); + } + + switch_img_write_png(argb_img, "test-argb.png"); + switch_img_data_url_png(argb_img, &data_url); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ARGB: %s\n", data_url); + free(data_url); + + + switch_img_free(&img); + switch_img_free(&argb_img); + } + FST_TEST_END() + } + FST_SUITE_END() +} +FST_CORE_END()