From 0aa83d8efcf477675669569b037f291464c4f146 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 10 Apr 2014 10:01:07 +0000 Subject: Re #1758: Initial implementation of OpenH264 wrapper. Supports: - library detection via autoconf - CBP - packetization modes: 0, 1 - key frame request and indication - obey remote's fmtp Also added video codec test in samples (similar to the one in pjmedia test though). And there are some fixes here and there too (e.g. in vid_codec_util.c). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4815 74dad513-b988-da41-8d7b-12977e46ad98 --- aconfigure | 301 ++++++--- aconfigure.ac | 67 ++ build.mak.in | 11 +- pjmedia/build/Makefile | 2 +- pjmedia/include/pjmedia-codec.h | 1 + pjmedia/include/pjmedia-codec/openh264.h | 69 ++ pjmedia/include/pjmedia/event.h | 5 + pjmedia/src/pjmedia-codec/openh264.cpp | 1080 ++++++++++++++++++++++++++++++ pjmedia/src/pjmedia/vid_codec_util.c | 2 +- pjmedia/src/test/vid_codec_test.c | 28 +- pjsip-apps/build/Samples.mak | 1 + pjsip-apps/src/samples/aviplay.c | 9 + pjsip-apps/src/samples/simpleua.c | 8 + pjsip-apps/src/samples/vid_codec_test.c | 521 ++++++++++++++ pjsip-apps/src/samples/vid_streamutil.c | 10 + pjsip/src/pjsua-lib/pjsua_vid.c | 13 + 16 files changed, 2016 insertions(+), 112 deletions(-) create mode 100644 pjmedia/include/pjmedia-codec/openh264.h create mode 100644 pjmedia/src/pjmedia-codec/openh264.cpp create mode 100644 pjsip-apps/src/samples/vid_codec_test.c diff --git a/aconfigure b/aconfigure index af89e524..025e7e6d 100755 --- a/aconfigure +++ b/aconfigure @@ -1,9 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pjproject 2.x. +# Generated by GNU Autoconf 2.68 for pjproject 2.x. # # -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -132,31 +134,6 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -190,8 +167,7 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" +test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -236,25 +212,21 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : @@ -356,14 +328,6 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -485,10 +449,6 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -523,16 +483,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -544,8 +504,28 @@ else as_mkdir_p=false fi -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -638,6 +618,8 @@ libcrypto_present libssl_present openssl_h_present ac_no_ssl +ac_openh264_ldflags +ac_openh264_cflags ac_v4l2_ldflags ac_v4l2_cflags PKG_CONFIG @@ -790,6 +772,8 @@ enable_sdl with_ffmpeg enable_ffmpeg enable_v4l2 +with_openh264 +enable_openh264 enable_ipp with_ipp with_ipp_samples @@ -1270,6 +1254,8 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1453,6 +1439,7 @@ Optional Features: --disable-sdl Disable SDL (default: not disabled) --disable-ffmpeg Disable ffmpeg (default: not disabled) --disable-v4l2 Disable Video4Linux2 (default: not disabled) + --disable-openh264 Disable OpenH264 (default: not disabled) --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 @@ -1491,6 +1478,7 @@ Optional Packages: include/lib paths) --with-sdl=DIR Specify alternate libSDL prefix --with-ffmpeg=DIR Specify alternate FFMPEG prefix + --with-openh264=DIR Specify alternate OpenH264 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 @@ -1583,9 +1571,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF pjproject configure 2.x -generated by GNU Autoconf 2.69 +generated by GNU Autoconf 2.68 -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1699,7 +1687,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - test -x conftest$ac_exeext + $as_test_x conftest$ac_exeext }; then : ac_retval=0 else @@ -1986,7 +1974,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by pjproject $as_me 2.x, which was -generated by GNU Autoconf 2.69. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2511,7 +2499,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2551,7 +2539,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2604,7 +2592,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2645,7 +2633,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -2703,7 +2691,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2747,7 +2735,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3193,7 +3181,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include -struct stat; +#include +#include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -3306,7 +3295,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3350,7 +3339,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3559,7 +3548,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3599,7 +3588,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3653,7 +3642,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3697,7 +3686,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4479,7 +4468,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -4545,7 +4534,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -6512,7 +6501,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6558,7 +6547,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_SDL_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6663,7 +6652,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_PKG_CONFIG="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7066,6 +7055,94 @@ fi fi + +# Check whether --with-openh264 was given. +if test "${with_openh264+set}" = set; then : + withval=$with_openh264; +else + with_openh264=no + +fi + + +if test "x$ac_cross_compile" != "x" -a "x$with_openh264" = "xno"; then + enable_openh264=no +fi + + + +# Check whether --enable-openh264 was given. +if test "${enable_openh264+set}" = set; then : + enableval=$enable_openh264; + if test "$enable_openh264" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if OpenH264 is disabled... yes" >&5 +$as_echo "Checking if OpenH264 is disabled... yes" >&6; } + fi + +else + + if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then + OPENH264_PREFIX=$with_openh264 + OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" + OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using OpenH264 prefix... $with_openh264" >&5 +$as_echo "Using OpenH264 prefix... $with_openh264" >&6; } + else + OPENH264_CFLAGS="" + OPENH264_LDFLAGS="" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenH264 availability" >&5 +$as_echo_n "checking OpenH264 availability... " >&6; } + + OPENH264_LIBS="-lwels" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$OPENH264_LIBS $LIBS" + LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" + CFLAGS="$OPENH264_CFLAGS $CFLAGS" + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +int main() { CreateSVCEncoder(0); return 0; } + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" + ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } + +else + + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +fi + + + + # Check whether --enable-ipp was given. if test "${enable_ipp+set}" = set; then : enableval=$enable_ipp; @@ -8345,16 +8422,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -8414,16 +8491,28 @@ else as_mkdir_p=false fi - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -8445,7 +8534,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by pjproject $as_me 2.x, which was -generated by GNU Autoconf 2.69. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -8507,10 +8596,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ pjproject config.status 2.x -configured by $0, generated by GNU Autoconf 2.69, +configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -8598,7 +8687,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' diff --git a/aconfigure.ac b/aconfigure.ac index e0da29ab..3af48d72 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -1095,6 +1095,73 @@ AC_ARG_ENABLE(v4l2, ) ]) +dnl # OpenH264 alt prefix +AC_ARG_WITH(openh264, + AC_HELP_STRING([--with-openh264=DIR], + [Specify alternate OpenH264 prefix]), + [], + [with_openh264=no] + ) + +dnl # Do not use default OpenH264 installation if we are cross-compiling +if test "x$ac_cross_compile" != "x" -a "x$with_openh264" = "xno"; then + enable_openh264=no +fi + +dnl # OpenH264 +AC_SUBST(ac_openh264_cflags) +AC_SUBST(ac_openh264_ldflags) +AC_ARG_ENABLE(openh264, + AC_HELP_STRING([--disable-openh264], + [Disable OpenH264 (default: not disabled)]), + [ + if test "$enable_openh264" = "no"; then + AC_MSG_RESULT([Checking if OpenH264 is disabled... yes]) + fi + ], + [ + if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then + OPENH264_PREFIX=$with_openh264 + OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" + OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" + AC_MSG_RESULT([Using OpenH264 prefix... $with_openh264]) + else + OPENH264_CFLAGS="" + OPENH264_LDFLAGS="" + fi + + AC_MSG_CHECKING([OpenH264 availability]) + + OPENH264_LIBS="-lwels" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CFLAGS="$CFLAGS" + + LIBS="$OPENH264_LIBS $LIBS" + LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS" + CFLAGS="$OPENH264_CFLAGS $CFLAGS" + + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include + #include + ]], + [int main() { CreateSVCEncoder(0); return 0; }] + )], + [ ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" + ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" + AC_MSG_RESULT(ok) + ], + [ + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CFLAGS="$SAVED_CFLAGS" + AC_MSG_RESULT(failed) + ]) + + ]) + + + dnl ######################################################## dnl # Intel IPP support dnl # diff --git a/build.mak.in b/build.mak.in index 58e9a682..07f54baf 100644 --- a/build.mak.in +++ b/build.mak.in @@ -133,7 +133,7 @@ endif SDL_CFLAGS = @ac_sdl_cflags@ SDL_LDFLAGS = @ac_sdl_ldflags@ -# FFMPEG dlags +# FFMPEG flags FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@ @@ -141,6 +141,10 @@ FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@ V4L2_CFLAGS = @ac_v4l2_cflags@ V4L2_LDFLAGS = @ac_v4l2_ldflags@ +# OPENH264 flags +OPENH264_CFLAGS = @ac_openh264_cflags@ +OPENH264_LDFLAGS = @ac_openh264_ldflags@ + # QT AC_PJMEDIA_VIDEO_HAS_QT = @ac_pjmedia_video_has_qt@ QT_CFLAGS = @ac_qt_cflags@ @@ -150,8 +154,9 @@ IOS_CFLAGS = @ac_ios_cflags@ # PJMEDIA features exclusion PJ_VIDEO_CFLAGS += $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \ - $(IOS_CFLAGS) -PJ_VIDEO_LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) + $(OPENH264_CFLAGS) $(IOS_CFLAGS) +PJ_VIDEO_LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \ + $(OPENH264_LDFLAGS) # CFLAGS, LDFLAGS, and LIBS to be used by applications diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 23dc715e..9967ece6 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -134,7 +134,7 @@ export PJSDP_LDFLAGS += $(PJMEDIA_LDLIB) \ # Defines for building PJMEDIA-Codec library # export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec -export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o \ +export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o openh264.o \ h263_packetizer.o h264_packetizer.o \ $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ ipp_codecs.o opencore_amr.o silk.o $(CODEC_OBJS) \ diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h index 01e3e842..a35c8540 100644 --- a/pjmedia/include/pjmedia-codec.h +++ b/pjmedia/include/pjmedia-codec.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include diff --git a/pjmedia/include/pjmedia-codec/openh264.h b/pjmedia/include/pjmedia-codec/openh264.h new file mode 100644 index 00000000..d41ea428 --- /dev/null +++ b/pjmedia/include/pjmedia-codec/openh264.h @@ -0,0 +1,69 @@ +/* $Id$ */ +/* + * Copyright (C) 2014 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 + */ +#ifndef __PJMEDIA_CODEC_OPENH264_H__ +#define __PJMEDIA_CODEC_OPENH264_H__ + +#include +#include + +/** + * @file pjmedia-codec/openh264.h + * @brief Open H.264 codec + */ + + +PJ_BEGIN_DECL + +/** + * @defgroup PJMEDIA_CODEC_OPENH264 Open H.264 Codec + * @ingroup PJMEDIA_CODEC_VID_CODECS + * @{ + */ + +/** + * Initialize and register OpenH264 codec factory. + * + * @param mgr The video codec manager instance where this codec will + * be registered to. Specify NULL to use default instance + * (in that case, an instance of video codec manager must + * have been created beforehand). + * @param pf Pool factory. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_openh264_vid_init(pjmedia_vid_codec_mgr *mgr, + pj_pool_factory *pf); + +/** + * Unregister OpenH264 video codecs factory from the video codec manager and + * deinitialize the codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_openh264_vid_deinit(void); + + +/** + * @} PJMEDIA_CODEC_OPENH264 + */ + + +PJ_END_DECL + +#endif /* __PJMEDIA_CODEC_OPENH264_H__ */ diff --git a/pjmedia/include/pjmedia/event.h b/pjmedia/include/pjmedia/event.h index 84baba9d..360a648e 100644 --- a/pjmedia/include/pjmedia/event.h +++ b/pjmedia/include/pjmedia/event.h @@ -239,6 +239,11 @@ typedef pj_status_t pjmedia_event_cb(pjmedia_event *event, */ typedef enum pjmedia_event_publish_flag { + /** + * Default flag. + */ + PJMEDIA_EVENT_PUBLISH_DEFAULT, + /** * Publisher will only post the event to the event manager. It is the * event manager that will later notify all the publisher's subscribers. diff --git a/pjmedia/src/pjmedia-codec/openh264.cpp b/pjmedia/src/pjmedia-codec/openh264.cpp new file mode 100644 index 00000000..2f91ceb5 --- /dev/null +++ b/pjmedia/src/pjmedia-codec/openh264.cpp @@ -0,0 +1,1080 @@ +/* $Id$ */ +/* + * Copyright (C)2014 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 +#include +#include +#include + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && \ + PJMEDIA_HAS_OPENH264_CODEC != 0 && \ + defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + +/* OpenH264: */ +#include +#include + +/* + * Constants + */ +#define THIS_FILE "openh264.cpp" +#define DEFAULT_WIDTH 720 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_FPS 15 +#define DEFAULT_AVG_BITRATE 256000 +#define DEFAULT_MAX_BITRATE 256000 + +#define MAX_RX_WIDTH 1200 +#define MAX_RX_HEIGHT 800 + + +/* + * Factory operations. + */ +static pj_status_t oh264_test_alloc(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info ); +static pj_status_t oh264_default_attr(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec_param *attr ); +static pj_status_t oh264_enum_info(pjmedia_vid_codec_factory *factory, + unsigned *count, + pjmedia_vid_codec_info codecs[]); +static pj_status_t oh264_alloc_codec(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec **p_codec); +static pj_status_t oh264_dealloc_codec(pjmedia_vid_codec_factory *factory, + pjmedia_vid_codec *codec ); + + +/* + * Codec operations + */ +static pj_status_t oh264_codec_init(pjmedia_vid_codec *codec, + pj_pool_t *pool ); +static pj_status_t oh264_codec_open(pjmedia_vid_codec *codec, + pjmedia_vid_codec_param *param ); +static pj_status_t oh264_codec_close(pjmedia_vid_codec *codec); +static pj_status_t oh264_codec_modify(pjmedia_vid_codec *codec, + const pjmedia_vid_codec_param *param); +static pj_status_t oh264_codec_get_param(pjmedia_vid_codec *codec, + pjmedia_vid_codec_param *param); +static pj_status_t oh264_codec_encode_begin(pjmedia_vid_codec *codec, + const pjmedia_vid_encode_opt *opt, + const pjmedia_frame *input, + unsigned out_size, + pjmedia_frame *output, + pj_bool_t *has_more); +static pj_status_t oh264_codec_encode_more(pjmedia_vid_codec *codec, + unsigned out_size, + pjmedia_frame *output, + pj_bool_t *has_more); +static pj_status_t oh264_codec_decode(pjmedia_vid_codec *codec, + pj_size_t count, + pjmedia_frame packets[], + unsigned out_size, + pjmedia_frame *output); + +/* Definition for OpenH264 codecs operations. */ +static pjmedia_vid_codec_op oh264_codec_op = +{ + &oh264_codec_init, + &oh264_codec_open, + &oh264_codec_close, + &oh264_codec_modify, + &oh264_codec_get_param, + &oh264_codec_encode_begin, + &oh264_codec_encode_more, + &oh264_codec_decode, + NULL +}; + +/* Definition for OpenH264 codecs factory operations. */ +static pjmedia_vid_codec_factory_op oh264_factory_op = +{ + &oh264_test_alloc, + &oh264_default_attr, + &oh264_enum_info, + &oh264_alloc_codec, + &oh264_dealloc_codec +}; + + +static struct oh264_factory +{ + pjmedia_vid_codec_factory base; + pjmedia_vid_codec_mgr *mgr; + pj_pool_factory *pf; + pj_pool_t *pool; +} oh264_factory; + + +typedef struct oh264_codec_data +{ + pj_pool_t *pool; + pjmedia_vid_codec_param *prm; + pj_bool_t whole; + pjmedia_h264_packetizer *pktz; + + /* Encoder state */ + ISVCEncoder *enc; + SSourcePicture *esrc_pic; + unsigned enc_input_size; + pj_uint8_t *enc_frame_whole; + unsigned enc_frame_size; + unsigned enc_processed; + pj_timestamp ets; + SFrameBSInfo bsi; + int ilayer; + + /* Decoder state */ + ISVCDecoder *dec; + pj_uint8_t *dec_buf; + unsigned dec_buf_size; +} oh264_codec_data; + +struct SLayerPEncCtx +{ + pj_int32_t iDLayerQp; + SSliceConfig sSliceCfg; +}; + +PJ_DEF(pj_status_t) pjmedia_codec_openh264_vid_init(pjmedia_vid_codec_mgr *mgr, + pj_pool_factory *pf) +{ + const pj_str_t h264_name = { (char*)"H264", 4}; + pj_status_t status; + + if (oh264_factory.pool != NULL) { + /* Already initialized. */ + return PJ_SUCCESS; + } + + if (!mgr) mgr = pjmedia_vid_codec_mgr_instance(); + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + /* Create OpenH264 codec factory. */ + oh264_factory.base.op = &oh264_factory_op; + oh264_factory.base.factory_data = NULL; + oh264_factory.mgr = mgr; + oh264_factory.pf = pf; + oh264_factory.pool = pj_pool_create(pf, "oh264factory", 256, 256, NULL); + if (!oh264_factory.pool) + return PJ_ENOMEM; + + /* Registering format match for SDP negotiation */ + status = pjmedia_sdp_neg_register_fmt_match_cb( + &h264_name, + &pjmedia_vid_codec_h264_match_sdp); + pj_assert(status == PJ_SUCCESS); + + /* Register codec factory to codec manager. */ + status = pjmedia_vid_codec_mgr_register_factory(mgr, + &oh264_factory.base); + + PJ_LOG(4,(THIS_FILE, "OpenH264 codec initialized")); + + /* Done. */ + return PJ_SUCCESS; + +on_error: + pj_pool_release(oh264_factory.pool); + oh264_factory.pool = NULL; + return status; +} + +/* + * Unregister OpenH264 codecs factory from pjmedia endpoint. + */ +PJ_DEF(pj_status_t) pjmedia_codec_openh264_vid_deinit(void) +{ + pj_status_t status = PJ_SUCCESS; + + if (oh264_factory.pool == NULL) { + /* Already deinitialized */ + return PJ_SUCCESS; + } + + /* Unregister OpenH264 codecs factory. */ + status = pjmedia_vid_codec_mgr_unregister_factory(oh264_factory.mgr, + &oh264_factory.base); + + /* Destroy pool. */ + pj_pool_release(oh264_factory.pool); + oh264_factory.pool = NULL; + + return status; +} + +static pj_status_t oh264_test_alloc(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info ) +{ + PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); + + if (info->fmt_id == PJMEDIA_FORMAT_H264 && + info->pt != 0) + { + return PJ_SUCCESS; + } + + return PJMEDIA_CODEC_EUNSUP; +} + +static pj_status_t oh264_default_attr(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec_param *attr ) +{ + PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); + PJ_ASSERT_RETURN(info && attr, PJ_EINVAL); + + pj_bzero(attr, sizeof(pjmedia_vid_codec_param)); + + attr->dir = PJMEDIA_DIR_ENCODING_DECODING; + attr->packing = PJMEDIA_VID_PACKING_PACKETS; + + /* Encoded format */ + pjmedia_format_init_video(&attr->enc_fmt, PJMEDIA_FORMAT_H264, + DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_FPS, 1); + + /* Decoded format */ + pjmedia_format_init_video(&attr->dec_fmt, PJMEDIA_FORMAT_I420, + DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_FPS, 1); + + /* Decoding fmtp */ + attr->dec_fmtp.cnt = 2; + attr->dec_fmtp.param[0].name = pj_str((char*)"profile-level-id"); + attr->dec_fmtp.param[0].val = pj_str((char*)"42e01e"); + attr->dec_fmtp.param[1].name = pj_str((char*)" packetization-mode"); + attr->dec_fmtp.param[1].val = pj_str((char*)"1"); + + /* Bitrate */ + attr->enc_fmt.det.vid.avg_bps = DEFAULT_AVG_BITRATE; + attr->enc_fmt.det.vid.max_bps = DEFAULT_MAX_BITRATE; + + /* Encoding MTU */ + attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; + + return PJ_SUCCESS; +} + +static pj_status_t oh264_enum_info(pjmedia_vid_codec_factory *factory, + unsigned *count, + pjmedia_vid_codec_info info[]) +{ + PJ_ASSERT_RETURN(info && *count > 0, PJ_EINVAL); + PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); + + *count = 1; + info->fmt_id = PJMEDIA_FORMAT_H264; + info->pt = PJMEDIA_RTP_PT_H264; + info->encoding_name = pj_str((char*)"H264"); + info->encoding_desc = pj_str((char*)"OpenH264 codec"); + info->clock_rate = 90000; + info->dir = PJMEDIA_DIR_ENCODING_DECODING; + info->dec_fmt_id_cnt = 1; + info->dec_fmt_id[0] = PJMEDIA_FORMAT_I420; + info->packings = PJMEDIA_VID_PACKING_PACKETS | + PJMEDIA_VID_PACKING_WHOLE; + info->fps_cnt = 3; + info->fps[0].num = 15; + info->fps[0].denum = 1; + info->fps[1].num = 25; + info->fps[1].denum = 1; + info->fps[2].num = 30; + info->fps[2].denum = 1; + + return PJ_SUCCESS; + +} + +static pj_status_t oh264_alloc_codec(pjmedia_vid_codec_factory *factory, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec **p_codec) +{ + pj_pool_t *pool; + pjmedia_vid_codec *codec; + oh264_codec_data *oh264_data; + int rc; + + PJ_ASSERT_RETURN(factory == &oh264_factory.base && info && p_codec, + PJ_EINVAL); + + *p_codec = NULL; + + pool = pj_pool_create(oh264_factory.pf, "oh264%p", 512, 512, NULL); + if (!pool) + return PJ_ENOMEM; + + /* codec instance */ + codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec); + codec->factory = factory; + codec->op = &oh264_codec_op; + + /* codec data */ + oh264_data = PJ_POOL_ZALLOC_T(pool, oh264_codec_data); + oh264_data->pool = pool; + codec->codec_data = oh264_data; + + /* encoder allocation */ + rc = CreateSVCEncoder(&oh264_data->enc); + if (rc != 0) + goto on_error; + + oh264_data->esrc_pic = PJ_POOL_ZALLOC_T(pool, SSourcePicture); + + /* decoder allocation */ + rc = CreateDecoder(&oh264_data->dec); + if (rc != 0) + goto on_error; + + *p_codec = codec; + return PJ_SUCCESS; + +on_error: + oh264_dealloc_codec(factory, codec); + return PJMEDIA_CODEC_EFAILED; +} + +static pj_status_t oh264_dealloc_codec(pjmedia_vid_codec_factory *factory, + pjmedia_vid_codec *codec ) +{ + oh264_codec_data *oh264_data; + + PJ_ASSERT_RETURN(codec, PJ_EINVAL); + + oh264_data = (oh264_codec_data*) codec->codec_data; + if (oh264_data->enc) { + DestroySVCEncoder(oh264_data->enc); + oh264_data->enc = NULL; + } + if (oh264_data->dec) { + oh264_data->dec->Uninitialize(); + DestroyDecoder(oh264_data->dec); + oh264_data->dec = NULL; + } + pj_pool_release(oh264_data->pool); + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_init(pjmedia_vid_codec *codec, + pj_pool_t *pool ) +{ + PJ_ASSERT_RETURN(codec && pool, PJ_EINVAL); + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(pool); + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_open(pjmedia_vid_codec *codec, + pjmedia_vid_codec_param *codec_param ) +{ + oh264_codec_data *oh264_data; + pjmedia_vid_codec_param *param; + pjmedia_h264_packetizer_cfg pktz_cfg; + pjmedia_vid_codec_h264_fmtp h264_fmtp; + SEncParamExt eprm; + SSpatialLayerConfig *elayer = &eprm.sSpatialLayers[0]; + SLayerPEncCtx elayer_ctx; + SDecodingParam sDecParam = {0}; + int rc; + pj_status_t status; + + PJ_ASSERT_RETURN(codec && codec_param, PJ_EINVAL); + + PJ_LOG(5,(THIS_FILE, "Opening codec..")); + + oh264_data = (oh264_codec_data*) codec->codec_data; + oh264_data->prm = pjmedia_vid_codec_param_clone( oh264_data->pool, + codec_param); + param = oh264_data->prm; + + /* Parse remote fmtp */ + pj_bzero(&h264_fmtp, sizeof(h264_fmtp)); + status = pjmedia_vid_codec_h264_parse_fmtp(¶m->enc_fmtp, &h264_fmtp); + if (status != PJ_SUCCESS) + return status; + + /* Apply SDP fmtp to format in codec param */ + if (!param->ignore_fmtp) { + status = pjmedia_vid_codec_h264_apply_fmtp(param); + if (status != PJ_SUCCESS) + return status; + } + + pj_bzero(&pktz_cfg, sizeof(pktz_cfg)); + pktz_cfg.mtu = param->enc_mtu; + /* Packetization mode */ +#if 0 + if (h264_fmtp.packetization_mode == 0) + pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; + else if (h264_fmtp.packetization_mode == 1) + pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED; + else + return PJ_ENOTSUP; +#else + if (h264_fmtp.packetization_mode!= + PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL && + h264_fmtp.packetization_mode!= + PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED) + { + return PJ_ENOTSUP; + } + /* Better always send in single NAL mode for better compatibility */ + pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; +#endif + + status = pjmedia_h264_packetizer_create(oh264_data->pool, &pktz_cfg, + &oh264_data->pktz); + if (status != PJ_SUCCESS) + return status; + + oh264_data->whole = (param->packing == PJMEDIA_VID_PACKING_WHOLE); + + /* + * Encoder + */ + + /* Init encoder parameters */ + pj_bzero(&eprm, sizeof(eprm)); + eprm.iInputCsp = videoFormatI420; + eprm.sSpatialLayers[0].uiProfileIdc = 66; // PRO_BASELINE + eprm.iPicWidth = param->enc_fmt.det.vid.size.w; + eprm.iPicHeight = param->enc_fmt.det.vid.size.h; + eprm.fMaxFrameRate = (param->enc_fmt.det.vid.fps.num * 1.0 / + param->enc_fmt.det.vid.fps.denum); + eprm.uiFrameToBeCoded = -1; + eprm.iTemporalLayerNum = 1; + eprm.uiIntraPeriod = 0; /* I-Frame interval in frames */ + eprm.bEnableSpsPpsIdAddition = (oh264_data->whole? false : true); + eprm.bEnableFrameCroppingFlag = true; + eprm.iLoopFilterDisableIdc = 0; + eprm.iLoopFilterAlphaC0Offset = 0; + eprm.iLoopFilterBetaOffset = 0; + eprm.iMultipleThreadIdc = 1; + eprm.bEnableRc = 1; + eprm.iTargetBitrate = param->enc_fmt.det.vid.avg_bps; + eprm.bEnableFrameSkip = 1; + eprm.bEnableDenoise = 0; + eprm.bEnableSceneChangeDetect = 1; + eprm.bEnableBackgroundDetection = 1; + eprm.bEnableAdaptiveQuant = 1; + eprm.bEnableLongTermReference = 0; + eprm.iLtrMarkPeriod = 30; + eprm.bPrefixNalAddingCtrl = false; + eprm.iSpatialLayerNum = 1; + + pj_bzero(&elayer_ctx, sizeof (SLayerPEncCtx)); + elayer_ctx.iDLayerQp = 24; + elayer_ctx.sSliceCfg.uiSliceMode = (oh264_data->whole ? + SM_SINGLE_SLICE : SM_DYN_SLICE); + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceSizeConstraint = param->enc_mtu; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceNum = 1; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[0] = 960; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[1] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[2] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[3] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[4] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[5] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[6] = 0; + elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[7] = 0; + + elayer->iVideoWidth = eprm.iPicWidth; + elayer->iVideoHeight = eprm.iPicHeight; + elayer->fFrameRate = eprm.fMaxFrameRate; + elayer->uiProfileIdc = eprm.sSpatialLayers[0].uiProfileIdc; + elayer->iSpatialBitrate = eprm.iTargetBitrate; + elayer->iDLayerQp = elayer_ctx.iDLayerQp; + elayer->sSliceCfg.uiSliceMode = elayer_ctx.sSliceCfg.uiSliceMode; + + memcpy( &elayer->sSliceCfg, + &elayer_ctx.sSliceCfg, + sizeof (SSliceConfig)); + memcpy( &elayer->sSliceCfg.sSliceArgument.uiSliceMbNum[0], + &elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[0], + sizeof (elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum)); + + /* Init input picture */ + oh264_data->esrc_pic->iColorFormat = videoFormatI420; + oh264_data->esrc_pic->uiTimeStamp = 0; + oh264_data->esrc_pic->iPicWidth = eprm.iPicWidth; + oh264_data->esrc_pic->iPicHeight = eprm.iPicHeight; + oh264_data->esrc_pic->iStride[0] = oh264_data->esrc_pic->iPicWidth; + oh264_data->esrc_pic->iStride[1] = + oh264_data->esrc_pic->iStride[2] = + oh264_data->esrc_pic->iStride[0]>>1; + + oh264_data->enc_input_size = oh264_data->esrc_pic->iPicWidth * + oh264_data->esrc_pic->iPicHeight * 3 >> 1; + + /* Initialize encoder */ + rc = oh264_data->enc->InitializeExt (&eprm); + if (rc != cmResultSuccess) { + PJ_LOG(4,(THIS_FILE, "SVC encoder Initialize failed, rc=%d", rc)); + return PJMEDIA_CODEC_EFAILED; + } + + /* + * Decoder + */ + sDecParam.sVideoProperty.size = sizeof (sDecParam.sVideoProperty); + sDecParam.iOutputColorFormat = videoFormatI420; + sDecParam.uiTargetDqLayer = (pj_uint8_t) - 1; + sDecParam.uiEcActiveFlag = 1; + sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT; + + //TODO: + // Apply "sprop-parameter-sets" here + + rc = CreateDecoder(&oh264_data->dec); + if (rc) { + PJ_LOG(4,(THIS_FILE, "Unable to create OpenH264 decoder")); + return PJMEDIA_CODEC_EFAILED; + } + + rc = oh264_data->dec->Initialize (&sDecParam); + if (rc) { + PJ_LOG(4,(THIS_FILE, "Decoder initialization failed, rc=%d", rc)); + return PJMEDIA_CODEC_EFAILED; + } + + int32_t color_fmt = videoFormatI420; + rc = oh264_data->dec->SetOption (DECODER_OPTION_DATAFORMAT, &color_fmt); + if (rc) { + PJ_LOG(4,(THIS_FILE, + "Warning: SetOption(DECODER_OPTION_DATAFORMAT) failed, rc=%d", + rc)); + } + + oh264_data->dec_buf_size = (MAX_RX_WIDTH * MAX_RX_HEIGHT * 3 >> 1) + + (MAX_RX_WIDTH); + oh264_data->dec_buf = (pj_uint8_t*)pj_pool_alloc(oh264_data->pool, + oh264_data->dec_buf_size); + + /* Need to update param back after values are negotiated */ + pj_memcpy(codec_param, param, sizeof(*codec_param)); + + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_close(pjmedia_vid_codec *codec) +{ + PJ_ASSERT_RETURN(codec, PJ_EINVAL); + PJ_UNUSED_ARG(codec); + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_modify(pjmedia_vid_codec *codec, + const pjmedia_vid_codec_param *param) +{ + PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(param); + return PJ_EINVALIDOP; +} + +static pj_status_t oh264_codec_get_param(pjmedia_vid_codec *codec, + pjmedia_vid_codec_param *param) +{ + struct oh264_codec_data *oh264_data; + + PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); + + oh264_data = (oh264_codec_data*) codec->codec_data; + pj_memcpy(param, oh264_data->prm, sizeof(*param)); + + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_encode_begin(pjmedia_vid_codec *codec, + const pjmedia_vid_encode_opt *opt, + const pjmedia_frame *input, + unsigned out_size, + pjmedia_frame *output, + pj_bool_t *has_more) +{ + struct oh264_codec_data *oh264_data; + int rc; + + PJ_ASSERT_RETURN(codec && input && out_size && output && has_more, + PJ_EINVAL); + + oh264_data = (oh264_codec_data*) codec->codec_data; + + PJ_ASSERT_RETURN(input->size == oh264_data->enc_input_size, + PJMEDIA_CODEC_EFRMINLEN); + + if (opt && opt->force_keyframe) { + oh264_data->enc->ForceIntraFrame(true); + } + + oh264_data->esrc_pic->pData[0] = (pj_uint8_t*)input->buf; + oh264_data->esrc_pic->pData[1] = oh264_data->esrc_pic->pData[0] + + (oh264_data->esrc_pic->iPicWidth * + oh264_data->esrc_pic->iPicHeight); + oh264_data->esrc_pic->pData[2] = oh264_data->esrc_pic->pData[1] + + (oh264_data->esrc_pic->iPicWidth * + oh264_data->esrc_pic->iPicHeight >>2); + + pj_memset (&oh264_data->bsi, 0, sizeof (SFrameBSInfo)); + rc = oh264_data->enc->EncodeFrame( oh264_data->esrc_pic, &oh264_data->bsi); + if (rc != cmResultSuccess) { + PJ_LOG(5,(THIS_FILE, "EncodeFrame() error, ret: %d", rc)); + return PJMEDIA_CODEC_EFAILED; + } + + if (oh264_data->bsi.eOutputFrameType == videoFrameTypeSkip) { + output->size = 0; + output->type = PJMEDIA_FRAME_TYPE_NONE; + output->timestamp = input->timestamp; + return PJ_SUCCESS; + } + + oh264_data->ets = input->timestamp; + oh264_data->ilayer = 0; + oh264_data->enc_frame_size = oh264_data->enc_processed = 0; + + if (oh264_data->whole) { + SLayerBSInfo* pLayerBsInfo; + pj_uint8_t *payload; + unsigned i, payload_size = 0; + + *has_more = PJ_FALSE; + + /* Find which layer with biggest payload */ + oh264_data->ilayer = 0; + payload_size = oh264_data->bsi.sLayerInfo[0].iNalLengthInByte[0]; + for (i=0; i < (unsigned)oh264_data->bsi.iLayerNum; ++i) { + unsigned j; + pLayerBsInfo = &oh264_data->bsi.sLayerInfo[i]; + for (j=0; j < (unsigned)pLayerBsInfo->iNalCount; ++j) { + if (pLayerBsInfo->iNalLengthInByte[j] > (int)payload_size) { + payload_size = pLayerBsInfo->iNalLengthInByte[j]; + oh264_data->ilayer = i; + } + } + } + + pLayerBsInfo = &oh264_data->bsi.sLayerInfo[oh264_data->ilayer]; + if (pLayerBsInfo == NULL) { + output->size = 0; + output->type = PJMEDIA_FRAME_TYPE_NONE; + return PJ_SUCCESS; + } + + payload = pLayerBsInfo->pBsBuf; + payload_size = 0; + for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { + payload_size += pLayerBsInfo->iNalLengthInByte[inal]; + } + + if (payload_size > out_size) + return PJMEDIA_CODEC_EFRMTOOSHORT; + + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + output->size = payload_size; + output->timestamp = input->timestamp; + pj_memcpy(output->buf, payload, payload_size); + + return PJ_SUCCESS; + } + + return oh264_codec_encode_more(codec, out_size, output, has_more); +} + + +static pj_status_t oh264_codec_encode_more(pjmedia_vid_codec *codec, + unsigned out_size, + pjmedia_frame *output, + pj_bool_t *has_more) +{ + struct oh264_codec_data *oh264_data; + const pj_uint8_t *payload; + pj_size_t payload_len; + pj_status_t status; + + PJ_ASSERT_RETURN(codec && out_size && output && has_more, + PJ_EINVAL); + + oh264_data = (oh264_codec_data*) codec->codec_data; + + if (oh264_data->enc_processed < oh264_data->enc_frame_size) { + /* We have outstanding frame in packetizer */ + status = pjmedia_h264_packetize(oh264_data->pktz, + oh264_data->enc_frame_whole, + oh264_data->enc_frame_size, + &oh264_data->enc_processed, + &payload, &payload_len); + if (status != PJ_SUCCESS) { + /* Reset */ + oh264_data->enc_frame_size = oh264_data->enc_processed = 0; + *has_more = (oh264_data->enc_processed < + oh264_data->enc_frame_size) || + (oh264_data->ilayer < oh264_data->bsi.iLayerNum); + + PJ_PERROR(3,(THIS_FILE, status, "pjmedia_h264_packetize() error")); + return status; + } + + PJ_ASSERT_RETURN(payload_len <= out_size, PJMEDIA_CODEC_EFRMTOOSHORT); + + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + pj_memcpy(output->buf, payload, payload_len); + output->size = payload_len; + + if (oh264_data->bsi.eOutputFrameType == videoFrameTypeIDR) { + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } + + *has_more = (oh264_data->enc_processed < oh264_data->enc_frame_size) || + (oh264_data->ilayer < oh264_data->bsi.iLayerNum); + return PJ_SUCCESS; + } + + if (oh264_data->ilayer >= oh264_data->bsi.iLayerNum) { + /* No more unretrieved frame */ + goto no_frame; + } + + SLayerBSInfo* pLayerBsInfo; + pLayerBsInfo = &oh264_data->bsi.sLayerInfo[oh264_data->ilayer++]; + if (pLayerBsInfo == NULL) { + goto no_frame; + } + + oh264_data->enc_frame_size = 0; + for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { + oh264_data->enc_frame_size += pLayerBsInfo->iNalLengthInByte[inal]; + } + + oh264_data->enc_frame_whole = pLayerBsInfo->pBsBuf; + oh264_data->enc_processed = 0; + + + status = pjmedia_h264_packetize(oh264_data->pktz, + oh264_data->enc_frame_whole, + oh264_data->enc_frame_size, + &oh264_data->enc_processed, + &payload, &payload_len); + if (status != PJ_SUCCESS) { + /* Reset */ + oh264_data->enc_frame_size = oh264_data->enc_processed = 0; + *has_more = (oh264_data->ilayer < oh264_data->bsi.iLayerNum); + + PJ_PERROR(3,(THIS_FILE, status, "pjmedia_h264_packetize() error [2]")); + return status; + } + + PJ_ASSERT_RETURN(payload_len <= out_size, PJMEDIA_CODEC_EFRMTOOSHORT); + + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + pj_memcpy(output->buf, payload, payload_len); + output->size = payload_len; + + if (oh264_data->bsi.eOutputFrameType == videoFrameTypeIDR) { + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } + + *has_more = (oh264_data->enc_processed < oh264_data->enc_frame_size) || + (oh264_data->ilayer < oh264_data->bsi.iLayerNum); + + return PJ_SUCCESS; + +no_frame: + *has_more = PJ_FALSE; + output->size = 0; + output->type = PJMEDIA_FRAME_TYPE_NONE; + return PJ_SUCCESS; +} + +static int write_yuv(pj_uint8_t *buf, + unsigned dst_len, + unsigned char* pData[3], + int iStride[2], + int iWidth, + int iHeight) +{ + unsigned req_size; + pj_uint8_t *dst = buf; + pj_uint8_t *max = dst + dst_len; + int i; + unsigned char* pPtr = NULL; + + req_size = (iWidth * iHeight) + (iWidth / 2 * iHeight / 2) + + (iWidth / 2 * iHeight / 2); + if (dst_len < req_size) + return -1; + + pPtr = pData[0]; + for (i = 0; i < iHeight && (dst + iWidth < max); i++) { + pj_memcpy(dst, pPtr, iWidth); + pPtr += iStride[0]; + dst += iWidth; + } + + if (i < iHeight) + return -1; + + iHeight = iHeight / 2; + iWidth = iWidth / 2; + pPtr = pData[1]; + for (i = 0; i < iHeight && (dst + iWidth <= max); i++) { + pj_memcpy(dst, pPtr, iWidth); + pPtr += iStride[1]; + dst += iWidth; + } + + if (i < iHeight) + return -1; + + pPtr = pData[2]; + for (i = 0; i < iHeight && (dst + iWidth <= max); i++) { + pj_memcpy(dst, pPtr, iWidth); + pPtr += iStride[1]; + dst += iWidth; + } + + if (i < iHeight) + return -1; + + return dst - buf; +} + +static pj_status_t oh264_got_decoded_frame(pjmedia_vid_codec *codec, + struct oh264_codec_data *oh264_data, + void *pData[3], + SBufferInfo *sDstBufInfo, + pj_timestamp *timestamp, + unsigned out_size, + pjmedia_frame *output) +{ + pj_uint8_t* pDst[3] = {NULL}; + + pDst[0] = (pj_uint8_t*)pData[0]; + pDst[1] = (pj_uint8_t*)pData[1]; + pDst[2] = (pj_uint8_t*)pData[2]; + + /* Do not reset size as it may already contain frame + output->size = 0; + */ + + if (!pDst[0] || !pDst[1] || !pDst[2]) { + return PJ_SUCCESS; + } + + int iStride[2]; + int iWidth = sDstBufInfo->UsrData.sSystemBuffer.iWidth; + int iHeight = sDstBufInfo->UsrData.sSystemBuffer.iHeight; + + iStride[0] = sDstBufInfo->UsrData.sSystemBuffer.iStride[0]; + iStride[1] = sDstBufInfo->UsrData.sSystemBuffer.iStride[1]; + + int len = write_yuv((pj_uint8_t *)output->buf, out_size, + pDst, iStride, iWidth, iHeight); + if (len > 0) { + output->timestamp = *timestamp; + output->size = len; + output->type = PJMEDIA_FRAME_TYPE_VIDEO; + } else { + /* buffer is damaged, reset size */ + output->size = 0; + return PJMEDIA_CODEC_EFRMTOOSHORT; + } + + /* Detect format change */ + if (iWidth != (int)oh264_data->prm->dec_fmt.det.vid.size.w || + iHeight != (int)oh264_data->prm->dec_fmt.det.vid.size.h) + { + pjmedia_event event; + + PJ_LOG(4,(THIS_FILE, "Frame size changed: %dx%d --> %dx%d", + oh264_data->prm->dec_fmt.det.vid.size.w, + oh264_data->prm->dec_fmt.det.vid.size.h, + iWidth, iHeight)); + + oh264_data->prm->dec_fmt.det.vid.size.w = iWidth; + oh264_data->prm->dec_fmt.det.vid.size.h = iHeight; + + /* Broadcast format changed event */ + pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, + timestamp, codec); + event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; + pjmedia_format_copy(&event.data.fmt_changed.new_fmt, + &oh264_data->prm->dec_fmt); + pjmedia_event_publish(NULL, codec, &event, + PJMEDIA_EVENT_PUBLISH_DEFAULT); + } + + return PJ_SUCCESS; +} + +static pj_status_t oh264_codec_decode(pjmedia_vid_codec *codec, + pj_size_t count, + pjmedia_frame packets[], + unsigned out_size, + pjmedia_frame *output) +{ + struct oh264_codec_data *oh264_data; + void* pData[3] = {NULL}; + const pj_uint8_t nal_start[] = { 0, 0, 1 }; + SBufferInfo sDstBufInfo; + pj_bool_t has_frame = PJ_FALSE; + unsigned buf_pos, whole_len = 0; + unsigned i, frm_cnt; + pj_status_t status = PJ_SUCCESS; + + PJ_ASSERT_RETURN(codec && count && packets && out_size && output, + PJ_EINVAL); + PJ_ASSERT_RETURN(output->buf, PJ_EINVAL); + + oh264_data = (oh264_codec_data*) codec->codec_data; + + /* + * Step 1: unpacketize the packets/frames + */ + whole_len = 0; + if (oh264_data->whole) { + for (i=0; i oh264_data->dec_buf_size) { + PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]")); + return PJMEDIA_CODEC_EFRMTOOSHORT; + } + + pj_memcpy( oh264_data->dec_buf + whole_len, + (pj_uint8_t*)packets[i].buf, + packets[i].size); + whole_len += packets[i].size; + } + + } else { + for (i=0; i + oh264_data->dec_buf_size) + { + PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]")); + return PJMEDIA_CODEC_EFRMTOOSHORT; + } + + status = pjmedia_h264_unpacketize( oh264_data->pktz, + (pj_uint8_t*)packets[i].buf, + packets[i].size, + oh264_data->dec_buf, + oh264_data->dec_buf_size, + &whole_len); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(THIS_FILE, status, "Unpacketize error")); + continue; + } + } + } + + if (whole_len + sizeof(nal_start) > oh264_data->dec_buf_size) { + PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [2]")); + return PJMEDIA_CODEC_EFRMTOOSHORT; + } + + /* Dummy NAL sentinel */ + pj_memcpy( oh264_data->dec_buf + whole_len, nal_start, sizeof(nal_start)); + + /* + * Step 2: parse the individual NAL and give to decoder + */ + buf_pos = 0; + for ( frm_cnt=0; ; ++frm_cnt) { + unsigned frm_size; + unsigned char *start; + + for (i = 0; buf_pos + i < whole_len; i++) { + if (oh264_data->dec_buf[buf_pos + i] == 0 && + oh264_data->dec_buf[buf_pos + i + 1] == 0 && + oh264_data->dec_buf[buf_pos + i + 2] == 1 && + i > 1) + { + break; + } + } + frm_size = i; + + pj_bzero( pData, sizeof(pData)); + pj_bzero( &sDstBufInfo, sizeof (SBufferInfo)); + + start = oh264_data->dec_buf + buf_pos; + + /* Decode */ + oh264_data->dec->DecodeFrame2( start, frm_size, pData, &sDstBufInfo); + + if (sDstBufInfo.iBufferStatus == 1) { + /* May overwrite existing frame but that's ok. */ + status = oh264_got_decoded_frame(codec, oh264_data, pData, + &sDstBufInfo, + &packets[0].timestamp, out_size, + output); + has_frame = (status==PJ_SUCCESS && output->size != 0); + } + + if (buf_pos + frm_size >= whole_len) + break; + + buf_pos += frm_size; + } + + /* Signal that we have no more frames */ + int32_t iEndOfStreamFlag; + iEndOfStreamFlag = true; + oh264_data->dec->SetOption( DECODER_OPTION_END_OF_STREAM, + (void*)&iEndOfStreamFlag); + + /* Retrieve the decoded frame */ + pj_bzero(pData, sizeof(pData)); + pj_bzero(&sDstBufInfo, sizeof (SBufferInfo)); + oh264_data->dec->DecodeFrame2 (NULL, 0, pData, &sDstBufInfo); + + if (sDstBufInfo.iBufferStatus == 1) { + /* Overwrite existing output frame and that's ok, because we assume + * newer frame have better quality because it has more NALs + */ + status = oh264_got_decoded_frame(codec, oh264_data, pData, + &sDstBufInfo, &packets[0].timestamp, + out_size, output); + has_frame = (status==PJ_SUCCESS && output->size != 0); + } + + if (!has_frame) { + pjmedia_event event; + + /* Broadcast missing keyframe event */ + pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, + &packets[0].timestamp, codec); + pjmedia_event_publish(NULL, codec, &event, + PJMEDIA_EVENT_PUBLISH_DEFAULT); + + PJ_LOG(5,(THIS_FILE, "Decode couldn't produce picture, " + "input nframes=%d, concatenated size=%d bytes", + count, whole_len)); + + output->type = PJMEDIA_FRAME_TYPE_NONE; + output->size = 0; + output->timestamp = packets[0].timestamp; + } + + return status; +} + +#endif /* PJMEDIA_HAS_OPENH264_CODEC */ diff --git a/pjmedia/src/pjmedia/vid_codec_util.c b/pjmedia/src/pjmedia/vid_codec_util.c index 75cbc8b3..c5b5abdc 100644 --- a/pjmedia/src/pjmedia/vid_codec_util.c +++ b/pjmedia/src/pjmedia/vid_codec_util.c @@ -631,7 +631,7 @@ static pj_status_t find_highest_res(pjmedia_vid_codec_h264_fmtp *fmtp, } /* Calculate maximum size (in macroblocks) */ - max_fs = fmtp->max_mbps * fps->denum / fps->num; + max_fs = fmtp->max_mbps * the_fps.denum / the_fps.num; max_fs = PJ_MIN(max_fs, fmtp->max_fs); /* Check if the specified ratio is using big numbers diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c index f613b84d..b0b2abc2 100644 --- a/pjmedia/src/test/vid_codec_test.c +++ b/pjmedia/src/test/vid_codec_test.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" -#include +#include #include #include #include @@ -297,6 +297,9 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, codec_param.packing = packing; + /* Don't apply SDP fmtp */ + codec_param.ignore_fmtp = PJ_TRUE; + /* Open codec */ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &codec); @@ -453,6 +456,13 @@ int vid_codec_test(void) if (status != PJ_SUCCESS) return -10; +#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_OPENH264_CODEC + status = pjmedia_codec_openh264_vid_init(NULL, mem); + if (status != PJ_SUCCESS) { + return -22; + } +#endif + #if PJMEDIA_HAS_FFMPEG_VID_CODEC status = pjmedia_codec_ffmpeg_vid_init(NULL, mem); if (status != PJ_SUCCESS) @@ -463,6 +473,7 @@ int vid_codec_test(void) if (rc != 0) goto on_return; +#if PJMEDIA_HAS_FFMPEG_VID_CODEC rc = encode_decode_test(pool, "h263-1998", PJMEDIA_VID_PACKING_WHOLE); if (rc != 0) goto on_return; @@ -470,10 +481,25 @@ int vid_codec_test(void) rc = encode_decode_test(pool, "h263-1998", PJMEDIA_VID_PACKING_PACKETS); if (rc != 0) goto on_return; +#endif + +#if PJMEDIA_HAS_FFMPEG_VID_CODEC || PJMEDIA_HAS_OPENH264_CODEC + rc = encode_decode_test(pool, "h264", PJMEDIA_VID_PACKING_WHOLE); + if (rc != 0) + goto on_return; + + rc = encode_decode_test(pool, "h264", PJMEDIA_VID_PACKING_PACKETS); + if (rc != 0) + goto on_return; +#endif + on_return: #if PJMEDIA_HAS_FFMPEG_VID_CODEC pjmedia_codec_ffmpeg_vid_deinit(); +#endif +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); #endif pjmedia_vid_dev_subsys_shutdown(); pj_pool_release(pool); diff --git a/pjsip-apps/build/Samples.mak b/pjsip-apps/build/Samples.mak index 6ec7f191..d8769ff1 100644 --- a/pjsip-apps/build/Samples.mak +++ b/pjsip-apps/build/Samples.mak @@ -45,6 +45,7 @@ SAMPLES := auddemo \ streamutil \ strerror \ tonegen \ + vid_codec_test \ vid_streamutil PJSUA2_SAMPLES := pjsua2_demo diff --git a/pjsip-apps/src/samples/aviplay.c b/pjsip-apps/src/samples/aviplay.c index 35b05044..40dfaf90 100644 --- a/pjsip-apps/src/samples/aviplay.c +++ b/pjsip-apps/src/samples/aviplay.c @@ -500,6 +500,12 @@ static int main_func(int argc, char *argv[]) goto on_return; } +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, &cp.factory); + if (status != PJ_SUCCESS) + goto on_return; +#endif + #if PJMEDIA_HAS_FFMPEG_VID_CODEC status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); if (status != PJ_SUCCESS) @@ -520,6 +526,9 @@ static int main_func(int argc, char *argv[]) on_return: #if PJMEDIA_HAS_FFMPEG_VID_CODEC pjmedia_codec_ffmpeg_vid_deinit(); +#endif +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); #endif pjmedia_aud_subsys_shutdown(); pjmedia_vid_dev_subsys_shutdown(); diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index 68c50b9b..b7f10250 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -385,6 +385,11 @@ int main(int argc, char *argv[]) status = pjmedia_vid_dev_subsys_init(&cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); +# if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, &cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); +# endif + # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 /* Init ffmpeg video codecs */ status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); @@ -576,6 +581,9 @@ int main(int argc, char *argv[]) # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 pjmedia_codec_ffmpeg_vid_deinit(); # endif +# if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +# endif #endif diff --git a/pjsip-apps/src/samples/vid_codec_test.c b/pjsip-apps/src/samples/vid_codec_test.c new file mode 100644 index 00000000..a249c71f --- /dev/null +++ b/pjsip-apps/src/samples/vid_codec_test.c @@ -0,0 +1,521 @@ +/* $Id$ */ +/* + * Copyright (C) 2014 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 + */ + + +/** + * \page page_pjmedia_samples_vid_codec_test_c Samples: Video Codec Test + * + * Video codec encode and decode test. + * + * This file is pjsip-apps/src/samples/vid_vodec_test.c + * + * \includelineno vid_vodec_test.c + */ + +#include +#include +#include +#include + + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + + +#include /* atoi() */ +#include + +#include "util.h" + + +static const char *desc = + " vid_vodec_test \n" +; + +#define THIS_FILE "vid_vodec_test.c" + + +/* If set, local renderer will be created to play original file */ +#define HAS_LOCAL_RENDERER_FOR_PLAY_FILE 1 + + +/* Default width and height for the renderer, better be set to maximum + * acceptable size. + */ +#define DEF_RENDERER_WIDTH 640 +#define DEF_RENDERER_HEIGHT 480 + + +/* Prototype */ +static void print_stream_stat(pjmedia_vid_stream *stream, + const pjmedia_vid_codec_param *codec_param); + +/* Prototype for LIBSRTP utility in file datatypes.c */ +int hex_string_to_octet_string(char *raw, char *hex, int len); + +/* + * Register all codecs. + */ +static pj_status_t init_codecs(pj_pool_factory *pf) +{ + pj_status_t status; + + /* To suppress warning about unused var when all codecs are disabled */ + PJ_UNUSED_ARG(status); + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + +#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 + status = pjmedia_codec_ffmpeg_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + + return PJ_SUCCESS; +} + +/* + * Register all codecs. + */ +static void deinit_codecs() +{ +#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 + pjmedia_codec_ffmpeg_vid_deinit(); +#endif + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +#endif + +} + +/* + * usage() + */ +static void usage() +{ + puts(desc); +} + +static void show_diff(const pj_uint8_t *buf1, const pj_uint8_t *buf2, + unsigned size) +{ + enum { + STEP = 50 + }; + unsigned i=0; + + for (; i 0) + { + break; + } + } + nal_len[0] = i; + for (i = 0; i < size[1]; i++) { + if (memcmp(buf[1] + pos[1] + i, start_nal, + sizeof(start_nal)) == 0 && i > 0) + { + break; + } + } + nal_len[1] = i; + + if (nal_len[0] != nal_len[1]) { + printf("Different size in frame %d (%d vs %d)\n", + frame_cnt, nal_len[0], nal_len[1]); + } + + if (memcmp(buf[0]+pos[0], buf[1]+pos[1], nal_len[0]) != 0) { + printf("Mismatch in frame %d\n", frame_cnt); + show_diff(buf[0]+pos[0], buf[1]+pos[1], nal_len[0]); + puts(""); + ++mismatch_cnt; + } + + pos[0] += nal_len[0]; + pos[1] += nal_len[1]; + + if (pos[0] >= size[0]) + break; + } + + free(buf[0]); + free(buf[1]); + + if (!mismatch_cnt) + puts("Files the same!"); + else + printf("%d mismatches\n", mismatch_cnt); +} + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + pj_caching_pool cp; + pjmedia_endpt *med_endpt; + pj_pool_t *pool; + pj_status_t status; + + /* Codec */ + char *codec_id = (char*)"H264"; + const pjmedia_vid_codec_info *codec_info; + pjmedia_vid_codec_param codec_param; + pjmedia_vid_codec *codec = NULL; + + //const char *save_filename = + // "/home/bennylp/Desktop/opt/src/openh264-svn/testbin/test.264"; + const char *save_filename = NULL; + + /* File */ + enum + { + WIDTH = 320, + HEIGHT = 192, + FPS = 12, + YUV_SIZE = WIDTH * HEIGHT * 3 >> 1, + YUV_BUF_SIZE = YUV_SIZE + WIDTH, + MAX_FRAMES = 32, + MTU = 1500 + }; + FILE *fyuv = NULL; + FILE *f264 = NULL; + typedef pj_uint8_t enc_buf_type[MTU]; + pj_uint8_t yuv_frame[YUV_BUF_SIZE]; + enc_buf_type enc_buf[MAX_FRAMES]; + unsigned read_cnt = 0, + pkt_cnt = 0, + dec_cnt = 0, + enc_cnt; + + if (0) { + diff_file(); + return 1; + } + + /* init PJLIB : */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + /* Initialize media endpoint. */ + status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Create memory pool for application purpose */ + pool = pj_pool_create( &cp.factory, /* pool factory */ + "app", /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + /* Init video format manager */ + pjmedia_video_format_mgr_create(pool, 64, 0, NULL); + + /* Init video converter manager */ + pjmedia_converter_mgr_create(pool, NULL); + + /* Init event manager */ + pjmedia_event_mgr_create(pool, 0, NULL); + + /* Init video codec manager */ + pjmedia_vid_codec_mgr_create(pool, NULL); + + /* Register all supported codecs */ + status = init_codecs(&cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Open YUV file */ + fyuv = fopen("pjsip-apps/bin/CiscoVT2people_320x192_12fps.yuv", "rb"); + if (!fyuv) { + puts("Unable to open ../CiscoVT2people_320x192_12fps.yuv"); + status = -1; + goto on_exit; + } + + /* Write 264 file if wanted */ + if (save_filename) { + f264 = fopen(save_filename, "wb"); + } + + /* Find which codec to use. */ + if (codec_id) { + unsigned count = 1; + pj_str_t str_codec_id = pj_str(codec_id); + + status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, + &str_codec_id, &count, + &codec_info, NULL); + if (status != PJ_SUCCESS) { + printf("Error: unable to find codec %s\n", codec_id); + return 1; + } + } else { + static pjmedia_vid_codec_info info[1]; + unsigned count = PJ_ARRAY_SIZE(info); + + /* Default to first codec */ + pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, NULL); + codec_info = &info[0]; + } + + /* Get codec default param for info */ + status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, + &codec_param); + pj_assert(status == PJ_SUCCESS); + + /* Alloc encoder */ + status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &codec); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error allocating codec")); + goto on_exit; + } + + codec_param.dir = PJMEDIA_DIR_ENCODING_DECODING; + codec_param.packing = PJMEDIA_VID_PACKING_PACKETS; + codec_param.enc_mtu = MTU; + codec_param.enc_fmt.det.vid.size.w = WIDTH; + codec_param.enc_fmt.det.vid.size.h = HEIGHT; + codec_param.enc_fmt.det.vid.fps.num = FPS; + codec_param.enc_fmt.det.vid.avg_bps = WIDTH * HEIGHT * FPS; + + status = pjmedia_vid_codec_init(codec, pool); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error initializing codec")); + goto on_exit; + } + + status = pjmedia_vid_codec_open(codec, &codec_param); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error opening codec")); + goto on_exit; + } + + while (fread(yuv_frame, 1, YUV_SIZE, fyuv) == YUV_SIZE) { + pjmedia_frame frm_yuv, frm_enc[MAX_FRAMES]; + pj_bool_t has_more = PJ_FALSE; + const pj_uint8_t start_nal[] = { 0, 0, 1 }; + unsigned i; + + ++ read_cnt; + + pj_bzero(&frm_enc, sizeof(frm_enc)); + pj_bzero(&frm_yuv, sizeof(frm_yuv)); + + frm_yuv.buf = yuv_frame; + frm_yuv.size = YUV_SIZE; + + enc_cnt = 0; + frm_enc[enc_cnt].buf = enc_buf[enc_cnt]; + frm_enc[enc_cnt].size = MTU; + + status = pjmedia_vid_codec_encode_begin(codec, NULL, &frm_yuv, + MTU, &frm_enc[enc_cnt], + &has_more); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec encode error")); + goto on_exit; + } + if (frm_enc[enc_cnt].size) { + if (f264) { + fwrite(start_nal, 1, sizeof(start_nal), f264); + fwrite(frm_enc[enc_cnt].buf, 1, frm_enc[enc_cnt].size, f264); + } + ++pkt_cnt; + ++enc_cnt; + } + + while (has_more) { + + if (enc_cnt >= MAX_FRAMES) { + status = -1; + puts("Error: too many encoded frames"); + goto on_exit; + } + + has_more = PJ_FALSE; + frm_enc[enc_cnt].buf = enc_buf[enc_cnt]; + frm_enc[enc_cnt].size = MTU; + + status = pjmedia_vid_codec_encode_more(codec, MTU, + &frm_enc[enc_cnt], + &has_more); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec encode error")); + goto on_exit; + } + + if (frm_enc[enc_cnt].size) { + if (f264) { + fwrite(start_nal, 1, sizeof(start_nal), f264); + fwrite(frm_enc[enc_cnt].buf, 1, frm_enc[enc_cnt].size, + f264); + } + ++pkt_cnt; + ++enc_cnt; + } + } + + if (enc_cnt) { + frm_yuv.buf = yuv_frame; + frm_yuv.size = YUV_BUF_SIZE; + status = pjmedia_vid_codec_decode(codec, enc_cnt, + frm_enc, + YUV_BUF_SIZE, + &frm_yuv); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec decode error")); + goto on_exit; + } + + if (frm_yuv.size != 0) { + ++dec_cnt; + } + } + } + + printf("Done.\n" + " Read YUV frames: %d\n" + " Encoded packets: %d\n" + " Decoded YUV frames: %d\n", + read_cnt, pkt_cnt, dec_cnt); + + /* Start deinitialization: */ +on_exit: + if (codec) { + pjmedia_vid_codec_close(codec); + pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec); + } + + if (f264) + fclose(f264); + + if (fyuv) + fclose(fyuv); + + /* Deinit codecs */ + deinit_codecs(); + + /* Destroy event manager */ + pjmedia_event_mgr_destroy(NULL); + + /* Release application pool */ + pj_pool_release( pool ); + + /* Destroy media endpoint. */ + pjmedia_endpt_destroy( med_endpt ); + + /* Destroy pool factory */ + pj_caching_pool_destroy( &cp ); + + /* Shutdown PJLIB */ + pj_shutdown(); + + return (status == PJ_SUCCESS) ? 0 : 1; +} + + +#else + +int main(int argc, char *argv[]) +{ + PJ_UNUSED_ARG(argc); + PJ_UNUSED_ARG(argv); + puts("Error: this sample requires video capability " + "(PJMEDIA_HAS_VIDEO == 1)"); + return -1; +} + +#endif /* PJMEDIA_HAS_VIDEO */ diff --git a/pjsip-apps/src/samples/vid_streamutil.c b/pjsip-apps/src/samples/vid_streamutil.c index f9f0a0d7..44c4f899 100644 --- a/pjsip-apps/src/samples/vid_streamutil.c +++ b/pjsip-apps/src/samples/vid_streamutil.c @@ -121,6 +121,11 @@ static pj_status_t init_codecs(pj_pool_factory *pf) /* To suppress warning about unused var when all codecs are disabled */ PJ_UNUSED_ARG(status); +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 status = pjmedia_codec_ffmpeg_vid_init(NULL, pf); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); @@ -137,6 +142,11 @@ static void deinit_codecs() #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 pjmedia_codec_ffmpeg_vid_deinit(); #endif + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +#endif + } static pj_status_t create_file_player( pj_pool_t *pool, diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 026942d1..c12cfd16 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -73,6 +73,15 @@ pj_status_t pjsua_vid_subsys_init(void) goto on_error; } +#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_OPENH264_CODEC + status = pjmedia_codec_openh264_vid_init(NULL, &pjsua_var.cp.factory); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "Error initializing OpenH264 library")); + goto on_error; + } +#endif + #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_VID_CODEC status = pjmedia_codec_ffmpeg_vid_init(NULL, &pjsua_var.cp.factory); if (status != PJ_SUCCESS) { @@ -133,6 +142,10 @@ pj_status_t pjsua_vid_subsys_destroy(void) pjmedia_codec_ffmpeg_vid_deinit(); #endif +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +#endif + if (pjmedia_vid_codec_mgr_instance()) pjmedia_vid_codec_mgr_destroy(NULL); -- cgit v1.2.3