FS-7506 FS-7513: set banner with video_banner_text variable set before calling in, NEEDS DOCS params are parsable inside a {} string

This commit is contained in:
Anthony Minessale 2015-02-11 14:07:06 -06:00 committed by Michael Jerris
parent 9d7eef28e6
commit 0697db4fb4
9 changed files with 1067 additions and 196 deletions

View File

@ -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) libfreeswitch_spandsp_la_CFLAGS = -Ilibs/spandsp/src $(CORE_CFLAGS) $(AM_CFLAGS)
CORE_LIBS+=libfreeswitch_spandsp.la CORE_LIBS+=libfreeswitch_spandsp.la
lib_LTLIBRARIES = libfreeswitch.la lib_LTLIBRARIES = libfreeswitch.la
libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(openssl_CFLAGS) $(AM_CFLAGS) libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_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_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) $(PLATFORM_CORE_LDFLAGS) -no-undefined
libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_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) $(FREETYPE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(openssl_LIBS) $(VPX_LIBS) $(PLATFORM_CORE_LIBS)
libfreeswitch_la_DEPENDENCIES = $(BUILT_SOURCES) libfreeswitch_la_DEPENDENCIES = $(BUILT_SOURCES)
if HAVE_ODBC if HAVE_ODBC
@ -215,6 +215,8 @@ library_include_HEADERS = \
src/include/switch_curl.h \ src/include/switch_curl.h \
src/include/switch_json.h \ src/include/switch_json.h \
src/include/switch_stfu.h \ src/include/switch_stfu.h \
src/include/switch_utf8.h \
src/include/switch_vpx.h \
libs/libteletone/src/libteletone_detect.h \ libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \ libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \ libs/libteletone/src/libteletone.h \
@ -293,6 +295,7 @@ libfreeswitch_la_SOURCES = \
src/switch_curl.c \ src/switch_curl.c \
src/switch_hashtable.c\ src/switch_hashtable.c\
src/switch_stfu.c \ src/switch_stfu.c \
src/switch_utf8.c \
libs/libtpl-1.5/src/tpl.c \ libs/libtpl-1.5/src/tpl.c \
libs/libteletone/src/libteletone_detect.c \ libs/libteletone/src/libteletone_detect.c \
libs/libteletone/src/libteletone_generate.c \ libs/libteletone/src/libteletone_generate.c \

View File

@ -1192,6 +1192,7 @@ module_enabled() {
grep -v -e "\#" -e "^\$" modules.conf | sed -e "s|^.*/||" | grep "^${1}\$" >/dev/null grep -v -e "\#" -e "^\$" modules.conf | sed -e "s|^.*/||" | grep "^${1}\$" >/dev/null
} }
PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 2.4.9])
PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.6.20]) PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.6.20])
PKG_CHECK_MODULES([CURL], [libcurl >= 7.19]) PKG_CHECK_MODULES([CURL], [libcurl >= 7.19])
PKG_CHECK_MODULES([PCRE], [libpcre >= 7.8]) PKG_CHECK_MODULES([PCRE], [libpcre >= 7.8])

View File

