From e52c0a1cdf980f6a4da0a3e2553bf12e4bff92f0 Mon Sep 17 00:00:00 2001 From: Riza Sulistyo Date: Mon, 14 Jul 2014 02:37:06 +0000 Subject: Re #1776: Initial implementation of Libyuv wrapper. Supports: - library detection via autoconf - scaling and conversion function (from/to I420 or BGRA) git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4875 74dad513-b988-da41-8d7b-12977e46ad98 --- aconfigure | 122 ++++++ aconfigure.ac | 75 ++++ build.mak.in | 9 +- pjmedia/build/Makefile | 2 +- pjmedia/build/os-auto.mak.in | 10 +- pjmedia/build/pjmedia.vcproj | 4 + pjmedia/include/pjmedia/config.h | 8 + pjmedia/src/pjmedia/converter.c | 16 +- pjmedia/src/pjmedia/converter_libyuv.c | 658 +++++++++++++++++++++++++++++++++ 9 files changed, 895 insertions(+), 9 deletions(-) create mode 100644 pjmedia/src/pjmedia/converter_libyuv.c diff --git a/aconfigure b/aconfigure index f691976f..0c78a825 100755 --- a/aconfigure +++ b/aconfigure @@ -618,6 +618,8 @@ libcrypto_present libssl_present openssl_h_present ac_no_ssl +ac_libyuv_ldflags +ac_libyuv_cflags ac_openh264_ldflags ac_openh264_cflags ac_v4l2_ldflags @@ -774,6 +776,8 @@ enable_ffmpeg enable_v4l2 with_openh264 enable_openh264 +with_libyuv +enable_libyuv enable_ipp with_ipp with_ipp_samples @@ -1440,6 +1444,7 @@ Optional Features: --disable-ffmpeg Disable ffmpeg (default: not disabled) --disable-v4l2 Disable Video4Linux2 (default: not disabled) --disable-openh264 Disable OpenH264 (default: not disabled) + --disable-libyuv Exclude libyuv in the build --enable-ipp Enable Intel IPP support. Specify the Intel IPP package and samples location using IPPROOT and IPPSAMPLES env var or with --with-ipp and @@ -1479,6 +1484,7 @@ Optional Packages: --with-sdl=DIR Specify alternate libSDL prefix --with-ffmpeg=DIR Specify alternate FFMPEG prefix --with-openh264=DIR Specify alternate OpenH264 prefix + --with-libyuv=DIR Specify alternate libyuv prefix --with-ipp=DIR Specify the Intel IPP location --with-ipp-samples=DIR Specify the Intel IPP samples location --with-ipp-arch=ARCH Specify the Intel IPP ARCH suffix, e.g. "64" or @@ -7142,6 +7148,122 @@ fi +# Check whether --with-libyuv was given. +if test "${with_libyuv+set}" = set; then : + withval=$with_libyuv; +else + with_libyuv=no + +fi + + +if test "x$ac_cross_compile" != "x" -a "x$with_libyuv" = "xno"; then + enable_libyuv=no +fi + + + +# Check whether --enable-libyuv was given. +if test "${enable_libyuv+set}" = set; then : + enableval=$enable_libyuv; if test "$enable_libyuv" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...yes" >&5 +$as_echo "Checking if libyuv is disabled...yes" >&6; } + fi +else + + if test "x$with_libyuv" != "xno" -a "x$with_libyuv" != "x"; then + LIBYUV_PREFIX=$with_libyuv + LIBYUV_CFLAGS="-I$LIBYUV_PREFIX/include" + + case $target in + arm-apple-darwin*) + LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out_ios/Release-iphoneos" + case $ARCH in + *armv7*) + LIBYUV_LIBS="-lyuv_neon" + ;; + *) + ;; + esac + ;; + *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*) + LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out/Release" + ;; + *) + LIBYUV_CFLAGS="" + LIBYUV_LDFLAGS="" + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using libyuv prefix... $with_libyuv" >&5 +$as_echo "Using libyuv prefix... $with_libyuv" >&6; } + else + LIBYUV_CFLAGS="" + LIBYUV_LDFLAGS="" + fi + + LIBYUV_LIBS="$LIBYUV_LIBS -lyuv" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$LIBYUV_LIBS $LIBS" + LDFLAGS="$LIBYUV_LDFLAGS $LDFLAGS" + CFLAGS="$LIBYUV_CFLAGS $CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for I420Scale in -lyuv" >&5 +$as_echo_n "checking for I420Scale in -lyuv... " >&6; } +if ${ac_cv_lib_yuv_I420Scale+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lyuv + $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char I420Scale (); +int +main () +{ +return I420Scale (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_yuv_I420Scale=yes +else + ac_cv_lib_yuv_I420Scale=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_yuv_I420Scale" >&5 +$as_echo "$ac_cv_lib_yuv_I420Scale" >&6; } +if test "x$ac_cv_lib_yuv_I420Scale" = xyes; then : + ac_libyuv_cflags="-DPJMEDIA_HAS_LIBYUV=1 $LIBYUV_CFLAGS" + ac_libyuv_ldflags="$LIBYUV_LDFLAGS $LIBYUV_LIBS" + +else + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + +fi + + +fi + + # Check whether --enable-ipp was given. if test "${enable_ipp+set}" = set; then : diff --git a/aconfigure.ac b/aconfigure.ac index 6c35836e..9fc6919a 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -1160,6 +1160,81 @@ AC_ARG_ENABLE(openh264, ]) +dnl # libyuv alt prefix +AC_ARG_WITH(libyuv, + AC_HELP_STRING([--with-libyuv=DIR], + [Specify alternate libyuv prefix]), + [], + [with_libyuv=no] + ) + +dnl # Do not use default libyuv installation if we are cross-compiling +if test "x$ac_cross_compile" != "x" -a "x$with_libyuv" = "xno"; then + enable_libyuv=no +fi + +dnl # Include libyuv +AC_SUBST(ac_libyuv_cflags) +AC_SUBST(ac_libyuv_ldflags) +AC_ARG_ENABLE(libyuv, + AC_HELP_STRING([--disable-libyuv], + [Exclude libyuv in the build]), + [if test "$enable_libyuv" = "no"; then + AC_MSG_RESULT([Checking if libyuv is disabled...yes]) + fi], + [ + if test "x$with_libyuv" != "xno" -a "x$with_libyuv" != "x"; then + LIBYUV_PREFIX=$with_libyuv + LIBYUV_CFLAGS="-I$LIBYUV_PREFIX/include" + + case $target in + arm-apple-darwin*) + LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out_ios/Release-iphoneos" + case $ARCH in + *armv7*) + LIBYUV_LIBS="-lyuv_neon" + ;; + *) + ;; + esac + ;; + *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*) + LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out/Release" + ;; + *) + LIBYUV_CFLAGS="" + LIBYUV_LDFLAGS="" + ;; + esac + + AC_MSG_RESULT([Using libyuv prefix... $with_libyuv]) + else + LIBYUV_CFLAGS="" + LIBYUV_LDFLAGS="" + fi + + LIBYUV_LIBS="$LIBYUV_LIBS -lyuv" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$LIBYUV_LIBS $LIBS" + LDFLAGS="$LIBYUV_LDFLAGS $LDFLAGS" + CFLAGS="$LIBYUV_CFLAGS $CFLAGS" + + AC_CHECK_LIB(yuv, + I420Scale, + [ ac_libyuv_cflags="-DPJMEDIA_HAS_LIBYUV=1 $LIBYUV_CFLAGS" + ac_libyuv_ldflags="$LIBYUV_LDFLAGS $LIBYUV_LIBS" + ], + [ LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + ], + [] + ) + ]) dnl ######################################################## diff --git a/build.mak.in b/build.mak.in index 07f54baf..61b156dd 100644 --- a/build.mak.in +++ b/build.mak.in @@ -152,12 +152,15 @@ QT_CFLAGS = @ac_qt_cflags@ # iOS IOS_CFLAGS = @ac_ios_cflags@ +# libyuv +LIBYUV_CFLAGS = @ac_libyuv_cflags@ +LIBYUV_LDFLAGS = @ac_libyuv_ldflags@ + # PJMEDIA features exclusion PJ_VIDEO_CFLAGS += $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \ - $(OPENH264_CFLAGS) $(IOS_CFLAGS) + $(OPENH264_CFLAGS) $(IOS_CFLAGS) $(LIBYUV_CFLAGS) PJ_VIDEO_LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \ - $(OPENH264_LDFLAGS) - + $(OPENH264_LDFLAGS) $(LIBYUV_LDFLAGS) # CFLAGS, LDFLAGS, and LIBS to be used by applications export APP_CC := @CC@ diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 7ff929f6..54395c38 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -60,7 +60,7 @@ export PJMEDIA_SRCDIR = ../src/pjmedia export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ alaw_ulaw.o alaw_ulaw_table.o avi_player.o \ bidirectional.o clock_thread.o codec.o conference.o \ - conf_switch.o converter.o converter_libswscale.o \ + conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \ delaybuf.o echo_common.o \ echo_port.o echo_suppress.o endpoint.o errno.o \ event.o format.o ffmpeg_util.o \ diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in index ea057824..91b14aa4 100644 --- a/pjmedia/build/os-auto.mak.in +++ b/pjmedia/build/os-auto.mak.in @@ -25,11 +25,17 @@ QT_CFLAGS = @ac_qt_cflags@ # iOS IOS_CFLAGS = @ac_ios_cflags@ +# libyuv +LIBYUV_CFLAGS = @ac_libyuv_cflags@ +LIBYUV_LDFLAGS = @ac_libyuv_ldflags@ + + # PJMEDIA features exclusion export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \ $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \ - $(IOS_CFLAGS) -export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) + $(IOS_CFLAGS) $(LIBYUV_CFLAGS) +export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \ + $(LIBYUV_LDFLAGS) # Define the desired sound device backend # Valid values are: diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj index 22fe2764..aae0101c 100644 --- a/pjmedia/build/pjmedia.vcproj +++ b/pjmedia/build/pjmedia.vcproj @@ -3802,6 +3802,10 @@ RelativePath="..\src\pjmedia\converter_libswscale.c" > + + diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 24fd5a99..c075538b 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1281,6 +1281,14 @@ # endif #endif +/** + * Specify if libyuv is available. + * + * Default: 0 (disable) + */ +#ifndef PJMEDIA_HAS_LIBYUV +# define PJMEDIA_HAS_LIBYUV 0 +#endif /** * @} diff --git a/pjmedia/src/pjmedia/converter.c b/pjmedia/src/pjmedia/converter.c index 7041b959..11948467 100644 --- a/pjmedia/src/pjmedia/converter.c +++ b/pjmedia/src/pjmedia/converter.c @@ -34,14 +34,16 @@ PJ_DECL(pj_status_t) pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr); #endif +#if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 +PJ_DECL(pj_status_t) +pjmedia_libyuv_converter_init(pjmedia_converter_mgr *mgr); +#endif PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **p_mgr) { pjmedia_converter_mgr *mgr; -#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL pj_status_t status = PJ_SUCCESS; -#endif mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr); pj_list_init(&mgr->factory_list); @@ -49,6 +51,14 @@ PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, if (!converter_manager_instance) converter_manager_instance = mgr; +#if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 + status = pjmedia_libyuv_converter_init(mgr); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(THIS_FILE, status, + "Error initializing libyuv converter")); + } +#endif + #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL status = pjmedia_libswscale_converter_init(mgr); if (status != PJ_SUCCESS) { @@ -60,7 +70,7 @@ PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, if (p_mgr) *p_mgr = mgr; - return PJ_SUCCESS; + return status; } PJ_DEF(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void) diff --git a/pjmedia/src/pjmedia/converter_libyuv.c b/pjmedia/src/pjmedia/converter_libyuv.c new file mode 100644 index 00000000..52435d1b --- /dev/null +++ b/pjmedia/src/pjmedia/converter_libyuv.c @@ -0,0 +1,658 @@ +/* $Id$ */ +/* + * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 + +#include + +static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, + pj_pool_t *pool, + const pjmedia_conversion_param*prm, + pjmedia_converter **p_cv); + +static void factory_destroy_factory(pjmedia_converter_factory *cf); + +static pj_status_t libyuv_conv_convert(pjmedia_converter *converter, + pjmedia_frame *src_frame, + pjmedia_frame *dst_frame); + +static void libyuv_conv_destroy(pjmedia_converter *converter); + +static pjmedia_converter_factory_op libyuv_factory_op = +{ + &factory_create_converter, + &factory_destroy_factory +}; + +static pjmedia_converter_op libyuv_converter_op = +{ + &libyuv_conv_convert, + &libyuv_conv_destroy +}; + +typedef struct fmt_info +{ + const pjmedia_video_format_info *vid_fmt_info; + pjmedia_video_apply_fmt_param apply_param; +} fmt_info; + +typedef enum conv_func_type +{ + CONV_PACK_TO_PACK, + CONV_PACK_TO_PLANAR, + CONV_PLANAR_TO_PACK, + CONV_PLANAR_TO_PLANAR, + SCALE_PACK, + SCALE_PLANAR +} conv_func_type; + +typedef void (*gen_conv_func)(); + +typedef int (*conv_pack_to_pack_method)(const uint8* src, int src_stride, + uint8* dst, int dst_stride, + int width, int height); + +typedef int (*conv_pack_to_planar_method)(const uint8* src, int src_stride, + uint8* dst1, int dst_stride1, + uint8* dst2, int dst_stride2, + uint8* dst3, int dst_stride3, + int width, int height); + +typedef int (*conv_planar_to_pack_method)(const uint8* src1, int src_stride1, + const uint8* src2, int src_stride2, + const uint8* src3, int src_stride3, + uint8* dst, int dst_stride, + int width, int height); + +typedef int (*conv_planar_to_planar_method)(const uint8* src1, int src_stride1, + const uint8* src2, int src_stride2, + const uint8* src3, int src_stride3, + uint8* dst1, int dst_stride1, + uint8* dst2, int dst_stride2, + uint8* dst3, int dst_stride3, + int width, int height); + +typedef int (*scale_pack_method) + (const uint8* src_argb, int src_stride_argb, + int src_width, int src_height, + uint8* dst_argb, int dst_stride_argb, + int dst_width, int dst_height, + enum FilterMode filtering); + +typedef int (*scale_planar_method) + (const uint8* src_y, int src_stride_y, + const uint8* src_u, int src_stride_u, + const uint8* src_v, int src_stride_v, + int src_width, int src_height, + uint8* dst_y, int dst_stride_y, + uint8* dst_u, int dst_stride_u, + uint8* dst_v, int dst_stride_v, + int dst_width, int dst_height, + enum FilterMode filtering); + +typedef union act_method +{ + conv_pack_to_pack_method conv_pack_to_pack; + conv_pack_to_planar_method conv_pack_to_planar; + conv_planar_to_pack_method conv_planar_to_pack; + conv_planar_to_planar_method conv_planar_to_planar; + scale_pack_method scale_pack; + scale_planar_method scale_planar; +} act_method; + +typedef struct fmt_convert_map { + pj_uint32_t src_id; + pj_uint32_t dst_id; + conv_func_type func_type; + gen_conv_func conv_func; +} fmt_convert_map; + +/* Maximum number of steps/act needed for the conversion/scale process. */ +#define MAXIMUM_ACT 3 + +/* Define the filter mode for libyuv: + * 0 : None (fastest) + * 1 : Linear + * 2 : Biinear + * 3 : Filter Box (best quality) + */ +#if !defined(LIBYUV_FILTER_MODE) +# define LIBYUV_FILTER_MODE 3 +#endif + +#define METHOD_IS_SCALE(mtd) mtd>CONV_PLANAR_TO_PLANAR + +/* Macro to help define format conversion table. */ +#define GET_PJ_FORMAT(fmt) PJMEDIA_FORMAT_##fmt + +#define MAP_CONV_PACK_TO_PACK(src,dst,method) GET_PJ_FORMAT(src),\ + GET_PJ_FORMAT(dst),CONV_PACK_TO_PACK,method +#define MAP_CONV_PACK_TO_PLANAR(src,dst,method) GET_PJ_FORMAT(src),\ + GET_PJ_FORMAT(dst),CONV_PACK_TO_PLANAR,method +#define MAP_CONV_PLANAR_TO_PACK(src,dst,method) GET_PJ_FORMAT(src),\ + GET_PJ_FORMAT(dst),CONV_PLANAR_TO_PACK,method +#define MAP_CONV_PLANAR_TO_PLANAR(src,dst,method) GET_PJ_FORMAT(src),\ + GET_PJ_FORMAT(dst),CONV_PLANAR_TO_PLANAR,method +#define MAP_SCALE_PACK(fmt,method) GET_PJ_FORMAT(fmt),\ + GET_PJ_FORMAT(fmt),SCALE_PACK,method +#define MAP_SCALE_PLANAR(fmt,method) GET_PJ_FORMAT(fmt),\ + GET_PJ_FORMAT(fmt),SCALE_PLANAR,method + +static fmt_convert_map conv_to_i420[] = +{ + {MAP_CONV_PACK_TO_PLANAR(RGB24,I420,RGB24ToI420)}, + {MAP_CONV_PACK_TO_PLANAR(RGBA,I420,ABGRToI420)}, + {MAP_CONV_PACK_TO_PLANAR(BGRA,I420,ARGBToI420)}, + {MAP_CONV_PACK_TO_PLANAR(YUY2,I420,YUY2ToI420)}, + {MAP_CONV_PACK_TO_PLANAR(UYVY,I420,UYVYToI420)}, + {MAP_CONV_PLANAR_TO_PLANAR(I422,I420,I422ToI420)} +}; + +static fmt_convert_map conv_from_i420[] = +{ + {MAP_CONV_PLANAR_TO_PACK(I420,RGB24,I420ToRGB24)}, + {MAP_CONV_PLANAR_TO_PACK(I420,RGBA,I420ToABGR)}, + {MAP_CONV_PLANAR_TO_PACK(I420,BGRA,I420ToARGB)}, + {MAP_CONV_PLANAR_TO_PACK(I420,YUY2,I420ToYUY2)}, + {MAP_CONV_PLANAR_TO_PACK(I420,UYVY,I420ToUYVY)}, + {MAP_CONV_PLANAR_TO_PLANAR(I420,I422,I420ToI422)}, + {MAP_SCALE_PLANAR(I420,I420Scale)} +}; + +static fmt_convert_map conv_to_bgra[] = +{ + {MAP_CONV_PACK_TO_PACK(RGB24,BGRA,RGB24ToARGB)}, + {MAP_CONV_PACK_TO_PACK(RGBA,BGRA,ABGRToARGB)}, + {MAP_CONV_PACK_TO_PACK(YUY2,BGRA,YUY2ToARGB)}, + {MAP_CONV_PACK_TO_PACK(UYVY,BGRA,UYVYToARGB)}, + {MAP_CONV_PLANAR_TO_PACK(I422,BGRA,I422ToARGB)}, + {MAP_CONV_PLANAR_TO_PACK(I420,BGRA,I420ToARGB)} +}; + +static fmt_convert_map conv_from_bgra[] = +{ + {MAP_CONV_PACK_TO_PACK(BGRA,RGB24,ARGBToRGB24)}, + {MAP_CONV_PACK_TO_PACK(BGRA,RGBA,ARGBToABGR)}, + {MAP_CONV_PACK_TO_PACK(BGRA,YUY2,ARGBToYUY2)}, + {MAP_CONV_PACK_TO_PACK(BGRA,UYVY,ARGBToUYVY)}, + {MAP_CONV_PACK_TO_PLANAR(BGRA,I422,ARGBToI422)}, + {MAP_CONV_PACK_TO_PLANAR(BGRA,I420,ARGBToI420)}, + {MAP_SCALE_PACK(BGRA,ARGBScale)} +}; + +typedef struct converter_act +{ + conv_func_type act_type; + struct fmt_info src_fmt_info; + struct fmt_info dst_fmt_info; + act_method method; +} converter_act; + +struct libyuv_converter +{ + pjmedia_converter base; + int act_num; + converter_act act[MAXIMUM_ACT]; +}; + +/* Find the matched format conversion map. */ +static pj_status_t get_converter_map(pj_uint32_t src_id, + pj_uint32_t dst_id, + const pjmedia_rect_size *src_size, + const pjmedia_rect_size *dst_size, + int act_num, + converter_act *act) +{ + fmt_convert_map *map = NULL; + unsigned cnt = 0, i = 0; + unsigned act_idx = act_num - 1; + +# define GET_MAP(src) \ + do { \ + map=src; \ + cnt=PJ_ARRAY_SIZE(src); \ + }while(0) + + if (src_id == PJMEDIA_FORMAT_I420) { + GET_MAP(conv_from_i420); + } else if (src_id == PJMEDIA_FORMAT_BGRA) { + GET_MAP(conv_from_bgra); + } + + if (!map) { + if (dst_id == PJMEDIA_FORMAT_I420) { + GET_MAP(conv_to_i420); + } else if (dst_id == PJMEDIA_FORMAT_BGRA) { + GET_MAP(conv_to_bgra); + } + } + + if (!map) + return PJ_ENOTSUP; + + for (;icolor_model == PJMEDIA_COLOR_MODEL_YUV) { + return PJMEDIA_FORMAT_I420; + } else { + return PJMEDIA_FORMAT_BGRA; + } +} + +/* This method will find and set all the steps needed for conversion/scale. + * More than one step might be needed, since not all format is provided with + * a direct conversion/scale method. + * e.g : Scale YUY2 (240*320) to YUY2 (320*720) + * - Step 1: Convert YUY2(240*320) to I420 (240*320) + * - Step 2: Scale I420 (320*760) + * - Step 3: Convert I420 (320*760) to YUY2 (320*760) + */ +static int set_converter_act(pj_uint32_t src_id, + pj_uint32_t dst_id, + const pjmedia_rect_size *src_size, + const pjmedia_rect_size *dst_size, + converter_act *act) +{ + unsigned act_num = 0; + pj_uint32_t current_id = src_id; + pj_bool_t need_scale = PJ_FALSE; + + /* Convert to I420 or BGRA if needed. */ + if ((src_id != PJMEDIA_FORMAT_I420) || (src_id != PJMEDIA_FORMAT_BGRA)) { + pj_uint32_t next_id = get_next_conv_fmt(src_id); + if (get_converter_map(src_id, next_id, src_size, dst_size, ++act_num, + act) != PJ_SUCCESS) + { + return 0; + } + + current_id = next_id; + } + + /* Scale if needed */ + need_scale = ((src_size->w != dst_size->w) || + (src_size->h != dst_size->h)); + + if (need_scale) { + if (get_converter_map(current_id, current_id, src_size, dst_size, + ++act_num, act) != PJ_SUCCESS) + { + return 0; + } + } + + /* Convert if needed */ + if (current_id != dst_id) { + if (get_converter_map(current_id, dst_id, src_size, dst_size, ++act_num, + act) != PJ_SUCCESS) + { + return 0; + } + } + + return act_num; +} + +/* Additional buffer might be needed for formats without direct conversion/scale + * method. This method will allocate and set the destination buffer needed by + * the conversion/scaling process. + */ +static pj_status_t set_destination_buffer(pj_pool_t *pool, + struct libyuv_converter *lconv) +{ + int i = 0; + + for (;iact_num-1;++i) { + pj_size_t buffer_size = 0; + fmt_info *info = &lconv->act[i].dst_fmt_info; + + /* Get destination buffer size. */ + (*info->vid_fmt_info->apply_fmt)(info->vid_fmt_info, + &info->apply_param); + + buffer_size = info->apply_param.framebytes; + + /* Allocate buffer. */ + lconv->act[i].dst_fmt_info.apply_param.buffer = + (pj_uint8_t*)pj_pool_alloc(pool, buffer_size); + + if (!lconv->act[i].dst_fmt_info.apply_param.buffer) + return PJ_ENOMEM; + } + return PJ_SUCCESS; +} + +/* Check the act input/output format matched the conversion/scale format. */ +static pj_bool_t check_converter_act(const converter_act *act, + int act_num, + pj_uint32_t src_id, + const pjmedia_rect_size *src_size, + pj_uint32_t dst_id, + const pjmedia_rect_size *dst_size) +{ + if (act_num) { + const struct fmt_info *first_fmt = &act[0].src_fmt_info; + const struct fmt_info *last_fmt = &act[act_num-1].dst_fmt_info; + + if ((first_fmt->vid_fmt_info->id == src_id) && + (first_fmt->apply_param.size.h == src_size->h) && + (first_fmt->apply_param.size.w == src_size->w) && + (last_fmt->vid_fmt_info->id == dst_id) && + (last_fmt->apply_param.size.h == dst_size->h) && + (last_fmt->apply_param.size.w == dst_size->w)) + { + return PJ_TRUE; + } + } + return PJ_FALSE; +} + +static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, + pj_pool_t *pool, + const pjmedia_conversion_param *prm, + pjmedia_converter **p_cv) +{ + const pjmedia_video_format_detail *src_detail, *dst_detail; + const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info; + struct libyuv_converter *lconv = NULL; + pj_status_t status = PJ_ENOTSUP; + + PJ_UNUSED_ARG(cf); + + /* Only supports video */ + if (prm->src.type != PJMEDIA_TYPE_VIDEO || + prm->dst.type != prm->src.type || + prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO || + prm->dst.detail_type != prm->src.detail_type) + { + return status; + } + + /* lookup source format info */ + src_fmt_info = pjmedia_get_video_format_info( + pjmedia_video_format_mgr_instance(), + prm->src.id); + + if (!src_fmt_info) + return status; + + /* lookup destination format info */ + dst_fmt_info = pjmedia_get_video_format_info( + pjmedia_video_format_mgr_instance(), + prm->dst.id); + + if (!dst_fmt_info) + return status; + + src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE); + dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE); + + lconv = PJ_POOL_ZALLOC_T(pool, struct libyuv_converter); + lconv->base.op = &libyuv_converter_op; + + lconv->act_num = set_converter_act(src_fmt_info->id, dst_fmt_info->id, + &src_detail->size, &dst_detail->size, + lconv->act); + + if (!lconv->act_num) { + return status; + } + + if (!check_converter_act(lconv->act, lconv->act_num, + src_fmt_info->id, &src_detail->size, + dst_fmt_info->id, &dst_detail->size)) + { + return status; + } + + status = set_destination_buffer(pool, lconv); + + *p_cv = &lconv->base; + + return status; +} + +static void factory_destroy_factory(pjmedia_converter_factory *cf) +{ + PJ_UNUSED_ARG(cf); +} + +static pj_status_t libyuv_conv_convert(pjmedia_converter *converter, + pjmedia_frame *src_frame, + pjmedia_frame *dst_frame) +{ + struct libyuv_converter *lconv = (struct libyuv_converter*)converter; + int i = 0; + + /* Set the first act buffer from src frame. */ + lconv->act[0].src_fmt_info.apply_param.buffer = src_frame->buf; + + /* Set the last act buffer from dst frame. */ + lconv->act[lconv->act_num-1].dst_fmt_info.apply_param.buffer = + dst_frame->buf; + + for (;iact_num;++i) { + /* Use destination info as the source info for the next act. */ + struct fmt_info *src_fmt_info = (i==0)?&lconv->act[i].src_fmt_info: + &lconv->act[i-1].dst_fmt_info; + + struct fmt_info *dst_fmt_info = &lconv->act[i].dst_fmt_info; + + (*src_fmt_info->vid_fmt_info->apply_fmt)(src_fmt_info->vid_fmt_info, + &src_fmt_info->apply_param); + + (*dst_fmt_info->vid_fmt_info->apply_fmt)(dst_fmt_info->vid_fmt_info, + &dst_fmt_info->apply_param); + + switch (lconv->act[i].act_type) { + case CONV_PACK_TO_PACK: + (*lconv->act[i].method.conv_pack_to_pack)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h); + break; + case CONV_PACK_TO_PLANAR: + (*lconv->act[i].method.conv_pack_to_planar)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.planes[1], + dst_fmt_info->apply_param.strides[1], + dst_fmt_info->apply_param.planes[2], + dst_fmt_info->apply_param.strides[2], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h); + break; + case CONV_PLANAR_TO_PACK: + (*lconv->act[i].method.conv_planar_to_pack)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + (const uint8*)src_fmt_info->apply_param.planes[1], + src_fmt_info->apply_param.strides[1], + (const uint8*)src_fmt_info->apply_param.planes[2], + src_fmt_info->apply_param.strides[2], + dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h); + break; + case CONV_PLANAR_TO_PLANAR: + (*lconv->act[i].method.conv_planar_to_planar)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + (const uint8*)src_fmt_info->apply_param.planes[1], + src_fmt_info->apply_param.strides[1], + (const uint8*)src_fmt_info->apply_param.planes[2], + src_fmt_info->apply_param.strides[2], + dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.planes[1], + dst_fmt_info->apply_param.strides[1], + dst_fmt_info->apply_param.planes[2], + dst_fmt_info->apply_param.strides[2], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h); + break; + case SCALE_PACK: + (*lconv->act[i].method.scale_pack)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + src_fmt_info->apply_param.size.w, + src_fmt_info->apply_param.size.h, + (uint8*)dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h, + LIBYUV_FILTER_MODE); + break; + case SCALE_PLANAR: + (*lconv->act[i].method.scale_planar)( + (const uint8*)src_fmt_info->apply_param.planes[0], + src_fmt_info->apply_param.strides[0], + (const uint8*)src_fmt_info->apply_param.planes[1], + src_fmt_info->apply_param.strides[1], + (const uint8*)src_fmt_info->apply_param.planes[2], + src_fmt_info->apply_param.strides[2], + src_fmt_info->apply_param.size.w, + src_fmt_info->apply_param.size.h, + (uint8*)dst_fmt_info->apply_param.planes[0], + dst_fmt_info->apply_param.strides[0], + (uint8*)dst_fmt_info->apply_param.planes[1], + dst_fmt_info->apply_param.strides[1], + (uint8*)dst_fmt_info->apply_param.planes[2], + dst_fmt_info->apply_param.strides[2], + dst_fmt_info->apply_param.size.w, + dst_fmt_info->apply_param.size.h, + LIBYUV_FILTER_MODE); + break; + }; + } + return PJ_SUCCESS; +} + +static void libyuv_conv_destroy(pjmedia_converter *converter) +{ + PJ_UNUSED_ARG(converter); +} + +static pjmedia_converter_factory libyuv_factory = +{ + NULL, NULL, /* list */ + "libyuv", /* name */ + PJMEDIA_CONVERTER_PRIORITY_NORMAL, /* priority */ + NULL /* op will be init-ed later */ +}; + +PJ_DEF(pj_status_t) +pjmedia_libyuv_converter_init(pjmedia_converter_mgr *mgr) +{ + libyuv_factory.op = &libyuv_factory_op; + return pjmedia_converter_mgr_register_factory(mgr, &libyuv_factory); +} + + +PJ_DEF(pj_status_t) +pjmedia_libyuv_converter_shutdown(pjmedia_converter_mgr *mgr, + pj_pool_t *pool) +{ + PJ_UNUSED_ARG(pool); + return pjmedia_converter_mgr_unregister_factory(mgr, &libyuv_factory, + PJ_TRUE); +} + +#ifdef _MSC_VER +# pragma comment(lib, "libyuv.lib") +#endif + +#endif //#if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 -- cgit v1.2.3