@ -22,6 +22,7 @@
* the Initial Developer. All Rights Reserved. * the Initial Developer. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Anthony Minessale II <anthm@freeswitch.org>
* *
* *
* switch_core_video.h -- Core Video header * switch_core_video.h -- Core Video header
@ -35,32 +36,14 @@
opportunity to thank libvpx for all the awesome stuff it does and for making my life much easier. opportunity to thank libvpx for all the awesome stuff it does and for making my life much easier.
*/ */
#ifndef SWITCH_VIDEO_H #ifndef SWITCH_VIDEO_H
#define SWITCH_VIDEO_H #define SWITCH_VIDEO_H
#include "vpx/vpx_image.h" #include <switch.h>
#include "vpx/vpx_integer.h"
SWITCH_BEGIN_EXTERN_C SWITCH_BEGIN_EXTERN_C
#define SWITCH_IMG_FMT_PLANAR VPX_IMG_FMT_PLANAR
#define SWITCH_IMG_FMT_UV_FLIP VPX_IMG_FMT_UV_FLIP
#define SWITCH_IMG_FMT_HAS_ALPHA VPX_IMG_FMT_HAS_ALPHA
#define SWITCH_PLANE_PACKED VPX_PLANE_PACKED
#define SWITCH_PLANE_Y VPX_PLANE_Y
#define SWITCH_PLANE_U VPX_PLANE_U
#define SWITCH_PLANE_V VPX_PLANE_V
#define SWITCH_PLANE_ALPHA VPX_PLANE_ALPHA
#ifndef VPX_IMG_FMT_HIGH /* not available in libvpx 1.3.0 (see commit hash e97aea28) */
#define VPX_IMG_FMT_HIGH 0x800 /**< Image uses 16bit framebuffer */
#endif
#define SWITCH_IMG_FMT_HIGH VPX_IMG_FMT_HIGH
#define SWITCH_IMG_FMT_I420 VPX_IMG_FMT_I420
typedef struct switch_yuv_color_s { typedef struct switch_yuv_color_s {
uint8_t y; uint8_t y;
uint8_t u; uint8_t u;
@ -68,9 +51,6 @@ typedef struct switch_yuv_color_s {
} switch_yuv_color_t; } switch_yuv_color_t;
typedef vpx_img_fmt_t switch_img_fmt_t;
typedef vpx_image_t switch_image_t;
/**\brief Representation of a rectangle on a surface */ /**\brief Representation of a rectangle on a surface */
typedef struct switch_image_rect { typedef struct switch_image_rect {
@ -80,6 +60,7 @@ typedef struct switch_image_rect {
unsigned int h; /**< height */ unsigned int h; /**< height */
} switch_image_rect_t; } switch_image_rect_t;
/*!\brief Open a descriptor, allocating storage for the underlying image /*!\brief Open a descriptor, allocating storage for the underlying image
* *
* Returns a descriptor for storing an image of the given format. The * Returns a descriptor for storing an image of the given format. The
@ -193,7 +174,16 @@ SWITCH_DECLARE(void) switch_img_fill(switch_image_t *img, int x, int y, int w, i
SWITCH_DECLARE(void) switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_yuv_color_t color); SWITCH_DECLARE(void) switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_yuv_color_t color);
SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str); SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, const char *color_str);
SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_create(switch_img_txt_handle_t **handleP, const char *font_family,
const char *font_color, uint16_t font_size, double angle, switch_memory_pool_t *pool);
SWITCH_DECLARE(void) switch_img_txt_handle_destroy(switch_img_txt_handle_t **handleP);
SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_render(switch_img_txt_handle_t *handle, switch_image_t *img,
int x, int y, const char *text,
const char *font_family, const char *font_color, uint16_t font_size, double angle);
/** @} */ /** @} */

View File

@ -40,7 +40,7 @@
#include <switch.h> #include <switch.h>
#include <switch_json.h> #include <switch_json.h>
#include <switch_core_video.h> #include <switch_vpx.h>
SWITCH_BEGIN_EXTERN_C SWITCH_BEGIN_EXTERN_C
#define SWITCH_ENT_ORIGINATE_DELIM ":_:" #define SWITCH_ENT_ORIGINATE_DELIM ":_:"
@ -2215,8 +2215,6 @@ typedef switch_status_t (*switch_core_codec_control_func_t) (switch_codec_t *cod
void **ret_data); void **ret_data);
typedef switch_status_t (*switch_image_write_callback_t) (switch_core_session_t *session, switch_frame_t *frame, switch_image_t *img, void *user_data);
typedef switch_status_t (*switch_core_codec_init_func_t) (switch_codec_t *, switch_codec_flag_t, const switch_codec_settings_t *codec_settings); typedef switch_status_t (*switch_core_codec_init_func_t) (switch_codec_t *, switch_codec_flag_t, const switch_codec_settings_t *codec_settings);
typedef switch_status_t (*switch_core_codec_fmtp_parse_func_t) (const char *fmtp, switch_codec_fmtp_t *codec_fmtp); typedef switch_status_t (*switch_core_codec_fmtp_parse_func_t) (const char *fmtp, switch_codec_fmtp_t *codec_fmtp);
typedef switch_status_t (*switch_core_codec_destroy_func_t) (switch_codec_t *); typedef switch_status_t (*switch_core_codec_destroy_func_t) (switch_codec_t *);
@ -2501,6 +2499,8 @@ typedef struct switch_waitlist_s {
struct switch_vb_s; struct switch_vb_s;
typedef struct switch_vb_s switch_vb_t; typedef struct switch_vb_s switch_vb_t;
struct switch_img_txt_handle_s;
typedef struct switch_img_txt_handle_s switch_img_txt_handle_t;
SWITCH_END_EXTERN_C SWITCH_END_EXTERN_C
#endif #endif

119
src/include/switch_utf8.h Normal file
View File

@ -0,0 +1,119 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* 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
* Seven Du <dujinfang@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Anthony Minessale II <anthm@freeswitch.org>
*
*
* switch_utf8.h UTf8
*
*/
/*
Basic UTF-8 manipulation routines
by Jeff Bezanson
placed in the public domain Fall 2005
This code is designed to provide the utilities you need to manipulate
UTF-8 as an internal string encoding. These functions do not perform the
error checking normally needed when handling UTF-8 data, so if you happen
to be from the Unicode Consortium you will want to flay me alive.
I do this because error checking can be performed at the boundaries (I/O),
with these routines reserved for higher performance on data known to be
valid.
*/
#include <switch.h>
/* is c the start of a utf8 sequence? */
#define isutf(c) (((c)&0xC0)!=0x80)
/* convert UTF-8 data to wide character */
SWITCH_DECLARE(int) switch_u8_toucs(uint32_t *dest, int sz, char *src, int srcsz);
/* the opposite conversion */
SWITCH_DECLARE(int) switch_u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz);
/* single character to UTF-8 */
SWITCH_DECLARE(int) switch_u8_wc_toutf8(char *dest, uint32_t ch);
/* character number to byte offset */
SWITCH_DECLARE(int) switch_u8_offset(char *str, int charnum);
/* byte offset to character number */
SWITCH_DECLARE(int) switch_u8_charnum(char *s, int offset);
/* return next character, updating an index variable */
SWITCH_DECLARE(uint32_t) switch_u8_nextchar(char *s, int *i);
/* move to next character */
SWITCH_DECLARE(void) switch_u8_inc(char *s, int *i);
/* move to previous character */
SWITCH_DECLARE(void) switch_u8_dec(char *s, int *i);
/* returns length of next utf-8 sequence */
SWITCH_DECLARE(int) switch_u8_seqlen(char *s);
/* assuming src points to the character after a backslash, read an
escape sequence, storing the result in dest and returning the number of
input characters processed */
SWITCH_DECLARE(int) switch_u8_read_escape_sequence(char *src, uint32_t *dest);
/* given a wide character, convert it to an ASCII escape sequence stored in
buf, where buf is "sz" bytes. returns the number of characters output.*/
SWITCH_DECLARE(int) switch_u8_escape_wchar(char *buf, int sz, uint32_t ch);
/* convert a string "src" containing escape sequences to UTF-8 */
SWITCH_DECLARE(int) switch_u8_unescape(char *buf, int sz, char *src);
/* convert UTF-8 "src" to ASCII with escape sequences.
if escape_quotes is nonzero, quote characters will be preceded by
backslashes as well. */
SWITCH_DECLARE(int) switch_u8_escape(char *buf, int sz, char *src, int escape_quotes);
/* utility predicates used by the above */
int octal_digit(char c);
int hex_digit(char c);
/* return a pointer to the first occurrence of ch in s, or NULL if not
found. character index of found character returned in *charn. */
SWITCH_DECLARE(char *) switch_u8_strchr(char *s, uint32_t ch, int *charn);
/* same as the above, but searches a buffer of a given size instead of
a NUL-terminated string. */
SWITCH_DECLARE(char *) switch_u8_memchr(char *s, uint32_t ch, size_t sz, int *charn);
/* count the number of characters in a UTF-8 string */
SWITCH_DECLARE(int) switch_u8_strlen(char *s);
SWITCH_DECLARE(int) switch_u8_is_locale_utf8(char *locale);
/* printf where the format string and arguments may be in UTF-8.
you can avoid this function and just use ordinary printf() if the current
locale is UTF-8. */
SWITCH_DECLARE(int) switch_u8_vprintf(char *fmt, va_list ap);
SWITCH_DECLARE(int) switch_u8_printf(char *fmt, ...);
SWITCH_DECLARE(uint32_t) switch_u8_get_char(char *s, int *i);

84
src/include/switch_vpx.h Normal file
View File

@ -0,0 +1,84 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* 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
* Seven Du <dujinfang@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
*
* switch_vpx.h -- vpx resources
*
*/
/*! \file switch_vpx.h
\brief vpx resources
The things powered by libvpx are renamed into the switch_ namespace to provide a cleaner
look to things and helps me to document what parts of video I am using I'd like to take this
opportunity to thank libvpx for all the awesome stuff it does and for making my life much easier.
*/
#ifndef SWITCH_VPX_H
#define SWITCH_VPX_H
#include <switch.h>
#include "vpx/vpx_image.h"
#include "vpx/vpx_integer.h"
SWITCH_BEGIN_EXTERN_C
#define SWITCH_IMG_FMT_PLANAR VPX_IMG_FMT_PLANAR
#define SWITCH_IMG_FMT_UV_FLIP VPX_IMG_FMT_UV_FLIP
#define SWITCH_IMG_FMT_HAS_ALPHA VPX_IMG_FMT_HAS_ALPHA
#define SWITCH_PLANE_PACKED VPX_PLANE_PACKED
#define SWITCH_PLANE_Y VPX_PLANE_Y
#define SWITCH_PLANE_U VPX_PLANE_U
#define SWITCH_PLANE_V VPX_PLANE_V
#define SWITCH_PLANE_ALPHA VPX_PLANE_ALPHA
#ifndef VPX_IMG_FMT_HIGH /* not available in libvpx 1.3.0 (see commit hash e97aea28) */
#define VPX_IMG_FMT_HIGH 0x800 /**< Image uses 16bit framebuffer */
#endif
#define SWITCH_IMG_FMT_HIGH VPX_IMG_FMT_HIGH
#define SWITCH_IMG_FMT_I420 VPX_IMG_FMT_I420
typedef vpx_img_fmt_t switch_img_fmt_t;
typedef vpx_image_t switch_image_t;
SWITCH_END_EXTERN_C
#endif
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/

View File

@ -358,8 +358,14 @@ typedef struct mcu_layer_s {
int member_id; int member_id;
int idx; int idx;
int tagged; int tagged;
int screen_w;
int screen_h;
int x_pos;
int y_pos;
switch_image_t *img; switch_image_t *img;
switch_image_t *cur_img; switch_image_t *cur_img;
switch_image_t *banner_img;
switch_img_txt_handle_t *txthandle;
} mcu_layer_t; } mcu_layer_t;
typedef struct mcu_canvas_s { typedef struct mcu_canvas_s {
@ -702,132 +708,6 @@ typedef struct layout_group_s {
} layout_group_t; } layout_group_t;
#include "utf8.h"
static const u_int32_t offsetsFromUTF8[6] = {
0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL
};
/* reads the next utf-8 sequence out of a string, updating an index */
uint32_t get_utf8_char(char *s, int *i)
{
u_int32_t ch = 0;
int sz = 0;
do {
ch <<= 6;
ch += (unsigned char)s[(*i)++];
sz++;
} while (s[*i] && !isutf(s[*i]));
ch -= offsetsFromUTF8[sz-1];
return ch;
}
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
static void draw_bitmap(switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y, switch_yuv_color_t color)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
switch (bitmap->pixel_mode) {
case FT_PIXEL_MODE_GRAY: // it should always be GRAY since we use FT_LOAD_RENDER?
break;
case FT_PIXEL_MODE_NONE:
case FT_PIXEL_MODE_MONO:
case FT_PIXEL_MODE_GRAY2:
case FT_PIXEL_MODE_GRAY4:
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "unsupported pixel mode %d\n", bitmap->pixel_mode);
return;
}
for ( i = x, p = 0; i < x_max; i++, p++ ) {
for ( j = y, q = 0; j < y_max; j++, q++ ) {
if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
if (bitmap->buffer[q * bitmap->width + p] > 128) {
switch_img_draw_pixel(img, i, j, color);
}
}
}
}
SWITCH_DECLARE(void) switch_img_draw_text(switch_image_t *img, int x, int y, switch_yuv_color_t color, uint16_t font_size, char *text)
{
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_Matrix matrix; /* transformation matrix */
FT_Vector pen; /* untransformed origin */
FT_Error error;
// char* font_family = "/usr/local/freeswitch/SimHei.ttf";
char* font_family = "/usr/local/freeswitch/Arial.ttf";
double angle;
int target_height;
int index = 0;
FT_ULong ch;
if (zstr(text)) return;
angle = 0; // (45.0 / 360 ) * 3.14159 * 2;
target_height = img->d_h;
error = FT_Init_FreeType( &library ); /* initialize library */
if (error) return;
error = FT_New_Face(library, font_family, 0, &face); /* create face object */
if (error) return;
/* use 50pt at 100dpi */
error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 96); /* set character size */
if (error) return;
slot = face->glyph;
/* set up matrix */
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
pen.x = x * 64;
pen.y = (target_height - y) * 64;
while(*(text + index)) {
ch = get_utf8_char(text, &index);
if (ch == '\n') {
pen.x = x * 64;
pen.y -= (font_size + font_size / 4) * 64;
continue;
}
/* set transformation */
FT_Set_Transform(face, &matrix, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char(face, ch, FT_LOAD_RENDER);
if (error) continue;
/* now, draw to our target surface (convert position) */
draw_bitmap(img, &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top + font_size, color);
/* increment pen position */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
FT_Done_Face(face);
FT_Done_FreeType(library);
}
static void conference_parse_layouts(conference_obj_t *conference) static void conference_parse_layouts(conference_obj_t *conference)
{ {
@ -1021,32 +901,22 @@ static void reset_image(switch_image_t *img, switch_yuv_color_t *color)
static void reset_layer(mcu_canvas_t *canvas, mcu_layer_t *layer) static void reset_layer(mcu_canvas_t *canvas, mcu_layer_t *layer)
{ {
int x = 0, y = 0;
int screen_w = 0, screen_h = 0;
layer->tagged = 0; layer->tagged = 0;
screen_w = canvas->img->d_w * layer->geometry.scale / SCALE_FACTOR; switch_img_free(&layer->banner_img);
screen_h = canvas->img->d_h * layer->geometry.scale / SCALE_FACTOR;
if (screen_w % 2) screen_w++; // round to even if (layer->img && (layer->img->d_w != layer->screen_w || layer->img->d_h != layer->screen_h)) {
if (screen_h % 2) screen_h++; // round to even
x = canvas->img->d_w * layer->geometry.x / SCALE_FACTOR;
y = canvas->img->d_h * layer->geometry.y / SCALE_FACTOR;
if (layer->img && (layer->img->d_w != screen_w || layer->img->d_h != screen_h)) {
switch_img_free(&layer->img); switch_img_free(&layer->img);
} }
if (!layer->img) { if (!layer->img) {
layer->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, screen_w, screen_h, 1); layer->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, layer->screen_w, layer->screen_h, 1);
} }
switch_assert(layer->img); switch_assert(layer->img);
reset_image(layer->img, &canvas->bgcolor); reset_image(layer->img, &canvas->bgcolor);
switch_img_patch(canvas->img, layer->img, x, y); switch_img_patch(canvas->img, layer->img, layer->x_pos, layer->y_pos);
switch_img_free(&layer->cur_img); switch_img_free(&layer->cur_img);
} }
@ -1054,29 +924,27 @@ static void reset_layer(mcu_canvas_t *canvas, mcu_layer_t *layer)
static void scale_and_patch(conference_obj_t *conference, mcu_layer_t *layer) static void scale_and_patch(conference_obj_t *conference, mcu_layer_t *layer)
{ {
int ret; int ret;
int x = 0, y = 0;
switch_image_t *IMG = conference->canvas->img, *img = layer->cur_img; switch_image_t *IMG = conference->canvas->img, *img = layer->cur_img;
if (layer->geometry.scale) { if (layer->geometry.scale) {
int screen_w = 0, screen_h = 0, img_w = 0, img_h = 0; int img_w = 0, img_h = 0;
double screen_aspect = 0, img_aspect = 0; double screen_aspect = 0, img_aspect = 0;
img_w = layer->screen_w = IMG->d_w * layer->geometry.scale / SCALE_FACTOR;
img_h = layer->screen_h = IMG->d_h * layer->geometry.scale / SCALE_FACTOR;
img_w = screen_w = IMG->d_w * layer->geometry.scale / SCALE_FACTOR; layer->x_pos = IMG->d_w * layer->geometry.x / SCALE_FACTOR;
img_h = screen_h = IMG->d_h * layer->geometry.scale / SCALE_FACTOR; layer->y_pos = IMG->d_h * layer->geometry.y / SCALE_FACTOR;
x = IMG->d_w * layer->geometry.x / SCALE_FACTOR; screen_aspect = (double) layer->screen_w / layer->screen_h;
y = IMG->d_h * layer->geometry.y / SCALE_FACTOR;
screen_aspect = (double) screen_w / screen_h;
img_aspect = (double) img->d_w / img->d_h; img_aspect = (double) img->d_w / img->d_h;
if (screen_aspect > img_aspect) { if (screen_aspect > img_aspect) {
img_w = img_aspect * screen_h; img_w = img_aspect * layer->screen_h;
x += (screen_w - img_w) / 2; layer->x_pos += (layer->screen_w - img_w) / 2;
} else if (screen_aspect < img_aspect) { } else if (screen_aspect < img_aspect) {
img_h = screen_w / img_aspect; img_h = layer->screen_w / img_aspect;
y += (screen_h - img_h) / 2; layer->y_pos += (layer->screen_h - img_h) / 2;
} }
@ -1121,11 +989,15 @@ static void scale_and_patch(conference_obj_t *conference, mcu_layer_t *layer)
// reserv the bottom room for text, e.g. caller id // reserv the bottom room for text, e.g. caller id
// switch_img_set_rect(layer->img, 0, 0, layer->img->d_w, layer->img->d_h - 20); // switch_img_set_rect(layer->img, 0, 0, layer->img->d_w, layer->img->d_h - 20);
} }
switch_img_patch(IMG, layer->img, x, y); switch_img_patch(IMG, layer->img, layer->x_pos, layer->y_pos);
} }
} else { } else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "insert at %d,%d\n", x, y); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "insert at %d,%d\n", 0, 0);
switch_img_patch(IMG, img, x, y); switch_img_patch(IMG, img, 0, 0);
}
if (layer->banner_img) {
switch_img_patch(IMG, layer->banner_img, layer->x_pos, layer->y_pos + (layer->screen_h - layer->banner_img->d_h));
} }
} }
@ -1165,6 +1037,10 @@ static void detach_video_layer(conference_member_t *member)
conf_api_sub_position(member, NULL, "0:0:0"); conf_api_sub_position(member, NULL, "0:0:0");
} }
if (layer->txthandle) {
switch_img_txt_handle_destroy(&layer->txthandle);
}
reset_layer(member->conference->canvas, layer); reset_layer(member->conference->canvas, layer);
layer->member_id = 0; layer->member_id = 0;
member->video_layer_id = -1; member->video_layer_id = -1;
@ -1172,13 +1048,74 @@ static void detach_video_layer(conference_member_t *member)
switch_mutex_unlock(member->conference->canvas->mutex); switch_mutex_unlock(member->conference->canvas->mutex);
} }
static void layer_set_banner(mcu_layer_t *layer, const char *text)
{
switch_yuv_color_t fgcolor, bgcolor;
int font_size = 24;
const char *fg = "#cccccc";
const char *bg = "#142e55";
char *parsed = NULL;
switch_event_t *params = NULL;
const char *font_face = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf";
const char *var;
if (*text == '{') {
if (switch_event_create_brackets((char *)text, '{', '}', ',', &params, &parsed, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS || !parsed) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n");
} else {
text = parsed;
}
}
if (params) {
if ((var = switch_event_get_header(params, "fg"))) {
fg = var;
}
if ((var = switch_event_get_header(params, "bg"))) {
bg = var;
}
if ((var = switch_event_get_header(params, "font_face"))) {
font_face = var;
}
if ((var = switch_event_get_header(params, "font_size"))) {
int tmp = atoi(var);
if (tmp >= 5 && tmp <= 50) {
font_size = tmp;
}
}
}
switch_color_set(&fgcolor, fg);
switch_color_set(&bgcolor, bg);
switch_img_free(&layer->banner_img);
layer->banner_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, layer->screen_w, font_size * 2, 1);
if (layer->txthandle) {
switch_img_txt_handle_destroy(&layer->txthandle);
}
switch_img_txt_handle_create(&layer->txthandle, font_face, fg, font_size, 0, NULL);
reset_image(layer->banner_img, &bgcolor);
switch_img_txt_handle_render(layer->txthandle, layer->banner_img, font_size / 2, font_size / 2, text, NULL, NULL, 0, 0);
switch_safe_free(parsed);
if (params) switch_event_destroy(&params);
}
static switch_status_t attach_video_layer(conference_member_t *member, int idx) static switch_status_t attach_video_layer(conference_member_t *member, int idx)
{ {
mcu_layer_t *layer = NULL; mcu_layer_t *layer = NULL;
switch_channel_t *channel = NULL; switch_channel_t *channel = NULL;
const char *res_id = NULL; const char *res_id = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS; switch_status_t status = SWITCH_STATUS_SUCCESS;
const char *banner = NULL;
if (!member->session) abort(); if (!member->session) abort();
switch_mutex_lock(member->conference->canvas->mutex); switch_mutex_lock(member->conference->canvas->mutex);
@ -1206,6 +1143,10 @@ static switch_status_t attach_video_layer(conference_member_t *member, int idx)
} }
} }
if ((banner = switch_channel_get_variable_dup(channel, "video_banner_text", SWITCH_FALSE, -1))) {
layer_set_banner(layer, banner);
}
layer->member_id = member->id; layer->member_id = member->id;
member->video_layer_id = idx; member->video_layer_id = idx;
check_used_layers(member->conference); check_used_layers(member->conference);
@ -1239,6 +1180,17 @@ static void init_canvas_layers(conference_obj_t *conference, video_layout_t *vla
layer->geometry.floor = vlayout->images[i].floor; layer->geometry.floor = vlayout->images[i].floor;
layer->idx = i; layer->idx = i;
layer->screen_w = conference->canvas->img->d_w * layer->geometry.scale / SCALE_FACTOR;
layer->screen_h = conference->canvas->img->d_h * layer->geometry.scale / SCALE_FACTOR;
if (layer->screen_w % 2) layer->screen_w++; // round to even
if (layer->screen_h % 2) layer->screen_h++; // round to even
layer->x_pos = conference->canvas->img->d_w * layer->geometry.x / SCALE_FACTOR;
layer->y_pos = conference->canvas->img->d_h * layer->geometry.y / SCALE_FACTOR;
if (layer->geometry.floor) { if (layer->geometry.floor) {
conference->canvas->layout_floor_id = i; conference->canvas->layout_floor_id = i;
} }
@ -1570,18 +1522,24 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread
} }
} }
if (1) { if (0) {
switch_img_txt_handle_t *txthandle = NULL;
switch_yuv_color_t color; switch_yuv_color_t color;
switch_color_set(&color, "#FFFFFF");
switch_img_draw_text(conference->canvas->img, 10, 10, color, 12, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!");
switch_img_draw_text(conference->canvas->img, 10, 40, color, 16, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!");
switch_img_draw_text(conference->canvas->img, 10, 80, color, 24, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!");
switch_img_draw_text(conference->canvas->img, 10, 160, color, 36, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!");
switch_img_draw_text(conference->canvas->img, 10, 300, color, 72, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!");
switch_img_fill(conference->canvas->img, 300, 10, 400, 40, color); switch_img_txt_handle_create(&txthandle, "/usr/share/fonts/truetype/Microsoft/Verdana.ttf",
"#FFFFFF", 24, 0, NULL);
switch_img_txt_handle_render(txthandle, conference->canvas->img, 10, 10, "W00t this works!", NULL, NULL, 0, 0);
switch_color_set(&color, "#FF0000"); switch_color_set(&color, "#FF0000");
switch_img_draw_text(conference->canvas->img, 300, 10, color, 32, "FreeSWITCH"); switch_img_fill(conference->canvas->img, 300, 10, 400, 40, color);
switch_img_txt_handle_render(txthandle, conference->canvas->img, 300, 22, "W00t this works!", NULL, NULL, 0, 0);
switch_img_txt_handle_destroy(&txthandle);
//switch_img_draw_text(conference->canvas->img, 300, 10, color, 32, "FreeSWITCH");
} }
if (used) { if (used) {
@ -1654,6 +1612,11 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread
switch_img_free(&layer->cur_img); switch_img_free(&layer->cur_img);
switch_img_free(&layer->img); switch_img_free(&layer->img);
switch_img_free(&layer->banner_img);
if (layer->txthandle) {
switch_img_txt_handle_destroy(&layer->txthandle);
}
} }
for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) { for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) {

View File

@ -22,6 +22,7 @@
* the Initial Developer. All Rights Reserved. * the Initial Developer. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Anthony Minessale II <anthm@freeswitch.org>
* *
* *
* switch_core_video.c -- Core Video * switch_core_video.c -- Core Video
@ -29,6 +30,7 @@
*/ */
#include <switch.h> #include <switch.h>
#include <switch_utf8.h>
SWITCH_DECLARE(switch_image_t *)switch_img_alloc(switch_image_t *img, SWITCH_DECLARE(switch_image_t *)switch_img_alloc(switch_image_t *img,
@ -247,7 +249,7 @@ SWITCH_DECLARE(void) switch_img_add_text(void *buffer, int w, int x, int y, char
} }
} }
SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str) SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, const char *color_str)
{ {
uint8_t y = 134; uint8_t y = 134;
uint8_t u = 128; uint8_t u = 128;
@ -256,8 +258,13 @@ SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str
if (color_str != NULL && strlen(color_str) == 7) { if (color_str != NULL && strlen(color_str) == 7) {
uint8_t red, green, blue; uint8_t red, green, blue;
char str[7]; char str[7];
int i;
color_str++; color_str++;
strncpy(str, color_str, 6); strncpy(str, color_str, 6);
for(i = 0; i < 6; i++) {
str[i] = switch_toupper(str[i]);
}
red = (str[0] >= 'A' ? (str[0] - 'A' + 10) * 16 : (str[0] - '0') * 16) + (str[1] >= 'A' ? (str[1] - 'A' + 10) : (str[0] - '0')); red = (str[0] >= 'A' ? (str[0] - 'A' + 10) * 16 : (str[0] - '0') * 16) + (str[1] >= 'A' ? (str[1] - 'A' + 10) : (str[0] - '0'));
green = (str[2] >= 'A' ? (str[2] - 'A' + 10) * 16 : (str[2] - '0') * 16) + (str[3] >= 'A' ? (str[3] - 'A' + 10) : (str[0] - '0')); green = (str[2] >= 'A' ? (str[2] - 'A' + 10) * 16 : (str[2] - '0') * 16) + (str[3] >= 'A' ? (str[3] - 'A' + 10) : (str[0] - '0'));
blue = (str[4] >= 'A' ? (str[4] - 'A' + 10) * 16 : (str[4] - '0') * 16) + (str[5] >= 'A' ? (str[5] - 'A' + 10) : (str[0] - '0')); blue = (str[4] >= 'A' ? (str[4] - 'A' + 10) * 16 : (str[4] - '0') * 16) + (str[5] >= 'A' ? (str[5] - 'A' + 10) : (str[0] - '0'));
@ -273,6 +280,194 @@ SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str
color->v = v; color->v = v;
} }
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
struct switch_img_txt_handle_s {
FT_Library library;
FT_Face face;
char *font_family;
double angle;
uint16_t font_size;
switch_yuv_color_t color;
switch_image_t *img;
switch_memory_pool_t *pool;
int free_pool;
};
SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_create(switch_img_txt_handle_t **handleP, const char *font_family,
const char *font_color, uint16_t font_size, double angle, switch_memory_pool_t *pool)
{
int free_pool = 0;
switch_img_txt_handle_t *new_handle;
if (!pool) {
free_pool = 1;
switch_core_new_memory_pool(&pool);
}
new_handle = switch_core_alloc(pool, sizeof(*new_handle));
if (FT_Init_FreeType(&new_handle->library)) {
return SWITCH_STATUS_FALSE;
}
new_handle->pool = pool;
new_handle->free_pool = free_pool;
new_handle->font_family = switch_core_strdup(new_handle->pool, font_family);
new_handle->font_size = font_size;
new_handle->angle = angle;
switch_color_set(&new_handle->color, font_color);
*handleP = new_handle;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void) switch_img_txt_handle_destroy(switch_img_txt_handle_t **handleP)
{
switch_img_txt_handle_t *old_handle = *handleP;
switch_memory_pool_t *pool;
*handleP = NULL;
if (old_handle->library) {
FT_Done_FreeType(old_handle->library);
old_handle->library = NULL;
}
pool = old_handle->pool;
if (old_handle->free_pool) {
switch_core_destroy_memory_pool(&pool);
pool = NULL;
old_handle = NULL;
}
}
static void draw_bitmap(switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y, switch_yuv_color_t color)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
switch (bitmap->pixel_mode) {
case FT_PIXEL_MODE_GRAY: // it should always be GRAY since we use FT_LOAD_RENDER?
break;
case FT_PIXEL_MODE_NONE:
case FT_PIXEL_MODE_MONO:
case FT_PIXEL_MODE_GRAY2:
case FT_PIXEL_MODE_GRAY4:
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "unsupported pixel mode %d\n", bitmap->pixel_mode);
return;
}
for ( i = x, p = 0; i < x_max; i++, p++ ) {
for ( j = y, q = 0; j < y_max; j++, q++ ) {
if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
if (bitmap->buffer[q * bitmap->width + p] > 128) {
switch_img_draw_pixel(img, i, j, color);
}
}
}
}
SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_render(switch_img_txt_handle_t *handle, switch_image_t *img,
int x, int y, const char *text,
const char *font_family, const char *font_color, uint16_t font_size, double angle)
{
FT_GlyphSlot slot;
FT_Matrix matrix; /* transformation matrix */
FT_Vector pen; /* untransformed origin */
FT_Error error;
int target_height;
int index = 0;
FT_ULong ch;
FT_Face face;
if (zstr(text)) return SWITCH_STATUS_FALSE;
if (font_family) {
handle->font_family = switch_core_strdup(handle->pool, font_family);
} else {
font_family = handle->font_family;
}
if (font_size) {
handle->font_size = font_size;
} else {
font_size = handle->font_size;
}
if (font_color) {
switch_color_set(&handle->color, font_color);
}
handle->angle = angle;
//angle = 0; (45.0 / 360 ) * 3.14159 * 2;
target_height = img->d_h;
error = FT_New_Face(handle->library, font_family, 0, &face); /* create face object */
if (error) {printf("WTF %s %d\n", font_family, __LINE__); return SWITCH_STATUS_FALSE;}
/* use 50pt at 100dpi */
error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 96); /* set character size */
if (error) {printf("WTF %d\n", __LINE__); return SWITCH_STATUS_FALSE;}
slot = face->glyph;
/* set up matrix */
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
pen.x = x * 64;
pen.y = (target_height - y) * 64;
while(*(text + index)) {
ch = switch_u8_get_char((char *)text, &index);
if (ch == '\n') {
pen.x = x * 64;
pen.y -= (font_size + font_size / 4) * 64;
continue;
}
/* set transformation */
FT_Set_Transform(face, &matrix, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char(face, ch, FT_LOAD_RENDER);
if (error) continue;
/* now, draw to our target surface (convert position) */
draw_bitmap(img, &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top + font_size, handle->color);
/* increment pen position */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
FT_Done_Face(face);
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c

516
src/switch_utf8.c Normal file
View File

@ -0,0 +1,516 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* 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
* Seven Du <dujinfang@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Anthony Minessale II <anthm@freeswitch.org>
*
*
* switch_utf8.c UTf8
*
*/
#include "switch_utf8.h"
/*
Basic UTF-8 manipulation routines
by Jeff Bezanson
placed in the public domain Fall 2005
This code is designed to provide the utilities you need to manipulate
UTF-8 as an internal string encoding. These functions do not perform the
error checking normally needed when handling UTF-8 data, so if you happen
to be from the Unicode Consortium you will want to flay me alive.
I do this because error checking can be performed at the boundaries (I/O),
with these routines reserved for higher performance on data known to be
valid.
*/
static const uint32_t offsetsFromUTF8[6] = {
0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL
};
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/* returns length of next utf-8 sequence */
SWITCH_DECLARE(int) switch_u8_seqlen(char *s)
{
return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
}
/* conversions without error checking
only works for valid UTF-8, i.e. no 5- or 6-byte sequences
srcsz = source size in bytes, or -1 if 0-terminated
sz = dest size in # of wide characters
returns # characters converted
dest will always be L'\0'-terminated, even if there isn't enough room
for all the characters.
if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
*/
SWITCH_DECLARE(int) switch_u8_toucs(uint32_t *dest, int sz, char *src, int srcsz)
{
uint32_t ch;
char *src_end = src + srcsz;
int nb;
int i=0;
while (i < sz-1) {
nb = trailingBytesForUTF8[(unsigned char)*src];
if (srcsz == -1) {
if (*src == 0)
goto done_toucs;
}
else {
if (src + nb >= src_end)
goto done_toucs;
}
ch = 0;
switch (nb) {
/* these fall through deliberately */
case 3: ch += (unsigned char)*src++; ch <<= 6;
case 2: ch += (unsigned char)*src++; ch <<= 6;
case 1: ch += (unsigned char)*src++; ch <<= 6;
case 0: ch += (unsigned char)*src++;
}
ch -= offsetsFromUTF8[nb];
dest[i++] = ch;
}
done_toucs:
dest[i] = 0;
return i;
}
/* srcsz = number of source characters, or -1 if 0-terminated
sz = size of dest buffer in bytes
returns # characters converted
dest will only be '\0'-terminated if there is enough space. this is
for consistency; imagine there are 2 bytes of space left, but the next
character requires 3 bytes. in this case we could NUL-terminate, but in
general we can't when there's insufficient space. therefore this function
only NUL-terminates if all the characters fit, and there's space for
the NUL as well.
the destination string will never be bigger than the source string.
*/
SWITCH_DECLARE(int) switch_u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz)
{
uint32_t ch;
int i = 0;
char *dest_end = dest + sz;
while (srcsz<0 ? src[i]!=0 : i < srcsz) {
ch = src[i];
if (ch < 0x80) {
if (dest >= dest_end)
return i;
*dest++ = (char)ch;
}
else if (ch < 0x800) {
if (dest >= dest_end-1)
return i;
*dest++ = (ch>>6) | 0xC0;
*dest++ = (ch & 0x3F) | 0x80;
}
else if (ch < 0x10000) {
if (dest >= dest_end-2)
return i;
*dest++ = (ch>>12) | 0xE0;
*dest++ = ((ch>>6) & 0x3F) | 0x80;
*dest++ = (ch & 0x3F) | 0x80;
}
else if (ch < 0x110000) {
if (dest >= dest_end-3)
return i;
*dest++ = (ch>>18) | 0xF0;
*dest++ = ((ch>>12) & 0x3F) | 0x80;
*dest++ = ((ch>>6) & 0x3F) | 0x80;
*dest++ = (ch & 0x3F) | 0x80;
}
i++;
}
if (dest < dest_end)
*dest = '\0';
return i;
}
SWITCH_DECLARE(int) switch_u8_wc_toutf8(char *dest, uint32_t ch)
{
if (ch < 0x80) {
dest[0] = (char)ch;
return 1;
}
if (ch < 0x800) {
dest[0] = (ch>>6) | 0xC0;
dest[1] = (ch & 0x3F) | 0x80;
return 2;
}
if (ch < 0x10000) {
dest[0] = (ch>>12) | 0xE0;
dest[1] = ((ch>>6) & 0x3F) | 0x80;
dest[2] = (ch & 0x3F) | 0x80;
return 3;
}
if (ch < 0x110000) {
dest[0] = (ch>>18) | 0xF0;
dest[1] = ((ch>>12) & 0x3F) | 0x80;
dest[2] = ((ch>>6) & 0x3F) | 0x80;
dest[3] = (ch & 0x3F) | 0x80;
return 4;
}
return 0;
}
/* charnum => byte offset */
SWITCH_DECLARE(int) switch_u8_offset(char *str, int charnum)
{
int offs=0;
while (charnum > 0 && str[offs]) {
(void)(isutf(str[++offs]) || isutf(str[++offs]) ||
isutf(str[++offs]) || ++offs);
charnum--;
}
return offs;
}
/* byte offset => charnum */
SWITCH_DECLARE(int) switch_u8_charnum(char *s, int offset)
{
int charnum = 0, offs=0;
while (offs < offset && s[offs]) {
(void)(isutf(s[++offs]) || isutf(s[++offs]) ||
isutf(s[++offs]) || ++offs);
charnum++;
}
return charnum;
}
/* number of characters */
SWITCH_DECLARE(int) switch_u8_strlen(char *s)
{
int count = 0;
int i = 0;
while (switch_u8_nextchar(s, &i) != 0)
count++;
return count;
}
/* reads the next utf-8 sequence out of a string, updating an index */
SWITCH_DECLARE(uint32_t) switch_u8_nextchar(char *s, int *i)
{
uint32_t ch = 0;
int sz = 0;
do {
ch <<= 6;
ch += (unsigned char)s[(*i)++];
sz++;
} while (s[*i] && !isutf(s[*i]));
ch -= offsetsFromUTF8[sz-1];
return ch;
}
SWITCH_DECLARE(void) switch_u8_inc(char *s, int *i)
{
(void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) ||
isutf(s[++(*i)]) || ++(*i));
}
SWITCH_DECLARE(void) switch_u8_dec(char *s, int *i)
{
(void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||
isutf(s[--(*i)]) || --(*i));
}
SWITCH_DECLARE(int) octal_digit(char c)
{
return (c >= '0' && c <= '7');
}
SWITCH_DECLARE(int) hex_digit(char c)
{
return ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f'));
}
/* assumes that src points to the character after a backslash
returns number of input characters processed */
SWITCH_DECLARE(int) switch_u8_read_escape_sequence(char *str, uint32_t *dest)
{
uint32_t ch;
char digs[9]="\0\0\0\0\0\0\0\0";
int dno=0, i=1;
ch = (uint32_t)str[0]; /* take literal character */
if (str[0] == 'n')
ch = L'\n';
else if (str[0] == 't')
ch = L'\t';
else if (str[0] == 'r')
ch = L'\r';
else if (str[0] == 'b')
ch = L'\b';
else if (str[0] == 'f')
ch = L'\f';
else if (str[0] == 'v')
ch = L'\v';
else if (str[0] == 'a')
ch = L'\a';
else if (octal_digit(str[0])) {
i = 0;
do {
digs[dno++] = str[i++];
} while (octal_digit(str[i]) && dno < 3);
ch = strtol(digs, NULL, 8);
}
else if (str[0] == 'x') {
while (hex_digit(str[i]) && dno < 2) {
digs[dno++] = str[i++];
}
if (dno > 0)
ch = strtol(digs, NULL, 16);
}
else if (str[0] == 'u') {
while (hex_digit(str[i]) && dno < 4) {
digs[dno++] = str[i++];
}
if (dno > 0)
ch = strtol(digs, NULL, 16);
}
else if (str[0] == 'U') {
while (hex_digit(str[i]) && dno < 8) {
digs[dno++] = str[i++];
}
if (dno > 0)
ch = strtol(digs, NULL, 16);
}
*dest = ch;
return i;
}
/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
example: u8_unescape(mybuf, 256, "hello\\u220e")
note the double backslash is needed if called on a C string literal */
SWITCH_DECLARE(int) switch_u8_unescape(char *buf, int sz, char *src)
{
int c=0, amt;
uint32_t ch;
char temp[4];
while (*src && c < sz) {
if (*src == '\\') {
src++;
amt = switch_u8_read_escape_sequence(src, &ch);
}
else {
ch = (uint32_t)*src;
amt = 1;
}
src += amt;
amt = switch_u8_wc_toutf8(temp, ch);
if (amt > sz-c)
break;
memcpy(&buf[c], temp, amt);
c += amt;
}
if (c < sz)
buf[c] = '\0';
return c;
}
SWITCH_DECLARE(int) switch_u8_escape_wchar(char *buf, int sz, uint32_t ch)
{
if (ch == L'\n')
return snprintf(buf, sz, "\\n");
else if (ch == L'\t')
return snprintf(buf, sz, "\\t");
else if (ch == L'\r')
return snprintf(buf, sz, "\\r");
else if (ch == L'\b')
return snprintf(buf, sz, "\\b");
else if (ch == L'\f')
return snprintf(buf, sz, "\\f");
else if (ch == L'\v')
return snprintf(buf, sz, "\\v");
else if (ch == L'\a')
return snprintf(buf, sz, "\\a");
else if (ch == L'\\')
return snprintf(buf, sz, "\\\\");
else if (ch < 32 || ch == 0x7f)
return snprintf(buf, sz, "\\x%hhX", (unsigned char)ch);
else if (ch > 0xFFFF)
return snprintf(buf, sz, "\\U%.8X", (uint32_t)ch);
else if (ch >= 0x80 && ch <= 0xFFFF)
return snprintf(buf, sz, "\\u%.4hX", (unsigned short)ch);
return snprintf(buf, sz, "%c", (char)ch);
}
SWITCH_DECLARE(int) switch_u8_escape(char *buf, int sz, char *src, int escape_quotes)
{
int c=0, i=0, amt;
while (src[i] && c < sz) {
if (escape_quotes && src[i] == '"') {
amt = snprintf(buf, sz - c, "\\\"");
i++;
}
else {
amt = switch_u8_escape_wchar(buf, sz - c, switch_u8_nextchar(src, &i));
}
c += amt;
buf += amt;
}
if (c < sz)
*buf = '\0';
return c;
}
SWITCH_DECLARE(char *) switch_u8_strchr(char *s, uint32_t ch, int *charn)
{
int i = 0, lasti=0;
uint32_t c;
*charn = 0;
while (s[i]) {
c = switch_u8_nextchar(s, &i);
if (c == ch) {
return &s[lasti];
}
lasti = i;
(*charn)++;
}
return NULL;
}
SWITCH_DECLARE(char *) switch_u8_memchr(char *s, uint32_t ch, size_t sz, int *charn)
{
int i = 0, lasti=0;
uint32_t c;
int csz;
*charn = 0;
while (i < sz) {
c = csz = 0;
do {
c <<= 6;
c += (unsigned char)s[i++];
csz++;
} while (i < sz && !isutf(s[i]));
c -= offsetsFromUTF8[csz-1];
if (c == ch) {
return &s[lasti];
}
lasti = i;
(*charn)++;
}
return NULL;
}
SWITCH_DECLARE(int) switch_u8_is_locale_utf8(char *locale)
{
/* this code based on libutf8 */
const char* cp = locale;
for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) {
if (*cp == '.') {
const char* encoding = ++cp;
for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++)
;
if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5))
|| (cp-encoding == 4 && !strncmp(encoding, "utf8", 4)))
return 1; /* it's UTF-8 */
break;
}
}
return 0;
}
SWITCH_DECLARE(int) switch_u8_vprintf(char *fmt, va_list ap)
{
int cnt, sz=0;
char *buf;
uint32_t *wcs;
sz = 512;
buf = (char*)alloca(sz);
try_print:
cnt = vsnprintf(buf, sz, fmt, ap);
if (cnt >= sz) {
buf = (char*)alloca(cnt - sz + 1);
sz = cnt + 1;
goto try_print;
}
wcs = (uint32_t*)alloca((cnt+1) * sizeof(uint32_t));
cnt = switch_u8_toucs(wcs, cnt+1, buf, cnt);
printf("%ls", (wchar_t*)wcs);
return cnt;
}
SWITCH_DECLARE(int) switch_u8_printf(char *fmt, ...)
{
int cnt;
va_list args;
va_start(args, fmt);
cnt = switch_u8_vprintf(fmt, args);
va_end(args);
return cnt;
}
/* reads the next utf-8 sequence out of a string, updating an index */
SWITCH_DECLARE(uint32_t) switch_u8_get_char(char *s, int *i)
{
u_int32_t ch = 0;
int sz = 0;
do {
ch <<= 6;
ch += (unsigned char)s[(*i)++];
sz++;
} while (s[*i] && !isutf(s[*i]));
ch -= offsetsFromUTF8[sz-1];
return ch;
}